PyQt5系列教程(51):QStackedWidget

PyQt5系列教程(51):QStackedWidget

上期我们一起学习了QTabWidget类。注意涉及到以下几个知识点:


1、心知天气API的使用

2、QTabWidget类的使用


今天我们一起来学习下QStackedWidget类,其实这与QTabWidget类一样,也是一个用有限的空间放入更多小部件的类。

总体介绍

QStackedWidget类提供了一堆小部件,其中一次只能看到一个小部件。

QStackedWidget可以用来创建一个类似于QTabWidget提供的用户界面。这是一个构建在QStackedLayout类之上的便利布局小部件。

像QStackedLayout一样,QStackedWidget可以被构建并填充一些子部件(“pages”):

firstPageWidget = QWidget()
secondPageWidget = QWidget()
thirdPageWidget = QWidget()
stackedWidget = QStackedWidget()
stackedWidget.addWidget(firstPageWidget)
stackedWidget.addWidget(secondPageWidget)
stackedWidget.addWidget(thirdPageWidget)
layout = QVBoxLayout()
layout.addWidget(stackedWidget)
self.setLayout(layout)

QStackedWidget不提供用户切换页面的内在手段。这通常是通过QComboBox或QListWidget完成的,例如:

pageComboBox = QComboBox()
pageComboBox.addItem("Page 1")
pageComboBox.addItem("Page 2")
pageComboBox.addItem("Page 3")
pageComboBox.activated[int].connect(stackedWidget.setCurrentIndex)

在填充堆叠小部件时,小部件将添加到内部列表中。indexOf()函数返回该列表中的小部件的索引。这些小部件可以使用addWidget()函数添加到列表的末尾,也可以使用insertWidget()函数插入到给定的索引处。

removeWidget()函数从堆叠的小部件中移除一个小部件。使用count()函数可以获得堆叠窗口小部件中包含的窗口小部件的数量。

widget()函数返回给定索引位置的小部件。屏幕上显示的小部件的索引由currentIndex()给出,并且可以使用setCurrentIndex()更改。以类似的方式,当前显示的小部件可以使用currentWidget()函数进行检索,并使用setCurrentWidget()函数进行更改。

每当堆叠窗口小部件中的当前窗口小部件发生更改或从堆叠窗口小部件中移除窗口小部件时,将分别发出currentChanged()和widgetRemoved()信号。

类归属

PyQt5->QtWidgets->QTabWidget

继承关系

PyQt5->QObject and QPaintDevice->QWidget->QFrame->QStackedWidget

更多详细的介绍,请参见官网:

小例子

本期我们的小例子是以模拟QQ秀为例,效果如下:

帅哥模式

https://www.zhihu.com/video/990281671173910528

美女模式

https://www.zhihu.com/video/990281182990319616

这个这个程序的原理非常的简单。下面我们简单谈谈思路。

实现思路

为了简化操作,我其实给每一个造型都做成了图片,以女生为例。性别选择 = 1张,头型选择 = 3张, 上衣选择 = 3* 3 张(因为每个头型要对应不同的衣服), 裤子选择 = 3*3*3张,一共 1 + 3 + 9 + 27 = 40。同理男生也是40张。

实际上QQ秀,我觉得应该不会这么去实现,毕竟我这里只是模拟而已。

利用QStackedWidget和QListWidget实现不同选择页面的切换,并传递相应的选择数据。最后就实现这个模拟QQ秀了。

图解如下:


核心源码

每个造型的选择是一个自定义QWidget类,这里我是用Qt设计师实现的。不做讲解了。大家自己看看就行了,也没什么好说的,拖拖就出来了。相关知识点:

这里把主程序说一下,以 进行到头型选择为例 ,衣服、裤子的选择实现都是一样,就不把源码放上来了。

class QQShow(QMainWindow, Ui_MainWindow):
    sexy = ""
    head = ""
    coat = ""
    pants = ""
    def __init__(self, parent=None):
        self.initUi()
    def initUi(self):
        self.sexywidget = SelectSexy()
        self.headwidget = SelectHead()
        self.stackedWidget.addWidget(self.sexywidget)
        self.stackedWidget.addWidget(self.headwidget)
        self.sexywidget.pushButton.clicked.connect(self.unlockhead)
        self.sexywidget.radioButton_man.toggled.connect(self.setman)
        self.sexywidget.radioButton_feman.toggled.connect(self.setfeman)
        self.headwidget.pushButton.clicked.connect(self.unlockcoat)
        self.headwidget.radioButton_head1.toggled.connect(self.sethead1)
        self.headwidget.radioButton_head2.toggled.connect(self.sethead2)
        self.headwidget.radioButton_head3.toggled.connect(self.sethead3)
    @pyqtSlot(int)
    def on_listWidget_currentRowChanged(self, p0):
        self.stackedWidget.setCurrentIndex(p0)
    @pyqtSlot(int)
    def on_stackedWidget_currentChanged(self, p0):
        if p0 == 1:
            if self.headwidget.radioButton_head1.isChecked():
                self.headwidget.label.setPixmap(QPixmap(self.sexy + self.head + ".png"))
            elif self.headwidget.radioButton_head2.isChecked():
                self.headwidget.label.setPixmap(QPixmap(self.sexy +  self.head + ".png"))
            elif self.headwidget.radioButton_head3.isChecked():
                self.headwidget.label.setPixmap(QPixmap(self.sexy +  self.head + ".png"))
            else:
                self.headwidget.label.setPixmap(QPixmap(self.sexy + "sexy.png"))
    def unlockhead(self):
        if self.sexywidget.radioButton_man.isChecked() or self.sexywidget.radioButton_feman.isChecked():
            QMessageBox.information(self, "提示", "头型解锁成功!")
            self.sexywidget.pushButton.setEnabled(False)
            item = self.listWidget.item(1)
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
        else:
            QMessageBox.information(self, "提示", "还没有选择男女呢?")    
    def setman(self):
        self.sexy = "./res/man/"
        self.sexywidget.label_sexy.setPixmap(QPixmap("./res/man/sexy.png"))
    def setfeman(self):
        self.sexy = "./res/feman/"
        self.sexywidget.label_sexy.setPixmap(QPixmap("./res/feman/sexy.png"))

以上只是进行到选择头型的代码。

sexy = ""
head = ""
coat = ""
pants = ""

这里使用4个变量,用于记录图片的路径信息。

self.sexywidget = SelectSexy()
self.headwidget = SelectHead()
self.stackedWidget.addWidget(self.sexywidget)
self.stackedWidget.addWidget(self.headwidget)

这里我们新建了性别、头型小部件(sexywidget、headwidget),然后添加到stackedWidget对象中。

self.sexywidget.pushButton.clicked.connect(self.unlockhead)
self.sexywidget.radioButton_man.toggled.connect(self.setman)
self.sexywidget.radioButton_feman.toggled.connect(self.setfeman)
self.headwidget.pushButton.clicked.connect(self.unlockcoat)
self.headwidget.radioButton_head1.toggled.connect(self.sethead1)
self.headwidget.radioButton_head2.toggled.connect(self.sethead2)
self.headwidget.radioButton_head3.toggled.connect(self.sethead3)

我们设定了在性别、头型选择页面中,点击不同的单选按钮以及解锁按钮时对应的槽函数。

性别页面→解锁→unlockhead()
性别页面→男→setman()
性别页面→女→setfeman()

头型页面→解锁→unlockcoat()
头型页面→头型1→sethead1()
头型页面→头型2→sethead2()
头型页面→头型3→sethead3()

def unlockhead(self):
    if self.sexywidget.radioButton_man.isChecked() or self.sexywidget.radioButton_feman.isChecked():
        QMessageBox.information(self, "提示", "头型解锁成功!")
        self.sexywidget.pushButton.setEnabled(False)
        item = self.listWidget.item(1)
        item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
    else:
        QMessageBox.information(self, "提示", "还没有选择男女呢?")

要是男、女都没有选择,你解锁的话,会提示不让你解锁的。当然要是选择了其中一个,就可以解锁了。

item = self.listWidget.item(1)
item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)

设定第1个列表选项(从0开始的),使其可以使用和被选择。最开始是不能的,这点是在Qt设计师里面设定的。如下图:


unlockcoat()的作用类似,不做重复介绍。

def setman(self):
    self.sexy = "./res/man/"
    self.sexywidget.label_sexy.setPixmap(QPixmap("./res/man/sexy.png"))
def setfeman(self):
    self.sexy = "./res/feman/"
    self.sexywidget.label_sexy.setPixmap(QPixmap("./res/feman/sexy.png"))

让self.sexy记录下当前的男女图片库,同时设定label显示男女照片。

@pyqtSlot(int)
def on_listWidget_currentRowChanged(self, p0):
    self.stackedWidget.setCurrentIndex(p0)

当我们选择的QListWidget项目发生变化的时候,就会产生currentRowChanged()信号,并把当前项目的索引号带上,然后我们设定stackedWidget当前显示的QWidget的索引是当前QListWidget项目的索引。也就是根据选择切换不同的页面。

@pyqtSlot(int)
def on_stackedWidget_currentChanged(self, p0):
    if p0 == 1:
        if self.headwidget.radioButton_head1.isChecked():
            self.headwidget.label.setPixmap(QPixmap(self.sexy + self.head + ".png"))