Barcode image generation not working when .exe generated

54 Views Asked by At

I have this PyQt5 custom widget:

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QGraphicsScene
from PyQt5.QtGui import QPixmap,QImage
from .MostrarBarcode import MostrarBarcode
import barcode
from barcode.writer import ImageWriter
from PIL import Image
import io


class ResponsiveDataWidget(QWidget):

    def mousePressEvent(self, event):
        self.mostrarBarcode()

    def __init__(self,datos,botonEditar,botonEliminar,botonReponer,anchoTabla,indiceBarcode):
        super().__init__()
        self.datos = datos
        self.cantidadBotones = 0
        self.hayBotonEditar = False
        self.hayBotonEliminar = False
        self.hayBotonReponer = False
        self.indiceBarcode = indiceBarcode
        if botonEditar:
            self.hayBotonEditar = True
            self.cantidadBotones = self.cantidadBotones+1
        if botonEliminar:
            self.hayBotonEliminar = True
            self.cantidadBotones = self.cantidadBotones+1
        if botonReponer:
            self.hayBotonReponer = True
            self.cantidadBotones = self.cantidadBotones+1
        espacioBotones = self.cantidadBotones*30
        self.anchoWidget = anchoTabla-25 # restamos 25 para dar espacio al scrollbar
        self.anchoRegistro = int(((self.anchoWidget-espacioBotones)/len(datos))-3) #restamos 3 para dar espacio a los separadores
        self.labels = []
        self.setupUi(self)

    def generar_codigo_barras(self,numero):
        # Genera el código de barras
        codigo = barcode.get('code39', numero, writer=ImageWriter())
        # Guarda el código de barras en un archivo
        ruta_temporal = codigo.save('codigo_barras_temp')
        imagen = Image.open(ruta_temporal)
        redim_imagen = imagen.resize((190,100),Image.Resampling.LANCZOS)

        ruta_imagen = 'codigo_barras.png'
        redim_imagen.save(ruta_imagen)

        return ruta_imagen

    def mostrarBarcode(self):
        if self.indiceBarcode!=None:
            self.widgetBarcode = MostrarBarcode()
            scene = QGraphicsScene(self.widgetBarcode.barcodePlaceholder)
            self.widgetBarcode.barcodePlaceholder.setScene(scene)
            pixmap = QPixmap(self.generar_codigo_barras(self.labels[self.indiceBarcode].text().strip()))
            scene.addPixmap(pixmap)
            self.widgetBarcode.show()


    def self_destruct(self):
        if self.layout() is not None:
            while self.layout().count():
                child = self.layout().takeAt(0)
                if child.widget() is not None:
                    child.widget().deleteLater()
        self.setParent(None)
        self.deleteLater()

    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.setFixedSize(self.anchoWidget, 30)

        x = 0
        for i in range(len(self.datos)):
            label = QtWidgets.QLabel(Form)
            label.setGeometry(QtCore.QRect(x, 0, self.anchoRegistro, 30))
            label.setText("   "+str(self.datos[i]))
            x = x + self.anchoRegistro
            self.labels.append(label)
            separador = QtWidgets.QFrame(Form)
            separador.setGeometry(QtCore.QRect(x, 0, 3, 30))
            separador.setFrameShape(QtWidgets.QFrame.VLine)
            separador.setFrameShadow(QtWidgets.QFrame.Sunken)
            x = x+3

        if self.hayBotonEliminar:
            self.butEliminar = QtWidgets.QPushButton(Form)
            self.butEliminar.setGeometry(QtCore.QRect(x,0,30,30))
            self.butEliminar.setStyleSheet("background:red;font-size:20px")
            self.butEliminar.setText("x")
            self.butEliminar.clicked.connect(self.self_destruct)
            x = x + 30

        if self.hayBotonReponer:
            self.butReponer = QtWidgets.QPushButton(Form)
            self.butReponer.setGeometry(QtCore.QRect(x, 0, 30, 30))
            font = QtGui.QFont()
            font.setFamily("Arial")
            font.setPointSize(15)
            self.butReponer.setFont(font)
            self.butReponer.setStyleSheet("background-color:cyan;color:white")
            self.butReponer.setObjectName("butReponer")
            self.butReponer.setText("+")
            x  = x + 30

        if self.hayBotonEditar:
            self.butEditar = QtWidgets.QPushButton(Form)
            self.butEditar.setGeometry(QtCore.QRect(x, 0, 30, 30))
            font = QtGui.QFont()
            font.setFamily("Arial")
            font.setPointSize(8)
            self.butEditar.setFont(font)
            self.butEditar.setStyleSheet("background-color:lightgreen;color:green")
            self.butEditar.setText("edit")
            self.butEditar.setObjectName("butEditar")

            
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Form = QtWidgets.QWidget()
    ui = ResponsiveDataWidget(["hola","adios","buenas","tardes","amigo","qué tal"],True,True,True,500)
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())

And this is MostrarBarcode class:

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QWidget


class MostrarBarcode(QWidget):

    def __init__(self):
        super().__init__()
        self.setupUi(self)

    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(190, 130)

        self.barcodePlaceholder = QtWidgets.QGraphicsView(Form)
        self.barcodePlaceholder.setGeometry(QtCore.QRect(0, 0, 190, 100))
        self.barcodePlaceholder.setObjectName("barcodePlaceholder")

        self.butDescargar = QtWidgets.QPushButton(Form)
        self.butDescargar.setGeometry(QtCore.QRect(115, 100, 75, 30))
        self.butDescargar.setObjectName("butDescargar")

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.butDescargar.setText(_translate("Form", "descargar"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Form = QtWidgets.QWidget()
    ui = MostrarBarcode()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())

the project is bigger so I cant give all the classes, but my issue lies in those two. My problem is that, when executing my app via 'python app.py', this classes work as intended: when clicking a 'ResponsiveDataWidget', a 'MostrarBarcode' window is displayed containing the image of the generated barcode. But when I generate a .exe of the project with 'pyinstaller --onefile app.py' and I execute the app via the .exe, It gives the following error:

error on cmd line I'm lost here.

I tried other methods of compilation but none of them worked. I wanna stick with pyinstaller because it seems like the easiest and fastest one to use.

EDIT: I tried the sugestion from the first answer but I get the error above The code is now:

    def generar_codigo_barras(self, numero):
        # Generar el código de barras con un archivo temporal
        with tempfile.NamedTemporaryFile(mode="wb", delete=False, suffix=".png") as tmp:
            codigo = barcode.get('code39', numero, writer=ImageWriter())
            codigo.write(tmp)
            tmp_path = tmp.name  # Guardar la ruta del archivo temporal

        # Abrir, redimensionar y guardar la imagen redimensionada en la ruta temporal
        with Image.open(tmp_path) as imagen:
            redim_imagen = imagen.resize((190, 100), Image.Resampling.LANCZOS)
            redim_imagen.save(tmp_path)

        return tmp_path

    def mostrarBarcode(self):
        if self.indiceBarcode is not None:
            self.widgetBarcode = MostrarBarcode()  # Asegúrate de que MostrarBarcode esté definido correctamente
            scene = QGraphicsScene(self.widgetBarcode.barcodePlaceholder)
            self.widgetBarcode.barcodePlaceholder.setScene(scene)
            ruta_imagen = self.generar_codigo_barras(self.labels[self.indiceBarcode].text().strip())
            pixmap = QPixmap(ruta_imagen)
            scene.addPixmap(pixmap)
            self.widgetBarcode.show()

EDIT #2: SOLVED!! It seems that python-barcode uses a font not supported by pyinstaller's default packaging. Changing this line from it's source code to an OS' default font like arial solves the issue: self.font_path = os.path.join(PATH, "fonts", "calibri.ttf")

1

There are 1 best solutions below

2
eissar On

It appears like you are creating a new file in the 'generar_codigo_barras' function. When you compile to the exe, it's possible it's trying to create the QR code, but I don't believe you can create new files in an executable for security/integrity reasons, You may have to create the QR code in the user's temp folder. How to save a temporarily jpg file by using python?

You can add some sanity checks with the print() function to make sure everything is working correctly.