Search code examples
pythonpyqtqpainter

Integrating QPainter in PyQt GUI


I'm trying to use QPainter-made object along with native widgets of PyQt in one layout and having a difficulty doing so.

I've tried using addWidget() to add it to the layout but no luck.

class window(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(600, 500)
        self.setWindowTitle('GUI 2.0')

        #LCD
        self.lcd = QLCDNumber()
        self.lcd.setFixedHeight(100)

        #Slider
        self.slider = QSlider(Qt.Horizontal)
        self.slider.setMaximum(40)
        self.slider.setMinimum(0)
        self.slider.valueChanged.connect(self.progress)

        #Push button
        pb = QPushButton('Button', self)
        pb.clicked.connect(self.button)

        #Progress Bar
        self.pbar = QProgressBar(self)
        self.pbar.setGeometry(30, 40, 200, 25)
        self.pbar.setTextVisible(False)

        #Qpainter
        painter = QPainter()
        painter.setPen(QPen(Qt.black, 5, Qt.SolidLine))
        painter.setBrush(QBrush(Qt.green, Qt.DiagCrossPattern))
        painter.drawRect(100, 15, 400,200)

        #Grid Layout 
        layout = QGridLayout()
        layout.addWidget(self.lcd, 1, 1)
        layout.addWidget(self.slider, 2, 1)
        layout.addWidget(pb, 2, 2)
        layout.addWidget(self.pbar, 0, 1)
        layout.addWidget(painter) # doesn't work!

        self.setLayout(layout)

My aim is for something like this where the slider will eventually change the water level in the tank and the button will reset the slider value and therefore empty the tank:

Water Tank

Water Tank

Whereas I have something like this:

Self-Made

How can I add the painter object to the layout?


Solution

  • QPainter is not a widget but only paints on a device (QWidget, QImage, QPixmap, etc.), so in your case you must create a custom widget where in the paintEvent method the tank is painted

    from PyQt5 import QtCore, QtGui, QtWidgets
    
    
    class TankWidget(QtWidgets.QWidget):
        progressChanged = QtCore.pyqtSignal(float)
    
        def __init__(self, parent=None):
            super().__init__(parent)
    
            self._progress = 0.0
    
        @QtCore.pyqtProperty(float, notify=progressChanged)
        def progress(self):
            return self._progress
    
        @progress.setter
        def progress(self, p):
            if 0 <= p <= 1.0:
                self._progress = p
                self.progressChanged.emit(p)
                self.update()
    
        def paintEvent(self, event):
            painter = QtGui.QPainter(self)
            height = self.progress * self.height()
    
            r = QtCore.QRect(0, self.height() - height, self.width(), height)
            painter.fillRect(r, QtGui.QBrush(QtCore.Qt.blue))
            pen = QtGui.QPen(QtGui.QColor("red"), 10)
            painter.setPen(pen)
            painter.drawRect(self.rect())
    
        def sizeHint(self):
            return QtCore.QSize(100, 100)
    
    
    class Widget(QtWidgets.QWidget):
        def __init__(self):
            super().__init__()
            self.resize(600, 500)
            self.setWindowTitle("GUI 2.0")
    
            self.tank = TankWidget()
            self.progressbar = QtWidgets.QProgressBar()
            self.lcd = QtWidgets.QLCDNumber()
            self.lcd.setFixedHeight(100)
            self.slider = QtWidgets.QSlider(QtCore.Qt.Horizontal, maximum=101)
            self.button = QtWidgets.QPushButton("Button")
    
            self.slider.valueChanged.connect(self.on_value_changed)
    
            lay = QtWidgets.QGridLayout(self)
            lay.addWidget(self.tank, 0, 0, 2, 1)
            lay.addWidget(QtWidgets.QLabel("Tank", alignment=QtCore.Qt.AlignCenter), 2, 0)
            lay.addWidget(self.progressbar, 0, 1)
            lay.addWidget(self.lcd, 1, 1)
            lay.addWidget(self.slider, 2, 1)
            lay.addWidget(
                QtWidgets.QLabel(
                    pixmap=QtGui.QPixmap("warning.png"), alignment=QtCore.Qt.AlignCenter
                ),
                0,
                2,
                2,
                1,
            )
            lay.addWidget(self.button, 2, 2)
    
            self.setFixedHeight(self.sizeHint().height())
    
            self.slider.setValue(40)
    
        @QtCore.pyqtSlot()
        def on_value_changed(self):
            progress = self.slider.value() * 1.0 / self.slider.maximum()
            self.tank.progress = progress
            self.progressbar.setValue(self.slider.value())
            self.lcd.display(self.slider.value())
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
        w = Widget()
        w.show()
        sys.exit(app.exec_())
    

    enter image description here