Search code examples
pythonpyqtpyqt5qwidgetqpainter

Showing different Paint class in same Qwidget


Following image showing an app with one input widget and one paintwidget.

and there are two paint classes in the code. The idea is when a button is clicked, calling first paint class and show it in Paintwidget, well when another paint class is called, wants to show in same Paintwidget. Not really overlap each other, rather than showing in same widget.

How could that be done?

When round button clicks this codeline changes from

    self.mainSplitter.addWidget(self.paint1)

to

    self.mainSplitter.addWidget(self.paint2)

And vice versa

Visualization:

enter image description here

The code:

from PyQt5 import QtCore, QtGui, QtWidgets

class Foo(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(Foo, self).__init__(parent)
        self.setGeometry(QtCore.QRect(200, 100, 800, 650))

        self.button = Button()
        self.paint1 = Paintwidget1()
        self.paint2 = Paintwidget2()
        self.button.valuesChanged.connect(self.paint1.set_size_squares)
        self.button.valueChanged.connect(self.paint2.set_size_round)

        self.mainSplitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
        self.mainSplitter.addWidget(self.button)
        self.mainSplitter.addWidget(self.paint1)

        self.setCentralWidget(self.mainSplitter)

class Paintwidget1(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.sizeHint()        
        self.setBackgroundRole(QtGui.QPalette.Base)     
        self.setAutoFillBackground(True)

        self._size = QtCore.QSizeF()
        self._path = QtGui.QPainterPath()
        self._rect = QtCore.QRectF()
        self._type = QtGui.QRegion.Rectangle
        self._factor = 1.0

        self._pos = QtCore.QPointF()
        self._initial_flag = False
        fnt = self.font() 
        fnt.setPointSize(20) 
        self.setFont(fnt) 

    def showEvent(self, event):
        if not self._initial_flag:
            self._pos = self.rect().center()
            self._initial_flag = True

    @QtCore.pyqtSlot(int, int)
    def set_size_squares(self, w, h):
        self._path = QtGui.QPainterPath()
        self._size = QtCore.QSizeF(w, h)
        self._type = QtGui.QRegion.Rectangle
        self.updatePath()

    def paintEvent(self, event):
        pen = QtGui.QPen()
        brush = QtGui.QBrush(QtCore.Qt.black)
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        painter.setPen(pen)
        painter.setBrush(brush)

        painter.translate(self.rect().center())
        painter.scale(self._factor, self._factor)
        painter.translate(-self.rect().center())

        painter.translate(self._pos)
        painter.drawPath(self._path)
        if self._type == QtGui.QRegion.Rectangle:
            painter.fillRect(self._rect, QtGui.QBrush(QtCore.Qt.gray, QtCore.Qt.Dense7Pattern))
            painter.setBrush(QtGui.QBrush(QtCore.Qt.NoBrush))
            painter.drawRect(self._rect)


    def mousePressEvent(self, event):
        QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor))
        self._initial_pos = event.pos()
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        delta = event.pos() - self._initial_pos
        self._path.translate(delta)
        self._rect.translate(delta)
        self.update()
        self._initial_pos = event.pos()
        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        QtWidgets.QApplication.restoreOverrideCursor()
        super().mouseReleaseEvent(event)

    def updatePath(self):
        r = QtCore.QRectF(QtCore.QPointF(), self._size)
        r.moveCenter(QtCore.QPointF())
        self._rect = QtCore.QRectF(r)
        self.update()

    def wheelEvent(self, event):
        self._factor *= 1.01**(event.angleDelta().y()/15.0)
        self.update()
        super().wheelEvent(event)

class Paintwidget2(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.sizeHint()        
        self.setBackgroundRole(QtGui.QPalette.Base)     
        self.setAutoFillBackground(True)

        self._size = QtCore.QSizeF()
        self._path = QtGui.QPainterPath()
        self._rect = QtCore.QRectF()
        self._type = QtGui.QRegion.Rectangle
        self._factor = 1.0

        self._pos = QtCore.QPointF()
        self._initial_flag = False
        fnt = self.font() 
        fnt.setPointSize(20) 
        self.setFont(fnt) 

    def showEvent(self, event):
        if not self._initial_flag:
            self._pos = self.rect().center()
            self._initial_flag = True

    @QtCore.pyqtSlot(int)
    def set_size_round(self, v):
        self._path = QtGui.QPainterPath()
        self._size = QtCore.QSizeF(v, 0.8*v)
        self._type = QtGui.QRegion.Ellipse
        self.updatePath()

    def paintEvent(self, event):
        pen = QtGui.QPen()
        brush = QtGui.QBrush(QtCore.Qt.black)
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        painter.setPen(pen)
        painter.setBrush(brush)

        painter.translate(self.rect().center())
        painter.scale(self._factor, self._factor)
        painter.translate(-self.rect().center())

        painter.translate(self._pos)
        painter.drawPath(self._path)
        if self._type == QtGui.QRegion.Ellipse:
            painter.setBrush(QtGui.QBrush(QtCore.Qt.gray, QtCore.Qt.Dense7Pattern))
            painter.drawEllipse(self._rect)

    def mousePressEvent(self, event):
        QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor))
        self._initial_pos = event.pos()
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        delta = event.pos() - self._initial_pos
        self._path.translate(delta)
        self._rect.translate(delta)
        self.update()
        self._initial_pos = event.pos()
        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        QtWidgets.QApplication.restoreOverrideCursor()
        super().mouseReleaseEvent(event)

    def updatePath(self):
        r = QtCore.QRectF(QtCore.QPointF(), self._size)
        r.moveCenter(QtCore.QPointF())
        self._rect = QtCore.QRectF(r)
        self.update()

    def wheelEvent(self, event):
        self._factor *= 1.01**(event.angleDelta().y()/15.0)
        self.update()
        super().wheelEvent(event)

class Button(QtWidgets.QWidget):
    valueChanged = QtCore.pyqtSignal(int)
    valuesChanged = QtCore.pyqtSignal(int,int)
    def __init__(self, parent=None):
        super(Button, self).__init__(parent)
        roundbutton = QtWidgets.QPushButton('Round')
        squarebutton = QtWidgets.QPushButton('Square')
        Alay = QtWidgets.QVBoxLayout(self)
        Alay.addWidget(roundbutton)
        Alay.addWidget(squarebutton)
        self.value = QtWidgets.QLabel()
        roundbutton.clicked.connect(self.getbuttonfunc)
        squarebutton.clicked.connect(self.sqaurebuttonfunc)

    @QtCore.pyqtSlot()
    def getbuttonfunc(self):
        number, ok = QtWidgets.QInputDialog.getInt(self, self.tr("Set Number"),
                                         self.tr("Input:"), 1, 1)
        if ok:
            self.valueChanged.emit(number)

    @QtCore.pyqtSlot()
    def sqaurebuttonfunc(self):
        number, ok = QtWidgets.QInputDialog.getInt(self, self.tr("Set Number"),
                                         self.tr("Input:"), 1, 1)
        if ok:
            self.valuesChanged.emit(number, number)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Foo()
    w.show()
    sys.exit(app.exec_())

Solution

  • You have to use a QStackedWidget to exchange widget, also set the sizeHint() of Paintwidget1 and Paintwidget2:

    class Foo(QtWidgets.QMainWindow):
        def __init__(self, parent=None):
            super(Foo, self).__init__(parent)
            self.setGeometry(QtCore.QRect(200, 100, 800, 650))
            self.button = Button()
            self.paint1 = Paintwidget1()
            self.paint2 = Paintwidget2()
            self.button.valuesChanged.connect(self.on_paint1)
            self.button.valueChanged.connect(self.on_paint2)
            self.mainSplitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
            self._stacked_widget = QtWidgets.QStackedWidget()
            self.mainSplitter.addWidget(self.button)
            self.mainSplitter.addWidget(self._stacked_widget)
            self.setCentralWidget(self.mainSplitter)
            self._stacked_widget.addWidget(self.paint1)
            self._stacked_widget.addWidget(self.paint2)
    
        @QtCore.pyqtSlot(int, int)
        def on_paint1(self, w, h):
            self._stacked_widget.setCurrentIndex(0)
            self.paint1.set_size_squares(w, h)
    
        @QtCore.pyqtSlot(int)
        def on_paint2(self, v):
            self._stacked_widget.setCurrentIndex(1)
            self.paint2.set_size_round(v)
    
    class Paintwidget1(QtWidgets.QWidget):
        # ...
        def sizeHint(self):
            return QtCore.QSize(640, 480)
    
    class Paintwidget2(QtWidgets.QWidget):
        # ...
        def sizeHint(self):
            return QtCore.QSize(640, 480)