PyQt: How to switch widgets in QStackedWidget

后端 未结 5 800
伪装坚强ぢ
伪装坚强ぢ 2020-12-11 23:11

I have two buttons (wgtbtnA & wgtbtnB) placed on two different pages (page1 and page2, respectively) inside a parent object (objectNa

5条回答
  •  攒了一身酷
    2020-12-11 23:33

    If you really need arrows in your stacked widget, another solution is to implement your own "Promoted Widget": it allows you to create your design with custom widgets, that might extend the basic widgets provided by Qt. You won't be able to interact with your own implementation in Designer, but you'll get the result once you run your program.

    This is the procedure: create your own subclass of the widget you want to extend, define your custom methods or override existing ones (remember that some private methods require a specific return value type, check the documentation). It's usually better to save the subclass(es) you created in a separate files. Then, in Designer add the widget you need (in this case, StackedWidget), right click on it and select "Promote to..."; in the dialog that will be shown, type the subclass name you created in the "Promoted class name" field (in the example below, it will be "StackedWidgetWithArrowButtons") and the file that contains it in the "header file" field: it will be treated as a python import, so do not add the trailing ".py" and remember that if you saved it in a subdirectory you'll need the full "module" path, for example "mysubclasses.customstackwidget", if the file is "customstackwidget" in the "mysubclasses" directory. Save the ui, compile it and run the program.

    class StackedWidgetWithArrowButtons(QtWidgets.QStackedWidget):
        def __init__(self, *args, **kwargs):
            QtWidgets.QStackedWidget.__init__(self, *args, **kwargs)
            self.backwardButton = QtWidgets.QToolButton(self)
            self.backwardButton.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_ArrowLeft))
            self.backwardButton.setMaximumSize(24, 24)
            self.backwardButton.setFocusPolicy(QtCore.Qt.NoFocus)
            self.forwardButton = QtWidgets.QToolButton(self)
            self.forwardButton.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_ArrowRight))
            self.forwardButton.setMaximumSize(24, 24)
            self.forwardButton.setFocusPolicy(QtCore.Qt.NoFocus)
            self.currentChanged.connect(self.checkSwitchButtons)
    
        def checkSwitchButtons(self):
            self.forwardButton.setEnabled(self.currentIndex() < self.count() - 1)
            self.backwardButton.setEnabled(self.currentIndex() > 0)
    
        def addWidget(self, widget):
            # this is a private method of QStackedWidget that is called when 
            # the ui is being built by the program, we just implement it 
            # to ensure that the buttons are correctly enabled;
            # the index *has* to be returned
            index = QtWidgets.QStackedWidget.addWidget(self, widget)
            self.checkSwitchButtons()
            return index
    
        def removeWidget(self, widget):
            # not necessary, but in case you want to remove widgets in the 
            # future, it will check buttons again
            index = QtWidgets.QStackedWidget.removeWidget(self, widget)
            self.checkSwitchButtons()
            return index
    
        def mousePressEvent(self, event):
            # due to the way QStackedWidget is implemented, children widgets
            # that are not in its layout might not receive mouse events,
            # but we just need to track clicks so this is enough
            if event.button() == QtCore.Qt.LeftButton:
                if event.pos() in self.backwardButton.geometry():
                    self.setCurrentIndex(self.currentIndex() - 1)
                elif event.pos() in self.forwardButton.geometry():
                    self.setCurrentIndex(self.currentIndex() + 1)
    
        def resizeEvent(self, event):
            # the base class resizeEvent *has* to be called, otherwise 
            # you could encounter problems with children widgets
            QtWidgets.QStackedWidget.resizeEvent(self, event)
    
            # now ensure that the buttons are always placed on the top
            # right corner; this positioning is completely manual and you
            # have to take button sizes in consideration to avoid 
            # overlapping buttons; obviously you can place them wherever
            # you want.
            self.forwardButton.move(self.rect().right() - self.forwardButton.width(), 0)
            self.backwardButton.move(self.forwardButton.x() - self.backwardButton.width(), 0)
    

    If you don't want buttons (or you don't like the way they appear) you could implement your own paintEvent. In this case I created small triangles using QPolygons.

    class StackedWidgetWithTriangles(QtWidgets.QStackedWidget):
        def __init__(self, *args, **kwargs):
            QtWidgets.QStackedWidget.__init__(self, *args, **kwargs)
            self.backwardRect = QtCore.QRect(0, 0, 16, 16)
            self.forwardRect = QtCore.QRect(0, 0, 16, 16)
            self.forwardArrow = QtGui.QPolygon([QtCore.QPoint(-6, -6), QtCore.QPoint(6, 0), QtCore.QPoint(-6, 6)])
            self.backwardArrow = QtGui.QPolygon([QtCore.QPoint(6, -6), QtCore.QPoint(-6, 0), QtCore.QPoint(6, 6)])
    
        def mousePressEvent(self, event):
            if event.button() == QtCore.Qt.LeftButton:
                if event.pos() in self.backwardRect:
                    self.setCurrentIndex(self.currentIndex() - 1)
                elif event.pos() in self.forwardRect:
                    self.setCurrentIndex(self.currentIndex() + 1)
    
        def resizeEvent(self, event):
            QtWidgets.QStackedWidget.resizeEvent(self, event)
            self.forwardRect.moveLeft(self.rect().right() - self.forwardRect.width())
            self.backwardRect.moveLeft(self.forwardRect.x() - self.forwardRect.width())
    
        def paintEvent(self, event):
            qp = QtGui.QPainter(self)
            qp.setRenderHints(qp.Antialiasing)
            # set colors according to the possibility of going back or forward,
            # showing a "disabled" arrow whenever it's not possible
            if self.currentIndex() > 0:
                qp.setPen(QtCore.Qt.darkGray)
                qp.setBrush(QtCore.Qt.black)
            else:
                qp.setPen(QtCore.Qt.lightGray)
                qp.setBrush(QtCore.Qt.transparent)
            qp.drawPolygon(self.backwardArrow.translated(self.backwardRect.center()))
            if self.currentIndex() < self.count() - 1:
                qp.setPen(QtCore.Qt.darkGray)
                qp.setBrush(QtCore.Qt.black)
            else:
                qp.setPen(QtCore.Qt.lightGray)
                qp.setBrush(QtCore.Qt.transparent)
            qp.drawPolygon(self.forwardArrow.translated(self.forwardRect.center()))
    

提交回复
热议问题