【问题标题】:PyQt: How to switch widgets in QStackedWidgetPyQt:如何在 QStackedWidget 中切换小部件
【发布时间】:2016-12-20 16:36:26
【问题描述】:

我有两个按钮(wgtbtnAwgtbtnB)放在两个不同的页面(page1page2,分别)在父对象(objectName:stackedWidget)内。我的困境是:当我运行代码时,箭头不会显示在 PyQt 中。为什么?如何从 page1 切换到 page2,反之亦然?

这是运行时的图像,它传达了我的要求:

Qt 设计师:

我想保留那些小后退箭头。

下面是我的代码:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'stackedWidget.ui'
#
# Created by: PyQt4 UI code generator 4.11.4
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui
import sys
import os


try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.resize(512, 304)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.stackedWidget = QtGui.QStackedWidget(self.centralwidget)
        self.stackedWidget.setGeometry(QtCore.QRect(150, 60, 161, 121))
        self.stackedWidget.setObjectName(_fromUtf8("stackedWidget"))
        self.page = QtGui.QWidget()
        self.page.setObjectName(_fromUtf8("page"))
        self.wgtMainWindow = QtGui.QPushButton(self.page)
        self.wgtMainWindow.setGeometry(QtCore.QRect(50, 50, 75, 23))
        self.wgtMainWindow.setObjectName(_fromUtf8("wgtMainWindow"))
        self.stackedWidget.addWidget(self.page)
        self.page_2 = QtGui.QWidget()
        self.page_2.setObjectName(_fromUtf8("page_2"))
        self.wgtbtnB = QtGui.QPushButton(self.page_2)
        self.wgtbtnB.setGeometry(QtCore.QRect(50, 50, 75, 23))
        self.wgtbtnB.setObjectName(_fromUtf8("wgtbtnB"))
        self.stackedWidget.addWidget(self.page_2)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtGui.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 512, 21))
        self.menubar.setObjectName(_fromUtf8("menubar"))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtGui.QStatusBar(MainWindow)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        self.stackedWidget.setCurrentIndex(1)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
        self.wgtMainWindow.setText(_translate("MainWindow", "Widget A", None))
        self.wgtbtnB.setText(_translate("MainWindow", "Widget B", None))


class ControlMainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(ControlMainWindow, self).__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    mySW = ControlMainWindow()
    mySW.show()
    sys.exit(app.exec_())

【问题讨论】:

    标签: python pyqt pyqt4 qt-designer qstackedwidget


    【解决方案1】:

    您可以使用按钮更改页面:{your QPushButton}.clicked.connect(lambda: {your QStackedWidget}.setCurrentIndex({another page}))

    举例:

    # -*- coding: utf-8 -*-
    
    # Form implementation generated from reading ui file 'stackedWidget.ui'
    #
    # Created by: PyQt4 UI code generator 4.11.4
    #
    # WARNING! All changes made in this file will be lost!
    
    from PyQt4 import QtCore, QtGui
    import sys
    
    
    try:
        _fromUtf8 = QtCore.QString.fromUtf8
    except AttributeError:
        def _fromUtf8(s):
            return s
    
    try:
        _encoding = QtGui.QApplication.UnicodeUTF8
        def _translate(context, text, disambig):
            return QtGui.QApplication.translate(context, text, disambig, _encoding)
    except AttributeError:
        def _translate(context, text, disambig):
            return QtGui.QApplication.translate(context, text, disambig)
    
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName(_fromUtf8("MainWindow"))
            MainWindow.resize(512, 304)
            self.centralwidget = QtGui.QWidget(MainWindow)
            self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
            self.stackedWidget = QtGui.QStackedWidget(self.centralwidget)
            self.stackedWidget.setGeometry(QtCore.QRect(150, 60, 161, 121))
            self.stackedWidget.setObjectName(_fromUtf8("stackedWidget"))
            self.page = QtGui.QWidget()
            self.page.setObjectName(_fromUtf8("page"))
            self.wgtMainWindow = QtGui.QPushButton(self.page)
            self.wgtMainWindow.setGeometry(QtCore.QRect(50, 50, 75, 23))
            self.wgtMainWindow.setObjectName(_fromUtf8("wgtMainWindow"))
            self.stackedWidget.addWidget(self.page)
            self.page_2 = QtGui.QWidget()
            self.page_2.setObjectName(_fromUtf8("page_2"))
            self.wgtbtnB = QtGui.QPushButton(self.page_2)
            self.wgtbtnB.setGeometry(QtCore.QRect(50, 50, 75, 23))
            self.wgtbtnB.setObjectName(_fromUtf8("wgtbtnB"))
            self.stackedWidget.addWidget(self.page_2)
            MainWindow.setCentralWidget(self.centralwidget)
            self.menubar = QtGui.QMenuBar(MainWindow)
            self.menubar.setGeometry(QtCore.QRect(0, 0, 512, 21))
            self.menubar.setObjectName(_fromUtf8("menubar"))
            MainWindow.setMenuBar(self.menubar)
            self.statusbar = QtGui.QStatusBar(MainWindow)
            self.statusbar.setObjectName(_fromUtf8("statusbar"))
            MainWindow.setStatusBar(self.statusbar)
    
            self.retranslateUi(MainWindow)
            self.stackedWidget.setCurrentIndex(1)
            QtCore.QMetaObject.connectSlotsByName(MainWindow)
    
        def retranslateUi(self, MainWindow):
            MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
            self.wgtMainWindow.setText(_translate("MainWindow", "Widget A", None))
            self.wgtbtnB.setText(_translate("MainWindow", "Widget B", None))
    
    
    class ControlMainWindow(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(ControlMainWindow, self).__init__(parent)
            self.ui = Ui_MainWindow()
            self.ui.setupUi(self)
    
            self.ui.wgtbtnB.clicked.connect(lambda : self.ui.stackedWidget.setCurrentIndex(0))
            self.ui.wgtMainWindow.clicked.connect(lambda : self.ui.stackedWidget.setCurrentIndex(1))
    
    if __name__ == '__main__':
        app = QtGui.QApplication(sys.argv)
        mySW = ControlMainWindow()
        mySW.show()
        sys.exit(app.exec_())
    

    启动应用:

    点击按钮后:

    点击另一个按钮后:

    【讨论】:

      【解决方案2】:

      您在设计器中看到的两个箭头(在下面的消息中)不会延续到您的应用程序,它是设计器中的一个功能,因此您可以轻松地在它们之间切换

      【讨论】:

      • @ekhumoro :如果您受到可以添加的数量的限制怎么办。由于我是新手,它不会让我在原始帖子中添加超过 2 张图片。不争辩;但是,当您可以添加的内容有限来证明这些观点时,很难提供具体性和清晰度。因此,我为我的无知道歉。也许,您对这些情况有替代解决方案?如果是这样,我很想听听。最好的问候,杰里米
      • @JeremyStiefel。无论如何编辑您的问题:具有必要代表的人可以稍后修复链接。由于没有足够的代表,您只能做有限的事情:所以简单的解决方案就是get more rep
      【解决方案3】:

      要实现从一个屏幕到下一个屏幕的箭头,您只需要传播这些箭头以确保它们出现在每个视图中 - 现在这可以通过代码轻松完成,但无法说明在设计器中这可能有多困难。这是一个执行您可能想要的操作的示例。

      from sys  import exit as sysExit
      
      from PyQt5.QtCore    import *
      from PyQt5.QtGui     import *
      from PyQt5.QtWidgets import *
      
      class Win1Disply(QFrame):
          def __init__(self, parent):
              QFrame.__init__(self)
      
              self.setFrameShape(QFrame.StyledPanel)
              self.setLineWidth(0.2)
              # -------
              self.Cntnr = QVBoxLayout()
              self.Cntnr.addWidget(QTextEdit('This is Window 1 with whatever contents you want'))
              self.Win1Btn = QPushButton('>>')
              self.Win1Btn.clicked.connect(parent.RightArrow)
              self.Cntnr.addWidget(self.Win1Btn)
              self.Cntnr.addStretch(1)
              # -------
              self.setLayout(self.Cntnr)
      
      class Win2Disply(QFrame):
          def __init__(self, parent):
              QFrame.__init__(self)
      
              self.setFrameShape(QFrame.StyledPanel)
              self.setLineWidth(0.2)
              # -------
              self.Cntnr = QVBoxLayout()
              self.Cntnr.addWidget(QTextEdit('This is Window 2 with whatever contents you want'))
              self.Win1Btn = QPushButton('>>')
              self.Win1Btn.clicked.connect(parent.RightArrow)
              self.Cntnr.addWidget(self.Win1Btn)
              self.Cntnr.addStretch(1)
              # -------
              self.setLayout(self.Cntnr)
      
      class OptionButtons(QToolButton):
      # Class OptionButtons ("Text", Connector) inherits from QToolButton
          def __init__(self, Text, Connector):
              QToolButton.__init__(self)
      
              self.setText(Text)
              self.setStyleSheet("font: bold;color: blue;height: 55px;width: 55px;")
              self.setIconSize(QSize(32,32))
              self.clicked.connect(Connector)
      
      ############################## Settings Class ##############################
      class OptionSettings(QDialog):
          def __init__(self, parent):
              QDialog.__init__(self, parent)
      
              line = QFrame()
              line.setFrameShape(QFrame.HLine)
              line.setFrameShadow(QFrame.Sunken)
      
              self.btnWin1 = OptionButtons('Win One', self.ShowWindow1)
              self.btnWin2 = OptionButtons('Win Two', self.ShowWindow2)
            # Vertical Box for Buttons *************************************
              self.UpLeft  = QVBoxLayout()
              self.UpLeft.addWidget(self.btnWin1)
              self.UpLeft.addWidget(self.btnWin2)
              self.UpLeft.addStretch(1)
        # Display Area on Right
            # Widget Flip Display ******************************************
              self.UpRite   = QHBoxLayout()
              self.Contents = QStackedWidget()
              self.Contents.addWidget(QTextEdit('Nothing Selected'))
              self.Contents.addWidget(Win1Disply(self))
              self.Contents.addWidget(Win2Disply(self))
              self.Contents.addWidget(QTextEdit('Settings Saved'))
              self.Contents.setCurrentIndex(0)
              self.UpRite.addWidget(self.Contents)
      
        # Button and Display Area on Top
              self.Upper = QHBoxLayout()
              self.Upper.addLayout(self.UpLeft)
              self.Upper.addLayout(self.UpRite)
        # Save and Cancel Area on Bottom
              self.btnSave = QPushButton("Save")
              self.btnSave.clicked.connect(self.SaveSettings)
              self.btnCncl = QPushButton("Cancel")
              self.btnCncl.clicked.connect(self.close)
              self.Lower   = QHBoxLayout()
              self.Lower.addStretch(1)
              self.Lower.addWidget(self.btnSave)
              self.Lower.addWidget(self.btnCncl)
        # Entire Options Window Layout
              self.OuterBox = QVBoxLayout()
              self.OuterBox.addLayout(self.Upper)
              self.OuterBox.addLayout(self.Lower)
              self.setLayout(self.OuterBox)
              self.setWindowTitle('Settings')
              #Geometry(Left, Top, Width, Hight)
              self.setGeometry(250, 250, 550, 450)
              self.setModal(True)
              self.exec()
      
          def ShowWindow1(self):
              self.Contents.setCurrentIndex(1)
      
          def ShowWindow2(self):
              self.Contents.setCurrentIndex(2)
      
          def SaveSettings(self):
              self.Contents.setCurrentIndex(3)
      
          def RightArrow(self):
              if self.Contents.currentIndex() == 1:
                 self.Contents.setCurrentIndex(2)
              else:
                 self.Contents.setCurrentIndex(1)
      
      class CenterPanel(QWidget):
          def __init__(self, MainWin):
              QWidget.__init__(self)
      
              CntrPane = QTextEdit('Center Panel is Placed Here')
      
              hbox = QHBoxLayout(self)
              hbox.addWidget(CntrPane)
      
              self.setLayout(hbox)
      
      class MenuToolBar(QDockWidget):
          def __init__(self, MainWin):
              QDockWidget.__init__(self)
              self.MainWin = MainWin
              self.MainMenu = MainWin.menuBar()
      
              self.WndowMenu  = self.MainMenu.addMenu('Windows')
      
              self.OptnAct = QAction('Options', self)
              self.OptnAct.setStatusTip('Open the Options Window')
              self.OptnAct.triggered.connect(MainWin.ShowOptions)
      
              self.WndowMenu.addAction(self.OptnAct)
      
              self.InitToolBar(MainWin)
      
          def InitToolBar(self, MainWin):
              self.mainToolBar = MainWin.addToolBar("Quick Access")
      
              self.mainToolBar.addAction(self.OptnAct)
      
      class UI_MainWindow(QMainWindow):
          def __init__(self):
              super(UI_MainWindow, self).__init__()
              self.setWindowTitle('Main Window')
      
            # Left, Top, Width, Height
              self.setGeometry(200, 200, 550, 550)
      
              self.CenterPane = CenterPanel(self)
              self.setCentralWidget(self.CenterPane)
      
              self.MenuToolBar = MenuToolBar(self)
      
          def ShowOptions(self):
              self.Options = OptionSettings(self)
      
      if __name__ == '__main__':
          MainApp = QApplication([])
      
          MainGui = UI_MainWindow()
          MainGui.show()
      
          sysExit(MainApp.exec_())
      

      【讨论】:

        【解决方案4】:

        如果您真的需要在堆叠的小部件中使用箭头,另一种解决方案是实现您自己的“Promoted Widget”:它允许您使用自定义小部件创建您的设计,这可能会扩展 Qt 提供的基本小部件。您将无法在 Designer 中与您自己的实现进行交互,但您将在运行程序后获得结果。

        这是程序:创建您自己想要扩展的小部件的子类,定义您的自定义方法或覆盖现有方法(请记住,某些私有方法需要特定的返回值类型,请查看文档)。 通常最好将您创建的子类保存在单独的文件中。 然后,在 Designer 中添加您需要的小部件(在本例中为 StackedWidget),右键单击它并选择“Promote to...”;在将显示的对话框中,键入您在“Promoted class name”字段中创建的子类名称(在下面的示例中,它将是“StackedWidgetWithArrowButtons”)以及在“header file”字段中包含它的文件:它将被视为 python 导入,因此不要添加尾随的“.py”并记住,如果将其保存在子目录中,则需要完整的“模块”路径,例如“mysubclasses” .customstackwidget”,如果文件是“mysubclasses”目录中的“customstackwidget”。 保存ui,编译并运行程序。

        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)
        

        如果您不想要按钮(或者您不喜欢它们的显示方式),您可以实现自己的paintEvent。在这种情况下,我使用 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()))
        

        【讨论】:

          【解决方案5】:

          ControlMainWindow 类中没有逻辑可以在小部件之间切换。 (我也没有看到任何用于切换的箭头小部件)您需要在您的主类中为您的QTbuttons 添加一个侦听器,如下所示以执行您的逻辑:

          yourQTbutton.itemClicked.connect(self.functioWithUIchangingLogic)
          

          【讨论】:

          • 对不起。我应该更具体。我知道如何连接信号和插槽。我的问题是,pyqt 自动格式化的箭头在哪里。通常,stackedWidgets 的右上角有两个黑色箭头。
          • 您是否使用 QT 设计器来创建 UI?如果是这样,您在设计器中看到的两个箭头不会延续到您的应用程序,它是设计器中的一个功能,因此您可以轻松地在它们之间切换
          • 知道了。所以没有办法以编程方式将它们传递过来,对吗?
          猜你喜欢
          • 1970-01-01
          • 2020-08-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-16
          • 1970-01-01
          • 2016-07-30
          • 1970-01-01
          相关资源
          最近更新 更多