PyQt5通过堆叠布局实现选项卡(多界面)功能
1、创建一个MainWindow
加入Text Brower
做标题,几个按钮。
然后在左侧containers
中添加Stacked Widget
这个控件,初步布局如下:
- 对窗口中的堆叠容器 “Stacked Widget”,选中后可以用鼠标拖动、拉伸来调整控件的位置和大小,或者在 “属性编辑器” 中设置 (X, Y)、宽度、高度属性。
- 堆叠容器 “Stacked Widget” 自动建立了 2个页面。鼠标位于堆叠容器 “Stacked Widget” ,右键唤出下拉菜单,选择 "插入页"可以插入新的页面,选择 “改变页顺序” 可以调整各页面的顺序。
- 在控件的右上角显示有一对黑色三角符号,可以在多个页面之间切换,也可以在 “对象查看器” 中选择要编辑的页面。
2、创建页面
2.1 修改当前页名称
在右侧属性中修改当前页名称,把第一页修改为page_first
2.2 增加页
选中Stacked Widget
,右键插入页
->当前页之后
,增加两页,并命名为page_fun1
,page_fun2
第一页增加输入框:账号、密码:
第二页增加显示框:
第三页增加显示框:
3、把ui转成py
转化后代码:
# -*- coding: utf-8 -*-# Form implementation generated from reading ui file 'stacked.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_MainWindow(object):def setupUi(self, MainWindow):MainWindow.setObjectName("MainWindow")MainWindow.resize(800, 600)self.centralwidget = QtWidgets.QWidget(MainWindow)self.centralwidget.setObjectName("centralwidget")self.btn_first = QtWidgets.QPushButton(self.centralwidget)self.btn_first.setGeometry(QtCore.QRect(50, 100, 93, 28))self.btn_first.setObjectName("btn_first")self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)self.textBrowser.setGeometry(QtCore.QRect(45, 10, 711, 41))self.textBrowser.setObjectName("textBrowser")self.btn_fun1 = QtWidgets.QPushButton(self.centralwidget)self.btn_fun1.setGeometry(QtCore.QRect(170, 100, 93, 28))self.btn_fun1.setObjectName("btn_fun1")self.btn_fun2 = QtWidgets.QPushButton(self.centralwidget)self.btn_fun2.setGeometry(QtCore.QRect(280, 100, 93, 28))self.btn_fun2.setObjectName("btn_fun2")self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget)self.stackedWidget.setGeometry(QtCore.QRect(50, 160, 691, 271))self.stackedWidget.setObjectName("stackedWidget")self.page_first = QtWidgets.QWidget()self.page_first.setObjectName("page_first")self.label = QtWidgets.QLabel(self.page_first)self.label.setGeometry(QtCore.QRect(70, 50, 72, 21))self.label.setObjectName("label")self.label_2 = QtWidgets.QLabel(self.page_first)self.label_2.setGeometry(QtCore.QRect(70, 90, 72, 21))self.label_2.setObjectName("label_2")self.account_edit = QtWidgets.QLineEdit(self.page_first)self.account_edit.setGeometry(QtCore.QRect(130, 50, 113, 21))self.account_edit.setObjectName("account_edit")self.password_edit = QtWidgets.QLineEdit(self.page_first)self.password_edit.setGeometry(QtCore.QRect(130, 90, 113, 21))self.password_edit.setObjectName("password_edit")self.btn_login = QtWidgets.QPushButton(self.page_first)self.btn_login.setGeometry(QtCore.QRect(130, 140, 93, 28))self.btn_login.setObjectName("btn_login")self.btn_registere = QtWidgets.QPushButton(self.page_first)self.btn_registere.setGeometry(QtCore.QRect(250, 140, 93, 28))self.btn_registere.setObjectName("btn_registere")self.stackedWidget.addWidget(self.page_first)self.page_fun1 = QtWidgets.QWidget()self.page_fun1.setObjectName("page_fun1")self.label_3 = QtWidgets.QLabel(self.page_fun1)self.label_3.setGeometry(QtCore.QRect(60, 50, 72, 21))self.label_3.setObjectName("label_3")self.show_text_1 = QtWidgets.QTextBrowser(self.page_fun1)self.show_text_1.setGeometry(QtCore.QRect(170, 40, 256, 192))self.show_text_1.setObjectName("show_text_1")self.stackedWidget.addWidget(self.page_fun1)self.page_fun2 = QtWidgets.QWidget()self.page_fun2.setObjectName("page_fun2")self.label_4 = QtWidgets.QLabel(self.page_fun2)self.label_4.setGeometry(QtCore.QRect(60, 30, 72, 21))self.label_4.setObjectName("label_4")self.show_text_2 = QtWidgets.QTextBrowser(self.page_fun2)self.show_text_2.setGeometry(QtCore.QRect(130, 20, 256, 192))self.show_text_2.setObjectName("show_text_2")self.stackedWidget.addWidget(self.page_fun2)MainWindow.setCentralWidget(self.centralwidget)self.menubar = QtWidgets.QMenuBar(MainWindow)self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 27))self.menubar.setObjectName("menubar")MainWindow.setMenuBar(self.menubar)self.statusbar = QtWidgets.QStatusBar(MainWindow)self.statusbar.setObjectName("statusbar")MainWindow.setStatusBar(self.statusbar)self.retranslateUi(MainWindow)self.stackedWidget.setCurrentIndex(0)QtCore.QMetaObject.connectSlotsByName(MainWindow)def retranslateUi(self, MainWindow):_translate = QtCore.QCoreApplication.translateMainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))self.btn_first.setText(_translate("MainWindow", "首页"))self.textBrowser.setHtml(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p align=\"center\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:16pt; color:#0000ff;\">PyQt5通过堆叠布局实现选项卡(多界面)功能</span></p></body></html>"))self.btn_fun1.setText(_translate("MainWindow", "功能1"))self.btn_fun2.setText(_translate("MainWindow", "功能2"))self.label.setText(_translate("MainWindow", "账号"))self.label_2.setText(_translate("MainWindow", "密码"))self.btn_login.setText(_translate("MainWindow", "登录"))self.btn_registere.setText(_translate("MainWindow", "注册"))self.label_3.setText(_translate("MainWindow", "功能1"))self.label_4.setText(_translate("MainWindow", "功能2"))
4、QStackedWidget 类说明
QStackedWidget 类继承自 QFrame类。
QStackedWidge t类提供了多页面切换的布局,一次只能看到一个界面。
QStackedWidget 类的信号:
currentChanged(int index):当前页面发生变化时候发射,index 为新的索引值。
widgetRemoved(int index):页面被移除时候发射,index 为页面对应的索引值。
QStackedWidget 类的槽函数:
setCurrentIndex(int index):设置索引 index 所在的页面为当前页面。setCurrentWidget(QWidget *widget):设置QWidget页面为当前页面。
使用如下程序可以设置 page_0 为当前显示的页面:
pageNo = 0 # 设置 page_0 为索引页(第一页面)
self.stackedWidget.setCurrentIndex(pageNo) # 设置使用 pageNo=0 作为当前显示页面
需要注意的是,不论我们为每个页面控件设置的名称(objectName)是什么,在 QStackedWidget 类中定义的页面索引 index 都是一个从 0 开始计数,即:第一页面的索引值 index=0,第二页面的索引值 index=1,…。
5、通过按钮来实现界面切换
示例代码:
# -*- coding: utf-8 -*-"""
@contact: 微信 1257309054
@file: main.py
@time: 2023/9/13 15:09
@author: LDC
"""
import datetime
import json
import sys
import timefrom PyQt5 import QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal, QMutexfrom PyQt5 import QtCorefrom stacked import Ui_MainWindowclass StackedDemo(QtWidgets.QMainWindow, Ui_MainWindow):def __init__(self):super(StackedDemo, self).__init__()self.setupUi(self) # 创建窗体对象self.init()def init(self):'''初始化:return:'''self.is_login = False # 是否登录self.is_register = False # 是否注册self.show_text_1_list = [] # 功能1显示内容列表self.show_text_2_list = [] # 功能1显示内容列表self.login_thread = LoginThread(self) # 开启多线程处理登录数据# 关联按钮事件self.btn_first.clicked.connect(self.btn_first_clicked) # 首页按钮点击事件self.btn_fun1.clicked.connect(self.btn_fun1_clicked) # 功能1按钮点击事件self.btn_fun2.clicked.connect(self.btn_fun2_clicked) # 功能2按钮点击事件self.btn_login.clicked.connect(self.btn_login_clicked) # 登录按钮点击事件self.btn_registere.clicked.connect(self.btn_register_clicked) # 注册按钮点击事件self.login_thread._signal_login.connect(self.threading_slot) # 绑定线程回调处理函数def btn_first_clicked(self):# 首页self.stackedWidget.setCurrentIndex(0)def btn_fun1_clicked(self):# 功能1self.stackedWidget.setCurrentIndex(1)def btn_fun2_clicked(self):# 功能2self.stackedWidget.setCurrentIndex(2)def btn_login_clicked(self):# 登录self.stackedWidget.setCurrentIndex(1) # 切换界面if not self.is_login:self.show_text_1_list.append('功能1界面--登录')self.login_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')self.show_text_1_list.append('登录时间:{}'.format(self.login_time))self.is_login = True # 修改登录标志if self.is_login:self.login_thread.start()def btn_register_clicked(self):# 注册self.stackedWidget.setCurrentIndex(2)self.show_text_2.setText('功能2界面--注册')def threading_slot(self, data):'''线程回调函数:param data::return:'''data = json.loads(data)if 'update_login_time' in data:if len(self.show_text_1_list) > 6:self.show_text_1_list.pop(0)now_time = data['update_login_time']time1 = datetime.datetime.strptime(self.login_time, '%Y-%m-%d %H:%M:%S')time2 = datetime.datetime.strptime(now_time, '%Y-%m-%d %H:%M:%S')diff = time2 - time1days = diff.daysseconds = diff.secondsself.show_text_1_list.append('\r\n{},已登录{}秒'.format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), seconds))info_msg = ''for stl in self.show_text_1_list:info_msg += stlself.show_text_1.setText(info_msg)# 登录后处理
class LoginThread(QThread):# 登录多线程_signal_login = pyqtSignal(str)def __init__(self, parent=None):super(LoginThread, self).__init__(parent)self.qmut = QMutex()self.window = parentself.is_exit = Falsedef run(self):while 1:self.qmut.lock()if self.is_exit:breakself.qmut.unlock()now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')self._signal_login.emit(json.dumps({'update_login_time': now_time})) # 发送信号给槽函数time.sleep(5)if __name__ == '__main__':QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) # 自适应分辨率app = QtWidgets.QApplication(sys.argv)ui = StackedDemo()ui.show()sys.exit(app.exec_())
通过线程来演示切换界面时,之前的界面功能不受影响。
6、效果
登录界面:
注册界面: