Python PyQt:如何在QStackedWidget中切换小部件

Python PyQt:如何在QStackedWidget中切换小部件,python,pyqt,pyqt4,qt-designer,qstackedwidget,Python,Pyqt,Pyqt4,Qt Designer,Qstackedwidget,我有两个按钮(wgtbtnA和wgtbtnB)放在父对象(objectName:stackedWidget)内的两个不同页面(分别为page1和page2)上。我的困境是:当我运行代码时,箭头不会显示在PyQt中。为什么?如何从第1页切换到第2页,反之亦然 下面是运行时的图像,它传达了我的要求: Qt设计器: 我想保留那些小背箭 下面是我的代码: # -*- coding: utf-8 -*- # Form implementation generated from reading ui

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

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

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_())

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

yourQTbutton.itemClicked.connect(self.functioWithUIchangingLogic)

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

您可以使用按钮更改页面:
{your QPushButton}.clicked.connect(lambda:{your QStackedWidget}.setCurrentIndex({other 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_())
启动应用程序:

单击按钮后:

单击另一个按钮后:


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

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_())

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

这是一个过程:创建您想要扩展的小部件的自己的子类,定义您的自定义方法或覆盖现有的方法(记住一些私有方法需要特定的返回值类型,请查看文档)。 通常最好将创建的子类保存在单独的文件中。 然后,在Designer中添加您需要的小部件(在本例中为StackedWidget),右键单击它并选择“升级到…”;在将显示的对话框中,在“升级的类名”字段中键入您创建的子类名称(在下面的示例中,它将是“StackedWidgetWithArrowButtons”),并在“头文件”字段中键入包含它的文件:它将被视为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)
类StackedWidgetWithArrowButtons(QtWidgets.QStackedWidget):
定义初始化(self,*args,**kwargs):
qtwidts.QStackedWidget.\uuuuu init\uuuuuu(self,*args,**kwargs)
self.backardButton=QtWidgets.QToolButton(self)
self.backwardButton.setIcon(self.style().standardIcon(qtwidts.QStyle.SP_箭头左))
self.backwardButton.setMaximumSize(24,24)
self.backwardButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.forwardButton=qtwidts.QToolButton(self)
self.forwardButton.setIcon(self.style().standardIcon(qtwidts.QStyle.SP_arrow right))
self.forwardButton.setMaximumSize(24,24)
self.forwardButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.currentChanged.connect(self.check开关按钮)
def检查开关按钮(自):
self.forwardButton.setEnabled(self.currentIndex()0)
def addWidget(self,widget):
#这是QStackedWidget的私有方法,在
#ui是由程序构建的,我们只是实现它
#确保按钮正确启用;
#必须返回索引*
index=QtWidgets.QStackedWidget.addWidget(self,widget)
self.checkSwitchButtons()
回报指数
def removeWidget(自我,小部件):
#不需要,但如果您想删除
#将来,它将再次检查按钮
index=QtWidgets.QStackedWidget.removeWidget(self,widget)
self.checkSwitchButtons()
回报指数
def鼠标压力事件(自身、事件):
#由于QStackedWidget的实现方式,子部件
#不在其布局中的可能不会接收鼠标事件,
#但我们只需要跟踪点击,这样就足够了
如果event.button()==QtCore.Qt.LeftButton:
如果self.backwardButton.geometry()中的event.pos()为:
self.setCurrentIndex(self.currentIndex()-1)
self.forwardButton.geometry()中的elif event.pos():
self.setCurrentIndex(self.currentIndex()+1)
def resizeEvent(自我,事件):
#否则,必须调用基类resizeEvent*
#您可能会遇到有关子部件的问题
qtwidts.QStackedWidget.resizeEvent(self,event)
#现在确保按钮始终位于顶部
#右上角;此定位完全是手动的,您可以
#必须考虑按钮尺寸,以避免
#重叠按钮;显然你可以把它们放在任何地方
#你想要的。
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()))
类StackedWidgetWithTriangles(QtWidgets.QStackedWidget):
定义i