The program terminates without warning [PyQt5]

0

I'm writing Paint-like program on PyQt5. I encountered a problem during writing pen tool.

My idea: if currentMode == 1 (penMode key) and mouse pressed - then program creates circle with width value == self.width and color == self.color. But my program terminates with exit code -1073740791 (0xC0000409). I'm not native English and I can't find way to fix this problem.

My code (main part):

class TPaintWorker(QtWidgets.QMainWindow, TPaintGUI):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.currentMode = 0
        self.width = 0
        self.drawX = 0
        self.drawY = 0
        self.endX = 0
        self.endY = 0
        self.doPaint = False
        self.color = QtGui.QColor(255, 255, 255)
        self.penMode.clicked.connect(self.penDrawerActivate)
        self.polygonMode.clicked.connect(self.polygonDrawerActivate)
        self.circleMode.clicked.connect(self.circleDrawerActivate)
        self.eraserMode.clicked.connect(self.eraserActivate)
        self.saveImage.clicked.connect(self.saveProcess)
        self.loadImage.clicked.connect(self.loadProcess)

    def mousePressEvent(self, event):
        if self.currentMode:
            self.drawX = event.x
            self.drawY = event.y
            self.paintPrepare()
        self.update()

    def paintPrepare(self):
        self.doPaint = True
        self.repaint()

    def paintEvent(self, event):
        if self.doPaint:
            qp = QtGui.QPainter()
            qp.begin(self)
            if self.currentMode == 1:
                self.penDrawer(qp)
            print("im out")
            qp.end()

    def penDrawerActivate(self):
        self.currentMode = 1
        self.width = QtWidgets.QInputDialog.getInt(self, "Input width value", "Width value:", 5, 1, 100, 1)
        self.color = QtWidgets.QColorDialog.getColor(QtGui.QColor(255, 255, 255))

    def penDrawer(self, qp):
        print("im in")
        self.pen = QtGui.QPen(self.color)
        qp.setPen(self.pen)
        qp.drawEllipse(self.drawX, self.drawY, self.width, self.width)


    def polygonDrawerActivate(self):
        self.currentMode = 2
        self.dots = QtWidgets.QInputDialog.getInt(self, "Input number of dots", "Number of dots:", 4, 3, 25, 1)
        self.width = QtWidgets.QInputDialog.getInt(self, "Input width value", "Width value:", 5, 1, 100, 1)
        self.color = QtWidgets.QColorDialog.getColor(QtGui.QColor(255, 255, 255))

    def circleDrawerActivate(self):
        self.currentMode = 3
        self.radius = QtWidgets.QInputDialog.getInt(self, "Input radius of circle", "Radius:", 50, 5, 200, 1)
        self.width = QtWidgets.QInputDialog.getInt(self, "Input width value", "Width value:", 5, 1, 100, 1)
        self.color = QtWidgets.QColorDialog.getColor(QtGui.QColor(255, 255, 255))

    def eraserActivate(self):
        self.currentMode = 4
        self.width = QtWidgets.QInputDialog.getInt(self, "Input width of eraser", "Width value:", 50, 5, 200, 1)

    def loadProcess(self):
        loadPath, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Load image", "C:/", "PNG (*.png);;JPEG (*.jpg)")
        if loadPath:
            pixmap = QtGui.QPixmap(loadPath)
            self.canvas.setPixmap(pixmap)

    def saveProcess(self):
        savePath, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save image", "C:/", "PNG (*.png);;JPEG (*.jpg)")
        if savePath:
            image = QtGui.QImage(self.canvas.pixmap())
            image.save(savePath)
python
user-interface
pyqt5
asked on Stack Overflow Nov 1, 2020 by tiom4eg

1 Answer

0

First of all, I suggest you to check the output of errors by running your program in a shell/prompt (note that IDEs sometimes are not completely able to show the full traceback): you'd have probably track your issues on your own.

The first problem is that you're not getting the coordinates in the mousePressEvent(); in Qt, access to almost all properties is done through callables (except some very specific exceptions, like properties of QStyleOption subclasses).
The result is that drawX and drawY are not actually coordinates, but references to event.x and event.y functions respectively.

Then, all QInputDialog static methods return a tuple made of (value, accepted): the second value specifies if the dialog has been accepted or not (if the user pressed Cancel, Esc or closed the window).
Since you're setting all values to the full tuple, Qt will obviously crash whenever you're trying to use those values for drawing, as the related QPainter function will not accept those parameters; as a side note, consider that if the user doesn't accept the dialog (because maybe she/he clicked the wrong button), you should not set those values, nor go on asking other parameters.

Finally, two suggestions.

  1. painting on the main window should not happen unless really required; this is because a QMainWindow is a very special kind of QWidget, and has a specific set of features that can potentially create issues with custom painting; drawing on a dedicated QWidget should be preferred instead. This can be done by installing an event filter on that widget (probably, the central widget) and paint when a paint event is detected, or by subclassing QWidget, implement its own paintEvent and add that widget to the main window.
  2. (this is quite trivial, but it can slightly simplify your code) you don't need to manually call begin and end on the QPainter instance, as you can just create the instance with the paint device in the constructor (qp = QtGui.QPainter(self)) and ignore the ending, since the painter will be automatically closed and correctly destroyed as soon as the paintEvent function returns.
answered on Stack Overflow Nov 1, 2020 by musicamante

User contributions licensed under CC BY-SA 3.0