Search code examples
python-3.xuser-interfacepyqt5touch-eventqpainter

Drawing a circle on a QWidget python GUI


I'm trying to draw a circle instead of dots Here is the class where it paints red dots on pressing event at any place in the widget I want to draw a circle (hollow circle) as an outline not a solid circle that hide part of my picture

     import sys
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *

        class Canvas(QWidget):

            def __init__(self, photo, *args, **kwargs):
                super().__init__(*

args, **kwargs)
            self.image = QImage(photo)
            self.setFixedSize(self.image.width(), self.image.height())

        def mousePressEvent(self, event):
            if event.button() == Qt.LeftButton:
                qp = QPainter(self.image)
                qp.setRenderHint(QPainter.Antialiasing)
                qp.setPen(QPen(Qt.red, 5))
                qp.setBrush(Qt.red)
                qp.drawPoint(event.pos())
                self.update()

        def paintEvent(self, event):
            qp = QPainter(self)
            rect = event.rect()
            qp.drawImage(rect, self.image, rect)


    class MainWindow(QMainWindow):

        def __init__(self):
            super().__init__()
            w = QWidget()
            self.setCentralWidget(w)
            grid = QGridLayout(w)
            grid.addWidget(Canvas('photo.jpeg'))

    if __name__ == '__main__':
        app = QApplication(sys.argv)
        gui = MainWindow()
        gui.show()
        sys.exit(app.exec_())

Solution

  • I believe this is what you're trying to do.

    enter image description here

    In which case you need to take advantage of which paint device to pass to QPainter. During mousePressEvent and mouseMoveEvent use QPainter(self) so anything painted will only last until the next update. Then in mouseReleaseEvent when satisfied with the size of the circle, you can paint on the QImage with QPainter(self.image) to permanently draw the circle.

    import sys
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    
    class Canvas(QWidget):
    
        def __init__(self, photo, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.image = QImage(photo)
            self.setFixedSize(self.image.width(), self.image.height())
            self.pressed = self.moving = False
            self.revisions = []
    
        def mousePressEvent(self, event):
            if event.button() == Qt.LeftButton:
                self.pressed = True
                self.center = event.pos()
                self.update()
    
        def mouseMoveEvent(self, event):
            if event.buttons() & Qt.LeftButton:
                self.moving = True
                r = (event.pos().x() - self.center.x()) ** 2 + (event.pos().y() - self.center.y()) ** 2
                self.radius = r ** 0.5
                self.update()
    
        def mouseReleaseEvent(self, event):
            if event.button() == Qt.LeftButton:
                self.revisions.append(self.image.copy())
                qp = QPainter(self.image)
                self.draw_circle(qp) if self.moving else self.draw_point(qp)
                self.pressed = self.moving = False
                self.update()
    
        def paintEvent(self, event):
            qp = QPainter(self)
            rect = event.rect()
            qp.drawImage(rect, self.image, rect)
            if self.moving:
                self.draw_circle(qp)
            elif self.pressed:
                self.draw_point(qp)
    
        def draw_point(self, qp):
            qp.setPen(QPen(Qt.black, 5))
            qp.drawPoint(self.center)
    
        def draw_circle(self, qp):
            qp.setRenderHint(QPainter.Antialiasing)
            qp.setPen(QPen(Qt.black, 3, Qt.DashLine))
            qp.drawEllipse(self.center, self.radius, self.radius)
    
        def undo(self):
            if self.revisions:
                self.image = self.revisions.pop()
                self.update()
    
        def reset(self):
            if self.revisions:
                self.image = self.revisions[0]
                self.revisions.clear()
                self.update()
    
    
    class MainWindow(QMainWindow):
    
        def __init__(self):
            super().__init__()
            w = QWidget()
            self.setCentralWidget(w)
            canvas = Canvas('photo.png')
            grid = QGridLayout(w)
            grid.addWidget(canvas)
            QShortcut(QKeySequence('Ctrl+Z'), self, canvas.undo)
            QShortcut(QKeySequence('Ctrl+R'), self, canvas.reset)
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        gui = MainWindow()
        gui.show()
        sys.exit(app.exec_())