小工具知识网

当前位置: 首页 > 行业知识 > PyQt5保姆级教程-- 从入门到精通

行业知识

PyQt5保姆级教程-- 从入门到精通

2026-10-21 07:10:09 来源:互联网转载

PyQt5保姆级教程-- 从入门到精通

主要内容:

1Qt Designer
2PyQt5基本窗口控件(QMainWindow、Qwidget、Qlabel、QLineEdit、菜单、工具栏等)
3PyQt5高级组件(QTableView、QListView、容器、线程等)
4PyQt5布局管理(QBoxLayout、QGirdLayout、QFormLayout、嵌套布局等)
5PyQt5信号与槽(事件处理、数据传递等)
6PyQt5图形与特效(定制窗口风格、绘图、QSS与UI美化、不规则窗口、设置样式等)
7PyQt5扩展应用(制作PyQt5安装程序、数据处理、第三方绘图库在PyQt5中的应用、UI自动化测试等)

���建PyQt5开发环境

工具:

Python

PyQt5模块

PyCharm

在PyCharm里面安装PyQt5

pip install PyQt5 -i https://pypi.douban.com/simple

在PyCharm里面安装Qt的工具包

pip install PyQt5-tools -i https://pypi.douban.com/simple

在安装tools时,报如下错误:

1.pip install PyQt5-tools安装失败
WARNING: Ignoring invalid distribution -yqt5 (e:\venvs\pyqt5_demo1\lib\site-packages)Installing collected packages: pyqt5, click, qt5-tools, pyqt5-plugins, pyqt5-toolsERROR: Could not install packages due to an OSError: [WinError 5] 拒绝访问。: 'e:\venvs\pyqt5_demo1\Lib\site-packages\PyQt5\QtCore.pyd'Check the permissions.

解决办法:

第一步:

将pip install ...加入--user为pip install --user ...即可

pip install PyQt5-tools  -i https://pypi.douban.com/simple   --user

换个思路

重启电脑,继续输入第一条命令安装

原因分析,可能占用了进程。

2.配置Qt Designer
Working directory:$FileDir$
3.配置PyUIC
Program:python的安装目录下的python.exe文件Arguments:-m PyQt5.uic.pyuic  $FileName$ -o $FileNameWithoutExtension$.py
4.配置Pyrcc
Program:python的安装目录下的Scripts文件夹的pyrcc5.exe文件Arguments:$FileName$ -o $FileNameWithoutExtension$_rc.py

展示效果如下:

5.ui转py的过程:

1.点击EXternal Tools里面的QTDesigner,会跳转到QT界面,

拖动组件,调整好界面,保存为first文件,它会默认生成first.ui文件

选中文件,鼠标右击,打开扩展,选择PyUIC,它会生成.py文件

将.ui文件转化为.py文件的命令行方法:

python -m PyQt5.uic.pyuic demo.ui -o demo.py
6.开发第一个基于PyQt5的桌面应用

必须使用两个类: QApplication和QWidget。都在PyQt5.QtWidgets。

第一个类表示应用程序,第二个类表示窗口

输入如下代码:

# 开发第一个基于PyQt5的桌面应用import sysfrom PyQt5.QtWidgets import QApplication,QWidgetif __name__ == '__main__':    # 创建QApplication类的实例    app = QApplication(sys.argv)    # 创建一个窗口    w = QWidget()    # 设置窗口尺寸   宽度300,高度150    w.resize(400,200)    # 移动窗口    w.move(300,300)    # 设置窗口的标题    w.setWindowTitle('第一个基于PyQt5的桌面应用')    # 显示窗口    w.show()    # 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)    sys.exit(app.exec_())

效果如下:

也可以在命令行运行

python 文件名.py
7.基本操作

左侧是可以选择的组件,右侧可以设定属性值,设置完成之后,可以在窗体选择预览,选择查看c++和python代码。

8.在QtDesigner中使用水平布局(Vertical Layout)

两种方式:

(1)先移组件,再布局。

放置五个按钮,让这五个按钮等宽的,水平的排列

(全部选中–>鼠标右键–>布局–>水平布局 预览)

预览:

(2)先布局,再移组件。

生成demo1.ui文件

转成demo2.py文件,转成py文件,才能在程序里面调。

生成的代码如下:

# -*- coding: utf-8 -*-# Form implementation generated from reading ui file 'demo1.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.widget = QtWidgets.QWidget(self.centralwidget)        self.widget.setGeometry(QtCore.QRect(70, 50, 651, 51))        self.widget.setObjectName("widget")        self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget)        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)        self.horizontalLayout.setObjectName("horizontalLayout")        self.pushButton = QtWidgets.QPushButton(self.widget)        self.pushButton.setObjectName("pushButton")        self.horizontalLayout.addWidget(self.pushButton)        self.pushButton_2 = QtWidgets.QPushButton(self.widget)        self.pushButton_2.setObjectName("pushButton_2")        self.horizontalLayout.addWidget(self.pushButton_2)        self.pushButton_3 = QtWidgets.QPushButton(self.widget)        self.pushButton_3.setObjectName("pushButton_3")        self.horizontalLayout.addWidget(self.pushButton_3)        self.pushButton_4 = QtWidgets.QPushButton(self.widget)        self.pushButton_4.setObjectName("pushButton_4")        self.horizontalLayout.addWidget(self.pushButton_4)        self.widget1 = QtWidgets.QWidget(self.centralwidget)        self.widget1.setGeometry(QtCore.QRect(110, 160, 578, 194))        self.widget1.setObjectName("widget1")        self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.widget1)        self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)        self.horizontalLayout_2.setObjectName("horizontalLayout_2")        self.listView = QtWidgets.QListView(self.widget1)        self.listView.setObjectName("listView")        self.horizontalLayout_2.addWidget(self.listView)        self.pushButton_5 = QtWidgets.QPushButton(self.widget1)        self.pushButton_5.setObjectName("pushButton_5")        self.horizontalLayout_2.addWidget(self.pushButton_5)        self.checkBox = QtWidgets.QCheckBox(self.widget1)        self.checkBox.setObjectName("checkBox")        self.horizontalLayout_2.addWidget(self.checkBox)        self.radioButton = QtWidgets.QRadioButton(self.widget1)        self.radioButton.setObjectName("radioButton")        self.horizontalLayout_2.addWidget(self.radioButton)        MainWindow.setCentralWidget(self.centralwidget)        self.menubar = QtWidgets.QMenuBar(MainWindow)        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 26))        self.menubar.setObjectName("menubar")        MainWindow.setMenuBar(self.menubar)        self.statusbar = QtWidgets.QStatusBar(MainWindow)        self.statusbar.setObjectName("statusbar")        MainWindow.setStatusBar(self.statusbar)        self.retranslateUi(MainWindow)        QtCore.QMetaObject.connectSlotsByName(MainWindow)    def retranslateUi(self, MainWindow):        _translate = QtCore.QCoreApplication.translate        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))        self.pushButton.setText(_translate("MainWindow", "Button1"))        self.pushButton_2.setText(_translate("MainWindow", "Button2"))        self.pushButton_3.setText(_translate("MainWindow", "Button3"))        self.pushButton_4.setText(_translate("MainWindow", "Button4"))        self.pushButton_5.setText(_translate("MainWindow", "PushButton"))        self.checkBox.setText(_translate("MainWindow", "CheckBox"))        self.radioButton.setText(_translate("MainWindow", "RadioButton"))

如何在程序里面调用,先新建一个Run_demo1.py文件

代码如下:

import sysimport demo1from PyQt5.QtWidgets import QApplication,QMainWindowif __name__ == '__main__':    # 只有直接运行这个脚本,才会往下执行    # 别的脚本文件执行,不会调用这个条件句    # 实例化,传参    app = QApplication(sys.argv)    # 创建对象    mainWindow = QMainWindow()    # 创建ui,引用demo1文件中的Ui_MainWindow类    ui = demo1.Ui_MainWindow()    # 调用Ui_MainWindow类的setupUi,创建初始组件    ui.setupUi(mainWindow)    # 创建窗口    mainWindow.show()    # 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)    sys.exit(app.exec_())

运行:

弹出如下窗口:

此时出现了一个小问题:

如何解决: (将此目录生成源代码目录)

设置完成之后,等待加载完成,导入文件名底下的红线消失

9.在QtDesigner中使用垂直布局(Horizontal Layout)

和水平布局的操作类似,也有两种布局方式:

(1) 先移动组件,再布局
(2) 先布局,再移动组件

点击保存,生成垂直布局文件demo2.ui

右键demo2.ui,生成demo2.py文件

demo2.py的代码如下:

# -*- coding: utf-8 -*-# Form implementation generated from reading ui file 'demo2.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.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)        self.verticalLayoutWidget.setGeometry(QtCore.QRect(180, 150, 441, 371))        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)        self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)        self.verticalLayout_2.setObjectName("verticalLayout_2")        self.label = QtWidgets.QLabel(self.verticalLayoutWidget)        self.label.setObjectName("label")        self.verticalLayout_2.addWidget(self.label)        self.pushButton_6 = QtWidgets.QPushButton(self.verticalLayoutWidget)        self.pushButton_6.setObjectName("pushButton_6")        self.verticalLayout_2.addWidget(self.pushButton_6)        self.pushButton_5 = QtWidgets.QPushButton(self.verticalLayoutWidget)        self.pushButton_5.setObjectName("pushButton_5")        self.verticalLayout_2.addWidget(self.pushButton_5)        self.pushButton_4 = QtWidgets.QPushButton(self.verticalLayoutWidget)        self.pushButton_4.setObjectName("pushButton_4")        self.verticalLayout_2.addWidget(self.pushButton_4)        self.checkBox = QtWidgets.QCheckBox(self.verticalLayoutWidget)        self.checkBox.setObjectName("checkBox")        self.verticalLayout_2.addWidget(self.checkBox)        self.widget = QtWidgets.QWidget(self.centralwidget)        self.widget.setGeometry(QtCore.QRect(40, 40, 95, 121))        self.widget.setObjectName("widget")        self.verticalLayout = QtWidgets.QVBoxLayout(self.widget)        self.verticalLayout.setContentsMargins(0, 0, 0, 0)        self.verticalLayout.setObjectName("verticalLayout")        self.pushButton = QtWidgets.QPushButton(self.widget)        self.pushButton.setObjectName("pushButton")        self.verticalLayout.addWidget(self.pushButton)        self.pushButton_2 = QtWidgets.QPushButton(self.widget)        self.pushButton_2.setObjectName("pushButton_2")        self.verticalLayout.addWidget(self.pushButton_2)        self.pushButton_3 = QtWidgets.QPushButton(self.widget)        self.pushButton_3.setObjectName("pushButton_3")        self.verticalLayout.addWidget(self.pushButton_3)        MainWindow.setCentralWidget(self.centralwidget)        self.menubar = QtWidgets.QMenuBar(MainWindow)        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 26))        self.menubar.setObjectName("menubar")        MainWindow.setMenuBar(self.menubar)        self.statusbar = QtWidgets.QStatusBar(MainWindow)        self.statusbar.setObjectName("statusbar")        MainWindow.setStatusBar(self.statusbar)        self.retranslateUi(MainWindow)        QtCore.QMetaObject.connectSlotsByName(MainWindow)    def retranslateUi(self, MainWindow):        _translate = QtCore.QCoreApplication.translate        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))        self.label.setText(_translate("MainWindow", "TextLabel"))        self.pushButton_6.setText(_translate("MainWindow", "PushButton"))        self.pushButton_5.setText(_translate("MainWindow", "PushButton"))        self.pushButton_4.setText(_translate("MainWindow", "PushButton"))        self.checkBox.setText(_translate("MainWindow", "CheckBox"))        self.pushButton.setText(_translate("MainWindow", "PushButton"))        self.pushButton_2.setText(_translate("MainWindow", "PushButton"))        self.pushButton_3.setText(_translate("MainWindow", "PushButton"))

在python程序里面调用,新建Run_demo2.py文件

代码如下:

import sysimport demo2from PyQt5.QtWidgets import QApplication,QMainWindowif __name__ == '__main__':    # 只有直接运行这个脚本,才会往下执行    # 别的脚本文件执行,不会调用这个条件句    # 实例化,传参    app = QApplication(sys.argv)    # 创建对象    mainWindow = QMainWindow()    # 创建ui,引用demo1文件中的Ui_MainWindow类    ui = demo2.Ui_MainWindow()    # 调用Ui_MainWindow类的setupUi,创建初始组件    ui.setupUi(mainWindow)    # 创建窗口    mainWindow.show()    # 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)    sys.exit(app.exec_())

运行程序:

10.在QtDesigner里面同时创建垂直布局和水平布局

新建一个 main window,点击 创建

在布局的时候,windows里面,可以通过ctrl+上下左右进行微调。

文件保存,命名为demo3.ui文件,同样用拓展工具,生成demo3.py文件。

同样,新建运行文件Run_demo3.py文件,代码如下:

import sysimport demo3from PyQt5.QtWidgets import QApplication,QMainWindowif __name__ == '__main__':    # 只有直接运行这个脚本,才会往下执行    # 别的脚本文件执行,不会调用这个条件句    # 实例化,传参    app = QApplication(sys.argv)    # 创建对象    mainWindow = QMainWindow()    # 创建ui,引用demo1文件中的Ui_MainWindow类    ui = demo3.Ui_MainWindow()    # 调用Ui_MainWindow类的setupUi,创建初始组件    ui.setupUi(mainWindow)    # 创建窗口    mainWindow.show()    # 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)    sys.exit(app.exec_())

运行代码,结果如下:

11.在QtDesigner中同时使用栅格布局(Grid Layout)

拖放四个按钮之后,选中栅格布局

选中之后,效果如下:

拖拽边角,可以放大:

练习:利用栅格布局实现计算器数字区域

拖动button键调整好位置,全选之后选中布局,再选栅格布局

点击栅格之后,效果如下:

保存,生成demo4.ui文件

同样进行上述操作,转成demo4.py文件,新建Run_demo4.py文件,代码如下:

import sysimport demo4from PyQt5.QtWidgets import QApplication,QMainWindowif __name__ == '__main__':    # 只有直接运行这个脚本,才会往下执行    # 别的脚本文件执行,不会调用这个条件句    # 实例化,传参    app = QApplication(sys.argv)    # 创建对象    mainWindow = QMainWindow()    # 创建ui,引用demo1文件中的Ui_MainWindow类    ui = demo4.Ui_MainWindow()    # 调用Ui_MainWindow类的setupUi,创建初始组件    ui.setupUi(mainWindow)    # 创建窗口    mainWindow.show()    # 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)    sys.exit(app.exec_())

效果如下:

栅格布局的注意点:摆放控件要尽可能的整齐,这样系统才会正确的识别。

栅格布局和水平布局,垂直布局一样,可以后期添加控件。

12.向栅格布局中拖动控件
13.在QtDesigner中使用表单布局(Form Layout)

新建一个 main window,点击 创建

选择需要的控件,进行如下操作:

调整好布局,保存文件为demo5.ui

利用pyUIC插件,生成python代码调试

创建Run_demo5.py文件,执行代码如下:

import sysimport demo5from PyQt5.QtWidgets import QApplication,QMainWindowif __name__ == '__main__':    # 只有直接运行这个脚本,才会往下执行    # 别的脚本文件执行,不会调用这个条件句    # 实例化,传参    app = QApplication(sys.argv)    # 创建对象    mainWindow = QMainWindow()    # 创建ui,引用demo1文件中的Ui_MainWindow类    ui = demo5.Ui_MainWindow()    # 调用Ui_MainWindow类的setupUi,创建初始组件    ui.setupUi(mainWindow)    # 创建窗口    mainWindow.show()    # 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)    sys.exit(app.exec_())

效果如下:

14.在容器中完成布局

跟上面一样,新建一个MainWindow,添加对应的组件,鼠标右键点击,变形为对应的容器。

同理,生成demo6.py文件,新建Run_demo6文档,添加代码

import sysimport demo6from PyQt5.QtWidgets import QApplication,QMainWindowif __name__ == '__main__':    # 只有直接运行这个脚本,才会往下执行    # 别的脚本文件执行,不会调用这个条件句    # 实例化,传参    app = QApplication(sys.argv)    # 创建对象    mainWindow = QMainWindow()    # 创建ui,引用demo1文件中的Ui_MainWindow类    ui = demo6.Ui_MainWindow()    # 调用Ui_MainWindow类的setupUi,创建初始组件    ui.setupUi(mainWindow)    # 创建窗口    mainWindow.show()    # 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)    sys.exit(app.exec_())

运行程序

15.在QtDesigner中使用绝对布局

跟上面一样,新建一个MainWindow,添加对应的组件,

同理,生成demo7.py文件,新建Run_demo7文档,添加代码(代码如上,略作修改),PyCharm里运行如下:

15.在QtDesigner中使用分割线与间隔

跟上面一样,新建一个MainWindow,

新建4个按钮,设置水平间隔,新建3个按钮,设置垂直间隔

在A2和A3之间设立分割线,在B2和B3之间设立分割线

保存文件为demo8.ui ,转为demo8.py代码,新建Run_demo8.py,添加代码(代码如上,略作修改),PyCharm里运行如下:

16.布局的最大尺寸和最小尺寸

默认状态下,它的尺寸可以自由调节,跟上面一样,新建一个MainWindow,

可以看到,原本这个bushButton控件最小宽高可以到0,最大宽高可以到1677215

通过改变右侧栏的值,就可以设置它不小于多少,不大于多少。

16.尺寸策略(sizePolicy)

对于大多数控件来说,sizeHint(期望尺寸)是只可读的,也就是说,你布局的时候不管拖拽多大,最后输出的还是默认值

读取pushButton的期望尺寸:

self.pushButton.sizeHint().width()self.pushButton.sizeHint().height()

即可以看到,一个pushButton的期望尺寸,宽是41,高度是28

在demo7里进行上面操作,还是得到一样的数值。

同理,也可以进行读取其他控件的操作,比如读取textBrowser的宽高,代码如下:

self.c.sizeHint().width()self.textBrowser.sizeHint().height()

效果如下:

即可以看到:控件textBrowser的默认宽高分别为256和192。

同样,也可以看最小的期望尺寸,以pushButton为例,其代码如下:

self.pushButton.minimumSizeHint().width()self.pushButton.minimumSizeHint().height()

还是以demo7.py测试

可以看到,对于大部分控件来说,它的期望尺寸和最小期望尺寸是一样的

为何使用尺寸策略:

就是因为拓展的组件无论如何拖拽大小,经过布局设置之后,会回到默认的大小,为了让布局更有个性,采用尺寸策略,可以改变组件的期望尺寸。如图:

练习,左侧放树,构造分栏效果

保存文档为demo9.ui,转为demo9.py代码,新建Run_demo9.py,添加代码(代码如上,略作修改),PyCharm里运行如下:

17.在QtDesigner里面设置控件之间的伙伴关系

即控件关联之后,可以通过一个控件来控制另外一个控件

新建一个MainWindow,布局如下,保存为demo10.ui

接着:

如上图所示,水平布局后面的“H”为热键 ,Ctrl +1 为快捷键

​ 垂直布局后面的“V”为热键 ,Ctrl +2 为快捷键

热键:只有在这个菜单显示时,按“H”时,才会起作用。菜单关闭时,按“H”不起作用。“V”同理。

然后给demo10.ui文件的lable添加热键:

给姓名添加热键成:姓名(&A):

给年龄添加热键成:年龄(&B):

给邮箱添加热键成:邮箱(&C):

然后打开编辑伙伴,按住鼠标左键,选中lable指向Line Edit

然后选择编辑窗口部件,会切回到正常的部件

保存文件为demo10.ui ,转为demo10.py代码,新建Run_demo10.py,添加代码(代码如上,略作修改),PyCharm里运行如下:

再添加三个lable试一下效果,删除掉原来的demo10.py文件,用新保存的demo10.ui去生成新的demo10.py文件,点击运行查看效果

果然,按住alt+a,alt+b,alt+c,alt+d,alt+e.alt+f分别在右边对应的的Line Edit里面有焦点光标。

拓展:

18.如何修改控件的Tab顺序

新建一个MainWindow,布局如下,在Edit里面,选中 编辑 Tab顺序,在控件前端就会出现序号,这个序号就是顺序

也可以选中从这里开始或者重新开始,依次点击形成顺序。

接下来选中统一的 Line Edit来演示

保存文档为demo11.ui,转为demo11.py代码,新建Run_demo11.py,添加代码(代码如上,略作修改),PyCharm里运行如下:

按Tab 键,焦点光标就会按照指定的顺序在Line Edit内跳转

19.在QtDesigner中完成信号与槽的设置

信号(signal)与槽(slot)是Qt的核心机制,由于PyQt忠实的继承了Qt的所有特性,所有信号与槽也是PyQt的核心机制。

信号:是由对象或控件发射出去的消息。可以理解为按钮的单击事件。

当单击按钮时,按钮就会向外部发送单击的消息,这些发送出去的信号需要一些代码来拦截,这些代码就是槽

槽本质上是一个函数或者方法,信号可以理解为事件,槽可以理解为事件函数

信号与槽的设置:就是需要将信号和槽绑定

一个信号可以和多个槽绑定,一个槽可以拦截多个信号。

信号和槽绑定有两种方式,一种是用QtDesigner进行绑定,一种是在代码中进行绑定

(1)用QtDesigner进行信号和槽绑定

需求:单机按钮,关闭窗口

新建一个MainWindow,拖拽一个pashButton按钮,修改文本为“关闭窗口”

在Edit里选择编辑信号/槽,点击pashButton按钮向外拖拽,

在Edit里选择编辑信号/槽,点击pashButton按钮向外拖拽,松开鼠标,左栏选择clicked(),勾选左下角“显示从QWidget”继承信号和槽,右栏选择close(),然后点击右下角ok.

在Edit里面选择“编辑窗口部件”,对布局页面进行恢复。

在“窗体”里选择“预览于”,选择“Windows风格”

此时点击页面的“关闭窗口”,则页面就会关闭

练习

添加两个CheckBox进行如下操作:

给第一个CheckBox进行信号与槽的绑定

给第二个CheckBox进行信号与槽的绑定

在Edit里面选择"编辑窗口部件"进行恢复主窗口

恢复完成后,窗口如下:

预览效果如下:

取消勾选check box的选项,下面的line Edit视角效果也会相应的变换。

保存文档为demo12.ui,转为demo12.py代码,新建Run_demo12.py,添加代码(代码如上,略作修改),PyCharm里运行如下:

拓展:

(2)用代码完成关闭主窗口

​ 见第23节

20 在QtDesigner中为窗口添加菜单和工具栏

一个窗口,应该拥有菜单栏,工具栏,状态栏

新建一个MainWindow,添加菜单栏,添加完成之后,也可以右键点击,选择移除,同理添加工具栏。

给菜单栏添加内容:

在”窗体“里选择预览,效果如下:

在菜单和工具条里面如何添加按钮?

不管是菜单还是工具条的按钮,是一个action的动作,添加步骤如下:

在"视图"里,选择动作编辑器

两个菜单自动生成两个动作,双击可用设置动作

点击"ok"之后,主窗口变化如下:

保存文档为demo13.ui,转为demo13.py代码,新建Run_demo13.py,添加代码(代码如上,略作修改),PyCharm里运行如下:

21 创建主窗口

主窗口类型,有三种窗口

窗口类型说明
QMainWindow可以包含菜单栏、工具栏、状态栏和标题栏,是最常见的窗口形式
QDialog是对话窗口的基类。没有菜单栏、工具栏、状态栏
QWidget不确定窗口的用途,就使用QWidget

如下图所示,新建一个controls文件夹,在controls里面新建images文件夹用来装图片,在controls里面新建

FirstMainWin.py文件。

在FirstMainWin.py文件中,添加代码如下:

# 第一个主窗口# 把所有和UI有关的代码都放在一个类里面,创建窗口只要创建类的实例就可以了import sys# 从PyQt里面创建窗口和应用from PyQt5.QtWidgets import QMainWindow,QApplication# 用来添加图标from PyQt5.QtGui import QIcon# 定义一个类,这个类从QMainWindow里面继承class FristMainWin(QMainWindow):    # 初始化    def __init__(self,parent=None):        super(FristMainWin,self).__init__(parent)        # 设置主窗口的标题        self.setWindowTitle('第一个主窗口应用')        # 设置窗口的尺寸        self.resize(400,300)        # 获得状态栏        self.status = self.statusBar()        # 在状态栏上,设置消息的状态时间5000ms        self.status.showMessage('只存在5秒的消息',5000)    # 防止别的脚本调用,只有自己单独运行,才会调用下面代码if __name__ == '__main__':    # 创建app实例,并传入参数    app =  QApplication(sys.argv)    # 设置图标    app.setWindowIcon(QIcon('images/horse.jpg'))    # 创建对象    main = FristMainWin()    # 创建窗口    main.show()    # 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)    sys.exit(app.exec_())

运行代码,效果如下:

五秒之后,页面效果如下:

22.让主窗口居中显示

计算窗口的左上角的坐标

移动左上角的坐标,带动整个窗口的移动。

左上角的横坐标就是窗口的左边距,左上角的纵坐标就是窗口的上边距到顶部的值

新建CenterForm.py文件,执行代码:

# 让主窗口居中显示# 通过QDesktopWidget类相应的API可以得到整个屏幕的尺寸# 通过move方法移动窗口import sys# 从PyQt里面创建窗口和应用from PyQt5.QtWidgets import QDesktopWidget,QMainWindow,QApplication# 用来添加图标from PyQt5.QtGui import QIcon# 定义一个类,这个类从QMainWindow里面继承class CenterForm(QMainWindow):    # 初始化    def __init__(self,parent=None):        super(CenterForm,self).__init__(parent)        # 设置主窗口的标题        self.setWindowTitle('让窗口居中')        # 设置窗口的尺寸        self.resize(400,300)        # 添加center方法,作用就是让窗口居中        def center(self):            # 创建实例,获得屏幕对象,得到屏幕的坐标系            screen = QDesktopWidget().screenGeometry()            # 得到窗口的坐标系            size = self.geometry()            # 获取屏幕的宽度、高度            # 窗口左边缘的坐标等于(屏幕的宽度-窗口的宽度)/2            newLeft = (screen.width()-size.width()) / 2            # 屏幕上边缘的坐标等于(屏幕的高度-窗口的高度) / 2            newTop = (screen.height() - size.height()) / 2            # 移动窗口            self.move(newLeft,newTop)        #  获得状态栏        # self.status = self.statusBar()        #        # # 在状态栏上,设置消息的状态时间5000ms        # self.status.showMessage('只存在5秒的消息',5000)    # 防止别的脚本调用,只有自己单独运行,才会调用下面代码if __name__ == '__main__':    # 创建app实例,并传入参数    app =  QApplication(sys.argv)    # 设置图标    # app.setWindowIcon(QIcon('images/001.jpg'))    # 创建对象    main =CenterForm()    # 创建窗口    main.show()    # 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)    sys.exit(app.exec_())

效果展示:

23.如何退出应用程序

可以通过关闭主窗口,由于整个程序只有一个窗口,关闭主窗口之后,应用程序就会退出,之前在第19节演示过

换个思路,通过代码,在窗口上添加一个pashButton,调用QApplication里面的click()方法,来实现退出应用程序,关闭所有窗口

在controls文件夹里,新建QuitApplication.py文件,添加下列代码

# 退出应用程序# 用到了水平布局,引入QHBoxLayout# 需要一个控件,引入了QWidget# 需要butoon,引入了QPushButtonimport sysfrom PyQt5.QtWidgets import QHBoxLayout,QMainWindow,QApplication,QPushButton,QWidget# 用来添加图标from PyQt5.QtGui import QIconclass QuitApplication(QMainWindow):    # 初始化    def __init__(self):        super(QuitApplication,self).__init__()        # 设计窗口的尺寸        self.resize(300,120)        # 设置主窗口的标题        self.setWindowTitle('退出应用程序')        # 添加Button        # 创建全局对象self.button1        self.button1 = QPushButton('退出应用程序')        # 发送单击信号,执行对应的方法  (将信息与槽关联)        self.button1.clicked.connect(self.onClick_Button)        # 创建水平布局        layout = QHBoxLayout()        # 将组件加到水平局部里面        layout.addWidget(self.button1)        # 放置一个主框架        mainFrame = QWidget()        # 在主框架内添加水平布局        mainFrame.setLayout(layout)        # 把主框架放在窗口上        self.setCentralWidget(mainFrame)    # 按钮的单击事件的方法(自定义的槽)    def onClick_Button(self):        sender = self.sender()        print(sender.text() + '按钮被按下')        # 得到实例        app = QApplication.instance()        # 退出应用程序        app.quit()    # 防止别的脚本调用,只有自己单独运行,才会调用下面代码if __name__ == '__main__':    # 创建app实例,并传入参数    app =  QApplication(sys.argv)    # 设置图标    app.setWindowIcon(QIcon('images/001.jpg'))    # 创建对象    main = QuitApplication()    # 创建窗口    main.show()    # 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)    sys.exit(app.exec_())

运行代码,效果如下:

点击“退出应用程序”

24.屏幕坐标系

在controls文件夹里,新建ScreenGeometry.py文件,添加下列代码

# 屏幕坐标系# 它是以屏幕左上角为原点,划分的坐标系# 下面演示用面向过程的方式进行演示  (面向对象的方式需要创建类,创建方法)import sysfrom PyQt5.QtWidgets import QHBoxLayout,QMainWindow,QApplication,QPushButton,QWidget# 创建实例app = QApplication(sys.argv)# 创建窗口widget = QWidget()# 在窗口里放buttonbtn = QPushButton(widget)# 在button里面更改文本btn.setText("按钮")# 添加点击事件,让鼠标点击button后,打印出“onclick”def onClick_Button():    print("第一种方式获取各个值")    # 窗口离屏幕原点到y轴的距离    print("widget.x() = %d" % widget.x())   # 600 (以屏幕为原点的窗口横坐标)    # 窗口离屏幕原点到x轴的距离    print("widget.y() = %d" % widget.y())    # 200  (以屏幕为原点的窗口纵坐标,不包含标题栏)    # 窗口本身的宽度    print("widget.width()=%d" % widget.width())    # 300 (窗口宽度)    # 窗口本身的高度    print("widget.height()= %d" % widget.height())    # 240  (工作区高度)    print("第二种方式获取各个值")    # 窗口离屏幕原点到y轴的距离    print("widget.geometry().x() = %d" % widget.geometry().x())  # 601   (以屏幕为原点的窗口横坐标)    # 窗口离屏幕原点到x轴的距离    print("widget.geometry().y() = %d" % widget.geometry().y())  # 238    (以屏幕为原点的窗口纵坐标,包含标题栏)    # 窗口本身的宽度    print("widget.geometry().width()=%d" % widget.geometry().width())  # 300    (窗口宽度)    # 窗口本身的高度    print("widget.geometry().height()= %d" % widget.geometry().height())  # 240    (工作区高度)    print("第三种方式获取各个值")    # 窗口离屏幕原点到y轴的距离    print("widget.frameGeometry().x() = %d" % widget.frameGeometry().x())    # 600  (以屏幕为原点的窗口横坐标)    # 窗口离屏幕原点到x轴的距离    print("widget.frameGeometry().y() = %d" % widget.frameGeometry().y())    # 200    (以屏幕为原点的窗口纵坐标,不包含标题栏)    # 窗口本身的宽度    print("widget.frameGeometry().width()=%d" % widget.frameGeometry().width())    # 302   (窗口宽度)    # 窗口本身的高度    print("widget.frameGeometry().height()= %d" % widget.frameGeometry().height())    # 279  (窗口高度,包含标题栏)# 将点击事件与槽绑定btn.clicked.connect(onClick_Button)# 移动button到窗口内的相应位置btn.move(24,52)# 设置窗口的尺寸widget.resize(300,240)  # 设置工作区的尺寸# 移动窗口到屏幕的相应位置widget.move(600,200)# 设置窗口的标题widget.setWindowTitle('屏幕坐标系')# 创建窗口widget.show()# 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)# 如果不添加下行代码,运行程序会闪退sys.exit(app.exec_())

运行代码,效果如下:

点击窗口里面的“按钮”,效果如下:

分析代码:

有些许误差是因为在windows下窗体有边框,在mac下窗体无边框。

25.设置窗口和应用程序图标

在controls文件夹里,新建IconForm.py文件,执行代码:

# 设置窗口和应用程序图标# 窗口的setWindowIcon方法设置窗口的图标,只在Windows和linux中可用,mac不可用import sys# 从PyQt里面创建窗口和应用from PyQt5.QtWidgets import QMainWindow,QApplication# 用来添加图标from PyQt5.QtGui import QIcon# 定义一个类,这个类从QMainWindow里面继承class IconForm(QMainWindow):    # 初始化    def __init__(self,parent=None):        super(IconForm,self).__init__(parent)        self.initUI()    # 规范代码,初始化直接写在一个方法里    def initUI(self):        # 设置坐标系,可用同时设置窗口的尺寸和位置        self.setGeometry(400,400,250,450)        # 设置主窗口的标题        self.setWindowTitle('设置窗口图标')        # 设置窗口图标        self.setWindowIcon(QIcon('./images/001.jpg'))        # self.setWindowIcon(QIcon('/images/3.ico'))   这行代码失效,原因:图片路径表示问题        # # 设置窗口的尺寸        # self.resize(400,300)        # 获得状态栏        # self.status = self.statusBar()        #        # # 在状态栏上,设置消息的状态时间5000ms        # self.status.showMessage('只存在5秒的消息',5000)    # 防止别的脚本调用,只有自己单独运行,才会调用下面代码if __name__ == '__main__':    # 创建app实例,并传入参数    app =  QApplication(sys.argv)    # QApplication中的setWindowIcon方法用于设置主窗口的图标和应用程序图标,但调用了窗口的setWinodowIcon方法    # QApplication中的setWindowIcon方法就只能用于设置应用程序图标了    # 设置图标    # app.setWindowIcon(QIcon('images/horse.jpg'))    # 创建对象    main = IconForm()    # 创建窗口    main.show()    # 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)    sys.exit(app.exec_())

效果如下:

26.显示控件的提示信息

在controls文件夹里,新建Tooltip.py文件,执行代码:

# 显示控件的提示信息# 需要用到 QToolTipimport sysfrom PyQt5.QtWidgets import  QHBoxLayout,QMainWindow,QApplication,QToolTip,QPushButton,QWidget# 提示信息需要设置字体from PyQt5.QtGui import QFontclass TooltipForm(QMainWindow):    def __init__(self):        super().__init__()        # 调用初始化ui的一个方法        self.initUI()    # 编写初始化UI的方法    def initUI(self):        # 设置字体和字号        QToolTip.setFont(QFont('SansSerif',12))        # 给窗口设置提示,这个方法支持富文本        self.setToolTip('今天是个好日子')        # 设置窗口的位置和尺寸        self.setGeometry(300,300,200,200)        # 设置窗口的标题        self.setWindowTitle('设置控件提示消息')# 防止别的脚本调用,只有自己单独运行时,才会调用下面代码if __name__ == '__main__':    # 创建app实例,并传入参数    app= QApplication(sys.argv)    # 创建对象    main = TooltipForm()    # 创建窗口    main.show()    # 进入程序的主循环,并通过exit函数,确保主循环安全结束(该释放资源的释放资源)    sys.exit(app.exec_())

效果如下:

拓展,给窗口添加一个按钮,并显示提示信息

# 显示控件的提示信息# 需要用到 QToolTipimport sysfrom PyQt5.QtWidgets import  QHBoxLayout,QMainWindow,QApplication,QToolTip,QPushButton,QWidget# 提示信息需要设置字体from PyQt5.QtGui import QFontclass TooltipForm(QMainWindow):    def __init__(self):        super().__init__()        # 调用初始化ui的一个方法        self.initUI()    # 编写初始化UI的方法    def initUI(self):        # 设置字体和字号        QToolTip.setFont(QFont('SansSerif',12))        # 给窗口设置提示,这个方法支持富文本        self.setToolTip('今天是个好日子')        # 设置窗口的位置和尺寸        self.setGeometry(300,300,200,200)        # 设置窗口的标题        self.setWindowTitle('设置控件提示消息')    # 添加butoon按钮并设置提示信息        # 添加Button        # 创建全局对象self.button1        self.button1 = QPushButton('我的按钮')        # 设置按钮提示        self.button1.setToolTip('这是按钮的提示信息')        # 发送单击信号,执行对应的方法  (将信息与槽关联)        self.button1.clicked.connect(self.onClick_Button)        # 创建水平布局        layout = QHBoxLayout()        # 将组件加到水平局部里面        layout.addWidget(self.button1)        # 放置一个主框架        mainFrame = QWidget()        # 在主框架内添加水平布局        mainFrame.setLayout(layout)        # 把主框架放在窗口上        self.setCentralWidget(mainFrame)    # 按钮的单击事件的方法(自定义的槽)    def onClick_Button(self):        sender = self.sender()        print(sender.text() + '按钮被按下')        # 得到实例        app = QApplication.instance()        # 退出应用程序        app.quit()# 防止别的脚本调用,只有自己单独运行时,才会调用下面代码if __name__ == '__main__':    # 创建app实例,并传入参数    app= QApplication(sys.argv)    # 创建对象    main = TooltipForm()    # 创建窗口    main.show()    # 进入程序的主循环,并通过exit函数,确保主循环安全结束(该释放资源的释放资源)    sys.exit(app.exec_())

效果如下:

27.QLabel控件的基本用法

PyQt5常用控件之一,QLable控件,常用来展示文本信息

控件方法说明
setAlignment()设置文本的对齐方式
setIndent()设置文本缩进
text()获取文本内容
setBuddy()设置伙伴关系
setText()设置文本内容
selectedText()返回所选择的字符
setWordWrap()设置是否允许换行

QLabel常用的信号(事件):

1.当鼠标划过QLabel控件时触发:linkHovered

2.当鼠标单击QLabel控件时触发:linkActivated

在controls文件夹里,新建QLabelDemo.py文件,执行代码:

# QLabel控件的基本用法import sys# 导入QLabel模块  QVBoxLayout垂直布局  (QHBoxLayout 水平布局)from PyQt5.QtWidgets import QVBoxLayout,QMainWindow,QApplication,QLabel,QWidget# 导入调制板,调制QLabel背景色# 导入显示图片包QPixmapfrom PyQt5.QtGui import QPixmap,QPalette# 导入一些Qt的常量from PyQt5.QtCore import Qt# 编写一个类,从QWidget中继承class QLabelDemo(QWidget):    def __init__(self):        super().__init__()        # 调用初始化UI的一个方法        self.initUI()    # 规范代码,初始化UI直接写在一个方法里    def initUI(self):        # 创建四个label控件        label1 = QLabel(self)        label2 = QLabel(self)        label3 = QLabel(self)        label4 = QLabel(self)        # 给label1设置文本,支持html的标签        label1.setText("这是一个文本标签.")        # 用调试板自动填充背景        label1.setAutoFillBackground(True)        # 创建调试板        palette = QPalette()        # 给调试板设置背景色        palette.setColor(QPalette.Window,Qt.blue)        # 对label1使用调试板        label1.setPalette(palette)        # 让label1居中对齐        label1.setAlignment(Qt.AlignCenter)        # 给label2设置标签        label2.setText("欢迎使用Python GUI程序")  # 可以在a标签里触发事件或者跳转网页 二者选其一        # 给label3设置文本居中        label3.setAlignment(Qt.AlignCenter)        # 给label3设置提示文本        label3.setToolTip('这是一个图片标签')        # 让label3显示图片        label3.setPixmap(QPixmap("./images/4.jpg"))   # 同级目录写法./images        # 给label4设置文本内容        label4.setText("打开百度")  # setText里面的内容要用双引号,单引号会报错        # 让label4打开链接        # 如果设为True,用浏览器打开网页,如果设为False,调用槽函数        label4.setOpenExternalLinks(True)        # 让label4的文本右对齐        label4.setAlignment(Qt.AlignRight)        # 给label4设置提示文本        label4.setToolTip('这是一个超链接')        # 创建一个垂直布局        vbox = QVBoxLayout()        # 分别把这四个控件放到这个布局里面           布局函数 addWidget        vbox.addWidget(label1)        vbox.addWidget(label2)        vbox.addWidget(label3)        vbox.addWidget(label4)        # 将信号与槽绑定        label2.linkHovered.connect(self.linkHovered)        label4.linkActivated.connect(self.linkClicked)        # 设置布局        self.setLayout(vbox)        self.setWindowTitle('QLabel控件演示')        # 设置标题    # 槽有两个方法 1.滑过  2.单击    def linkHovered(self):        print("当鼠标滑过label2标签时,触发事件")    def linkClicked(self):        print("当鼠标单击label4标签时,触发事件")  # 防止别的脚本调用,只有自己单独运行,才会调用下面代码if __name__ == '__main__':    # 创建app实例,并传入参数    app =  QApplication(sys.argv)    # 设置图标    # app.setWindowIcon(QIcon('images/001.jpg'))    # 创建对象    main = QLabelDemo()    # 创建窗口    main.show()    # 进入程序的主循环,并通过exit函数确保主循环安全结束(该释放资源的一定要释放)    sys.exit(app.exec_())

效果展示:

注意:调整好图片的尺寸,防止它跳出屏幕外

28.如何设置QLabel控件和其他控件的伙伴关系

在controls文件夹里,新建QLabelBuddy.py文件,执行代码:

# QLabel与伙伴控件# 1.通过setBuddy设置伙伴关系# 2.通过栅格布局来完成手动布局,依靠# mainLayout.addWidget(控件对象,rowIndex,columnIndex,row,column)   (行索引,索引,占用多少行,占用多少列)import sysfrom PyQt5.QtWidgets import *class QLabelBuddy(QDialog):    def __init__(self):        super().__init__()        self.initUI()    # 规范代码,初始化直接写在一个方法里    def initUI(self):        # 设置主窗口的标题        self.setWindowTitle('QLabel与伙伴控件')        # 创建nameLabel控件        #添加热键   添加热键的方法& + 英文字母  ,后面的英文字母就变成了热键。在可视化窗口里通过 "Alt" + 英文字母 就可以起作用        nameLabel =QLabel('&Name',self)        # 创建QLineEdit控件        nameLineEdit = QLineEdit(self)        # 把nameLabel和nameLineEdit设置伙伴关系        nameLabel.setBuddy(nameLineEdit)        # 创建PasswordQLabel控件, 并添加热键        passwordLabel = QLabel('&Password',self)        # 创建PasswordLineEdit控件        passwordLineEdit = QLineEdit(self)        # 把passwordLabel和passwordLineEdit设置成伙伴关系        passwordLabel.setBuddy(passwordLineEdit)        # 创建两个按钮,一个上面写OK,一个上面写Cancel        btnOK = QPushButton('&OK')        btnCancel = QPushButton('&Cancel')        # 使用栅格布局        mainLayout = QGridLayout(self)        # 将nameLabel控件放到布局里面    第一行第一列        mainLayout.addWidget(nameLabel,0,0)        # 将nameLineEdit控件放到布局里面  第一行第二列,占用一行两列        mainLayout.addWidget(nameLineEdit,0,1,1,2)        # 将passwordLabel控件放到布局里面  第二行第一列        mainLayout.addWidget(passwordLabel,1,0)        # 将passwordLineEdit控件放到布局里面 第二行第一列,占用一行两列        mainLayout.addWidget(passwordLineEdit,1,1,1,2)        # 经过上面操作,此时有两行,每行有三列        # 放置按钮  第三行第二列        mainLayout.addWidget(btnOK,2,1)        # 放置按钮    第三行第三列        mainLayout.addWidget(btnCancel,2,2)# 防止别的脚本调用,只有自己单独运行时,才会调用下面代码if __name__ == '__main__':    # 创建app实例,并传入参数    app= QApplication(sys.argv)    # 创建对象    main = QLabelBuddy()    # 创建窗口    main.show()    # 进入程序的主循环,并通过exit函数,确保主循环安全结束(该释放资源的释放资源)    sys.exit(app.exec_())

效果如下:

28.QLineEdit控件与回显模式(EchoMode)

在controls文件夹里,新建QLineEditEchoMode.py文件,执行代码:

# QLineEdit控件与回显模式# QLineEdit控件的基本功能:1.输入单行的文本 2.设置回显模式EcoMode"""EcoMode(回显模式)4种回显模式1.Normal 正常的显示2.Normal  不显示   类似于linux中输入密码没反应 但已经提交3,Password  密码式的显示   类似于输入密码出现小黑点或*号4,PasswordEchoOnEdit     密码显示编辑模式   常见于手机端,类似于  输入一个字母A,前两秒编辑框里显示的是A,过了一两秒编程框里变成的一个点或者*号"""import sysfrom PyQt5.QtWidgets import *# 从QWidget窗口类里面继承class QLineEditEchoMode(QWidget):    def __init__(self):        super(QLineEditEchoMode,self).__init__()        self.initUI()    # 编写初始化方法    def initUI(self):        # 设置窗口标题        self.setWindowTitle('文本输入框的回显模式')        # 创建表单布局        formLayout = QFormLayout()        # 根据4种回显模式,分别创建4种表单布局        # 第一种回显模式        normalLineEdit = QLineEdit()        # 第二种回显模式        noEchoLineEdit = QLineEdit()        # 第三种回显模式        passwordLineEdit = QLineEdit()        # 第四种回显模式        passwordEchoONEditLineEdit = QLineEdit()        # 把这四个控件添加到表单布局里面        formLayout.addRow("Normal",normalLineEdit)        formLayout.addRow("NoEcho",noEchoLineEdit)        formLayout.addRow("Password",passwordLineEdit)        formLayout.addRow("PasswordEchoOnEdit",passwordEchoONEditLineEdit)        # 为每个文本框设置placeholdertext,就是当输入框没有输入时,以灰色字体显示这个文本框的提示        normalLineEdit.setPlaceholderText("Normal")        normalLineEdit.setPlaceholderText("NoEcho")        passwordLineEdit.setPlaceholderText("Password")        passwordEchoONEditLineEdit.setPlaceholderText("PasswprdEchoOnEdit")        # 设置模式        normalLineEdit.setEchoMode(QLineEdit.Normal)        noEchoLineEdit.setEchoMode(QLineEdit.NoEcho)        passwordLineEdit.setEchoMode(QLineEdit.Password)        passwordEchoONEditLineEdit.setEchoMode(QLineEdit.PasswordEchoOnEdit)        # 应用表单布局        self.setLayout(formLayout)# 防止别的脚本调用,只有自己单独运行时,才会调用下面代码if __name__ == '__main__':    # 创建app实例,并传入参数    app= QApplication(sys.argv)    # 创建对象    main = QLineEditEchoMode()    # 创建窗口    main.show()    # 进入程序的主循环,并通过exit函数,确保主循环安全结束(该释放资源的释放资源)    sys.exit(app.exec_())

效果如下:

28.限制QLineEdit控件的输入(校验器)

在controls文件夹里,新建QLineEditEValidator.py文件,执行代码:

# 限制QLineEdit控件的输入(校验器)   只能输入满足格式的数据# 如限制只能输入整数、浮点数或满足一定条件的字符串# 本次演示做三种限制: 1.整数  2.浮点数   3.字母或者数字import sysfrom PyQt5.QtWidgets import *# 导入PyQt5的正则(三个校验器,第三个可自定义)from PyQt5.QtGui import QIntValidator,QDoubleValidator,QRegExpValidator# 导入PyQt5里正则表达式的一个类QRegExpfrom PyQt5.QtCore import QRegExp# 编写一个类,从QWidget窗口类里面继承class QLineEditValidator(QWidget):    def __init__(self):        super(QLineEditValidator,self).__init__()        self.initUI()    # 编写初始化方法    def initUI(self):        # 设置一下窗口标题        self.setWindowTitle('校验器')        # 创建表单布局        formLayout = QFormLayout()        # 创建三个文本输入框        intLineEdit = QLineEdit()        doubleLineEdit = QLineEdit()        validatorLineEdit = QLineEdit()        # 将这三个控件添加到form表单布局里        formLayout.addRow('整数类型',intLineEdit)        formLayout.addRow('浮点类型',doubleLineEdit)        formLayout.addRow('数字和字母',validatorLineEdit)        # 为每个文本框设置placeholdertext,就是当输入框没有输入时,以灰色字体显示这个文本框的提示        intLineEdit.setPlaceholderText('整数')        doubleLineEdit.setPlaceholderText('浮点型')        validatorLineEdit.setPlaceholderText('字母和数字')        # 创建整数校验器        inValidator = QIntValidator(self)        # 设置整数的范围 [1,99]        inValidator.setRange(1,99)        # 创建浮点校验器        doubleValidator = QDoubleValidator(self)        # 设置浮点校验器[-360,360]        doubleValidator.setRange(-360,-360)        # 小数点的表示        doubleValidator.setNotation(QDoubleValidator.StandardNotation)        # 设置精度,小数点2位        doubleValidator.setDecimals(2)        # 创建数字和字母的正则表达式        reg = QRegExp('[a-zA-Z0-9]+$')   # 此时+表示至少有一个        # 创建数字和字母的校验器        validator = QRegExpValidator(self)        # 将正则表达式放置在校验器内        validator.setRegExp(reg)        # 设置校验器        intLineEdit.setValidator(inValidator)        doubleLineEdit.setValidator(doubleValidator)        validatorLineEdit.setValidator(validator)        # 应用表单布局        self.setLayout(formLayout)# 防止别的脚本调用,只有自己单独运行时,才会调用下面代码if __name__ == '__main__':    # 创建app实例,并传入参数    app= QApplication(sys.argv)    # 创建对象    main = QLineEditValidator()    # 创建窗口    main.show()    # 进入程序的主循环,并通过exit函数,确保主循环安全结束(该释放资源的释放资源)    sys.exit(app.exec_())

效果如下:

29.使用掩码限制QLineEdit控件的输入
掩码说明
AASCⅡ字母字符是必须输入的(A-Z、a-z)
aASCⅡ字母字符是允许输入的,但不是必需的(A-Z、a-z)
NASCⅡ字母字符是必须输入的(A-Z、a-z、0-9)
nASCⅡ字母的允许输入的,但不是必需的(A-Z、a-z、0-9)
X任何字符都是必须输入的
x任何字符都是允许输入的,但不是必需的
9ASCⅡ数字字符是必须输入的(0-9)
0ASCⅡ数字字符是允许输入的,但不是必需的(0-9)
DASCⅡ数字字符是必须输入的(1-9)
dASCⅡ数字字符是允许输入的,但不是必需的(1-9)
#ASCⅡ数字字符或加减符号是允许输入的,但不是必需的
H十六进制格式字符是必须输入的(A-F、a-f、0-9)
h十六进制格式字符是允许输入的,但不是必需的(A-F、a-f、0-9)
B二进制格式字符是必须输入 的(0,1)
b二进制格式字符是允许输入的,但不是必需的(0,1)
>所有的字母字符都大写
0: items = items[0] # 设置背景色 items.setBackground(QBrush(QColor(0,255,0))) items.setForeground(QBrush(QColor(255,0,0))) # 获得当前项所在的行 row = items.row() # 定位到指定的行 # verticalScrollBar 获得滚动条 tableWidget.verticalScrollBar().setSliderPosition(row) # 搜索满足条件的Cell text = '(1' # MatchStartsWit 以..开头 items = tableWidget.findItems(text, QtCore.Qt.MatchStartsWith) if len(items) > 0: items = items[0] # 设置背景色 items.setBackground(QBrush(QColor(0, 255, 0))) items.setForeground(QBrush(QColor(255, 0, 0))) # 获得当前项所在的行 row = items.row() # 定位到指定的行 # verticalScrollBar 获得滚动条 tableWidget.verticalScrollBar().setSliderPosition(row) # 应用于布局 self.setLayout(layout)if __name__ == '__main__': # app实例化 传参 app = QApplication(sys.argv) # 创建对象 example = DataLocation() # 创建窗口 example.show() # 进入主循环 sys.exit(app.exec_())

效果展示:

64.设置单元格字体和颜色

在table_tree文件夹里新建CellFontAndColor.py文件,执行代码:

"""设置单元格字体和颜色"""import sysfrom PyQt5.QtWidgets import (QWidget,QTableWidget,QHBoxLayout,QApplication,QTableWidgetItem)from PyQt5.QtGui import QBrush,QColor,QFontclass CellFontAndColor(QWidget):    def __init__(self):        super(CellFontAndColor, self).__init__()        self.initUI()    def initUI(self):        # 设置窗口标题        self.setWindowTitle("设置单元格字体和颜色")        # 设置窗口的尺寸        self.resize(600,300)        # 创建水平布局        layout = QHBoxLayout()        # 创建QTableWidget控件        tableWidget = QTableWidget()        # 设置tableWidget的行        tableWidget.setRowCount(4)        # 设置tableWidget的列        tableWidget.setColumnCount(3)        # 把控件放置在布局里        layout.addWidget(tableWidget)        # 设水平表头        tableWidget.setHorizontalHeaderLabels(['姓名','性别','体重(kg)'])        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('水手')        # 字号 字体        newItem.setFont(QFont('Times',14,QFont.Black))        # 设置字颜色        newItem.setForeground(QBrush(QColor(255,0,0)))        # 添加到第一行第一列        tableWidget.setItem(0,0,newItem)        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('大海')        # 设置字的颜色        newItem.setForeground(QBrush(QColor(255,200,0)))        # 设置背景色        newItem.setBackground(QBrush(QColor(0,0,220)))        # 添加到第一行第二列        tableWidget.setItem(0,1,newItem)        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('你好')        # 设置字的颜色        newItem.setFont(QFont('Times', 25, QFont.Black))        newItem.setForeground(QBrush(QColor(125, 50, 0)))        # 设置背景色        newItem.setBackground(QBrush(QColor(0, 0, 20)))        # 添加到第一行第二列        tableWidget.setItem(0, 2, newItem)        # 应用于水平布局        self.setLayout(layout)if __name__ == '__main__':    app = QApplication(sys.argv)    example = CellFontAndColor()    example.show()    sys.exit(app.exec_())

效果展示:

65.按列排序

在table_tree文件夹里新建ColumnSort.py文件,执行代码:

"""按列排序1.按那一列排序2.排序类型  升序或降序sortItems(columnIdex,orderType)"""import sysfrom PyQt5.QtWidgets import *from PyQt5.QtCore import *class ColumnSort(QWidget):    def __init__(self):        super(ColumnSort, self).__init__()        self.initUI()    def initUI(self):        # 设置窗口标题        self.setWindowTitle('按列排序')        # 设置窗口尺寸        self.resize(600,400)        # 创建垂直布局        layout = QVBoxLayout()        # 创建QTableWdiget控件        self.tableWidget  = QTableWidget()        # 设置行数        self.tableWidget.setRowCount(4)        # 设置列数        self.tableWidget.setColumnCount(3)        # 把控件添加到布局里        layout.addWidget(self.tableWidget)        # 设置水平表头        self.tableWidget.setHorizontalHeaderLabels(['姓名','性别','体重(kg)'])        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('张三')        # 添加到第一行第一列        self.tableWidget.setItem(0,0,newItem)        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('男')        # 添加到第一行第二列        self.tableWidget.setItem(0, 1, newItem)        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('178')        # 添加到第一行第三列        self.tableWidget.setItem(0, 2, newItem)        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('李四')        # 添加到第二行第一列        self.tableWidget.setItem(1, 0, newItem)        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('男')        # 添加到第二行第二列        self.tableWidget.setItem(1, 1, newItem)        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('172')        # 添加到第二行第三列        self.tableWidget.setItem(1, 2, newItem)        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('花花')        # 添加到第三行第一列        self.tableWidget.setItem(2, 0, newItem)        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('女')        # 添加到第三行第二列        self.tableWidget.setItem(2, 1, newItem)        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('168')        # 添加到第三行第三列        self.tableWidget.setItem(2, 2, newItem)       # 添加button按钮        self.button = QPushButton('排序')        # 绑定 信号 槽        self.button.clicked.connect(self.order)       # 把控件放到布局里        layout.addWidget(self.button)       # 设置当前的排序类型   降序排列        self.orderType = Qt.DescendingOrder        # 应用于布局        self.setLayout(layout)    # 槽方法    def order(self):        # 如果当前排序是降序,则改为升序        if self.orderType == Qt.DescendingOrder:            self.orderType = Qt.AscendigOrder        else:            # 如果是升序,改成降序            self.orderType = Qt.DescendingOrder        # 排序        self.tableWidget.sortItems(2,self.orderType)if __name__ == '__main__':    app = QApplication(sys.argv)    main = ColumnSort()    # 创建窗口    main.show()    # 创建主程序    sys.exit(app.exec_())

效果展示:

ps:windows上,因为DescendingOrder方法问题,只能显示上图效果,点击排序按钮后,退出。待后续解决。

66.设置单元格的文本对齐方式

在table_tree文件夹里新建CellTextAlignment.py文件,执行代码:

"""设置单元格的文本对齐方式使用setTextAlignment方法里面有一些常量  Qt.AlignRight  Qt.AlignBottom"""import sysfrom PyQt5.QtWidgets import (QWidget,QTableWidget,QHBoxLayout,QApplication,QTableWidgetItem)from PyQt5.QtCore import  Qtclass CellTextAlignment(QWidget):    def __init__(self):        super(CellTextAlignment, self).__init__()        self.initUI()    def initUI(self):        # 设置窗口标题        self.setWindowTitle('设置单元格的文本对齐方式')        # 设置尺寸        self.resize(430,230)        # 创建水平布局        layout = QHBoxLayout()        # 创建QTableWidget控件        tableWidget = QTableWidget()        # 设置行数        tableWidget.setRowCount(4)        # 设置列数        tableWidget.setColumnCount(3)        # 把控件添加到布局里        layout.addWidget(tableWidget)        # 设置水平表头        tableWidget.setHorizontalHeaderLabels(['姓名','性别','体重(kg)'])        # 添加字段        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('水生')        # 设置文本为右对齐  默认单元格的顶端显示  可以设置为底端        newItem.setTextAlignment(Qt.AlignRight | Qt.AlignBottom)        # 给tableWidget添加newItem字段   此时表内是空的        #  把newItem字段添加到第一行第一列        tableWidget.setItem(0,0,newItem)        # 添加字段        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('28')        # 设置文本为中心对齐  上下左右都对称    Qt.AlignBottom未起作用        newItem.setTextAlignment(Qt.AlignCenter | Qt.AlignBottom)        # 给tableWidget添加newItem字段   此时表内是空的        #  把newItem字段添加到第一行第二列        tableWidget.setItem(0, 1, newItem)        # 添加字段        # 创建QTableWidgetItem控件        newItem = QTableWidgetItem('178')        # 设置文本为右对齐          newItem.setTextAlignment(Qt.AlignRight)        # 给tableWidget添加newItem字段   此时表内是空的        #  把newItem字段添加到第一行第三列        tableWidget.setItem(0, 2, newItem)        # 应用于水平布局        self.setLayout(layout)# 单独执行此脚本,才会运行下面的代码if __name__ == '__main__':    # app实例化,并传参    app = QApplication(sys.argv)    # 创建对象    example = CellTextAlignment()    # 创建窗口    example.show()    # 进入主循环,调用exit方法,确保主循环顺利退出    sys.exit(app.exec_())

效果展示:

67.合并单元格

在table_tree文件夹里新建Span.py文件,执行代码:

"""合并单元格setSpan(row,col,要合并的行数,要合并的列数)"""import sysfrom PyQt5.QtWidgets import (QWidget,QTableWidget,QHBoxLayout,QApplication,QTableWidgetItem)class Span(QWidget):    def __init__(self):        super(Span, self).__init__()        self.initUI()    def initUI(self):        self.setWindowTitle('合并单元格')        self.resize(430,230)        # 创建水平布局        layout = QHBoxLayout()        # 创建表格控件        tableWidget = QTableWidget()        # 设置表格的行数        tableWidget.setRowCount(4)        # 设置表格的列数        tableWidget.setColumnCount(3)        # 把表格控件添加到布局里        layout.addWidget(tableWidget)        # 创建水平表头        tableWidget.setHorizontalHeaderLabels(['姓名','年龄','身高'])        # 创建字段        newItem = QTableWidgetItem('大卫')        # newItem添加到表格里 第一行第一列        tableWidget.setItem(0,0,newItem)        # 合并第一行第一列  ,合并3行,合并一列        tableWidget.setSpan(0,0,3,1)        # 创建字段        newItem = QTableWidgetItem('18')        # newItem添加到表格里  第一行第二列        tableWidget.setItem(0, 1, newItem)        # 合并第一行第二列   合并两行,合并一列        tableWidget.setSpan(0,1,2,1)        # 创建字段        newItem = QTableWidgetItem('180')        # newItem添加到表格里   第一行第三列        tableWidget.setItem(0, 2, newItem)        # 合并第一行第三列  合并4行 合并一列        tableWidget.setSpan(0,2,4,1)        # 创建字段        newItem = QTableWidgetItem('测试')        # newItem添加到表格里   第四行第一列        tableWidget.setItem(3, 0, newItem)        # 合并第四行第一  合并一行 合并两列        tableWidget.setSpan(3, 0, 1, 2)        # 应用于水平布局        self.setLayout(layout)# 直接调用该脚本,执行下面代码if __name__ == '__main__':    # app实例化,并传参    app = QApplication(sys.argv)    # 创建对象    main = Span()    # 创建窗口    main.show()    # 进入主循环,调用exit方法,确保主循环安全退出    sys.exit(app.exec_())

效果展示:

68.设置单元格的尺寸

在table_tree文件夹里新建CellSize.py文件,执行代码:

"""设置单元格尺寸"""import sysfrom PyQt5.QtGui import QBrush, QColor, QFontfrom PyQt5.QtWidgets import (QWidget,QTableWidget,QHBoxLayout,QApplication,QTableWidgetItem)class CellSize(QWidget):    def __init__(self):        super(CellSize, self).__init__()        self.initUI()    def initUI(self):        self.setWindowTitle('QTableWidget 例子')        self.resize(530,300)        # 创建水平布局        layout  = QHBoxLayout()        # 创建表格控件        tableWidget = QTableWidget()        # 设置表格控件的行        tableWidget.setRowCount(4)        # 设置表格控件的列        tableWidget.setColumnCount(3)        # 创建字段        newItem = QTableWidgetItem('活力')        # 设置字体  字体大小        newItem.setFont(QFont('Times',20,QFont.Black))        # 设置字体颜色        newItem.setForeground(QBrush(QColor(30,113,150)))        # 设置单元格背景        newItem.setBackground(QBrush(QColor(30,82,30)))        # 把字段添加到表格里  第一行第一列        tableWidget.setItem(0,0,newItem)        # 创建字段        newItem = QTableWidgetItem('18')        # 设置字体  字体大小        newItem.setFont(QFont('Times', 40, QFont.Black))        #改变行的高度   第一个参数是行  第二个参数是设定值   第一行  高度80        tableWidget.setRowHeight(0,120)        # 把字段添加到表格里    第一行第二列        tableWidget.setItem(0, 1, newItem)        # 创建字段        newItem = QTableWidgetItem('167')        # 设置字体  字体大小        newItem.setFont(QFont('Times', 60, QFont.Black))        # 改变第三行的高度   第三行  高度80        tableWidget.setRowHeight(2,20)        # 改变列的高度   第一个参数是列  第二个参数是设定值   第三列  宽度120        tableWidget.setColumnWidth(2,160)        # 把字段添加到表格里   第一行第三列        tableWidget.setItem(0, 2, newItem)        # 把表格控件添加到布局里        layout.addWidget(tableWidget)        #应用于表格控件        self.setLayout(layout)# 直接执行此脚本,才会调用下面代码if __name__ == '__main__':    # app实例化,并传参    app =QApplication(sys.argv)    # 创建对象    main = CellSize()    # 创建窗口    main.show()    # 创建主循环,调用exit方法,确保主循环安全退出    sys.exit(app.exec_())

效果展示:

69.在单元格中实现图文混排的效果

在table_tree文件夹里新建CellImageText.py文件,执行代码:

"""在单元格中实现图文混排的效果"""# 让文本和图像 同时显示到一个单元格import sysfrom PyQt5.QtWidgets import *from PyQt5.QtGui import *class CellImageText(QWidget):    def __init__(self):        super(CellImageText, self).__init__()        self.initUI()    def initUI(self):        self.setWindowTitle('在单元格实现图文混排的效果')        self.resize(800,300)        # 创建水平布局        layout = QHBoxLayout()        # 创建全局表格控件        self.tableWidget = QTableWidget()        # 给表格控件设置行        self.tableWidget.setRowCount(5)        # 给表格控件设置列        self.tableWidget.setColumnCount(4)        # 给表格控件设置水平表头        self.tableWidget.setHorizontalHeaderLabels(['姓名','性别','体重','显示图片'])        # 创建字段        # 添加QTableWidgetItem控件        newItem = QTableWidgetItem('黎明')        # 把字段控件放到表格控件里  第一行第一列        self.tableWidget.setItem(0,0,newItem)        newItem = QTableWidgetItem('男')        # 把字段控件放到表格控件里  第一行第二列        self.tableWidget.setItem(0, 1, newItem)        newItem = QTableWidgetItem('18')        # 把字段控件放到表格控件里  第一行第三列        self.tableWidget.setItem(0, 2, newItem)        # 第四列添加图片        newItem = QTableWidgetItem(QIcon('../controls/images/5.png'),'背包')        # 把newItem控件放到表格控件里 第一行第四列        self.tableWidget.setItem(0,3,newItem)        # 把表格控件添加到水平布局里面        layout.addWidget(self.tableWidget)        # 应用于水平布局        self.setLayout(layout)if __name__ == '__main__':    app =QApplication(sys.argv)    main = CellImageText()    main.show()    sys.exit(app.exec_())

效果展示:

70.改变单元格中图片的尺寸

在table_tree文件夹里新建CellImageSize.py文件,执行代码:

"""改变单元格中的图片尺寸setIconSize(QSize(width,height))"""import sysfrom PyQt5.QtWidgets import *from PyQt5.QtGui import *from PyQt5.QtCore import *class CellImageSize(QWidget):    def __init__(self):        super(CellImageSize, self).__init__()        self.initUI()    def initUI(self):        self.setWindowTitle('改变单元格中图片的尺寸')        self.resize(800,600)        # 创建水平布局        layout = QHBoxLayout()        # 创建表格控件        tablewidget = QTableWidget()        # 设置表格行        tablewidget.setRowCount(5)        # 设置表格列        tablewidget.setColumnCount(3)        # 设置表格内图像的尺寸        tablewidget.setIconSize(QSize(200,80))        # 设置水平表头        tablewidget.setHorizontalHeaderLabels(['图片1','图片2','图片3'])        # 让列的宽度和图片的宽度相同        for i in range(3):            tablewidget.setColumnWidth(i,200)        # 让行的高度和图片的高度相同        for i in range(5):            tablewidget.setRowHeight(i,80)        # 添加图片        # 如果有15张图片        for k in range(15):            i = k / 3  # 行            j = k % 3  # 列            item = QTableWidgetItem()            item.setIcon(QIcon('./images/00%s.jpg'% k))            tablewidget.setItem(i,j,item)        # 把表格控件添加到水平布局里        layout.addWidget(tablewidget)        # 应用于水平布局        self.setLayout(layout)if __name__ == '__main__':    app =QApplication(sys.argv)    main = CellImageSize()    main.show()    sys.exit(app.exec_())

效果展示:

71.在表格中显示上下文菜单

在table_tree文件夹里新建TableWidgetContextMenu.py文件,执行代码:

"""在表格中显示上下文1.如何弹出菜单2.如何在满足条件的情况下弹出菜单 QMenu.exec_"""# 特定单元格点击鼠标右键弹出菜单import sysfrom PyQt5.QtCore import Qtfrom PyQt5.QtWidgets import (QMenu,QPushButton,QWidget,QTableWidget,QHBoxLayout,QApplication,QTableWidgetItem,QHeaderView)class TableWidgetContextMenu(QWidget):    def __init__(self):        super(TableWidgetContextMenu, self).__init__()        self.initUI()    def initUI(self):        self.setWindowTitle('在表格中显示上下文菜单')        self.resize(500,300)        # 创建水平布局        layout = QHBoxLayout()        # 创建全局的表格控件        self.tableWidget = QTableWidget()        # 表格设置行        self.tableWidget.setRowCount(5)        # 表格设置列        self.tableWidget.setColumnCount(3)        # 把表格添加到水平布局里        layout.addWidget(self.tableWidget)        # 设置水平表格头        self.tableWidget.setHorizontalHeaderLabels(['姓名','性别','体重'])        # 添加字段        newItem = QTableWidgetItem('张三')        # 把字段添加到表格里 第一行第一列        self.tableWidget.setItem(0,0,newItem)        # 添加字段        newItem = QTableWidgetItem('女')        # 把字段添加到表格里  第一行第二列        self.tableWidget.setItem(0, 1, newItem)        # 添加字段        newItem = QTableWidgetItem('28')        # 把字段添加到表格里   第一行第三列        self.tableWidget.setItem(0, 2, newItem)        # 设置允许弹出菜单  单击右键响应事件        self.tableWidget.setContextMenuPolicy(Qt.CustomContextMenu)        # 将信号请求连接到一个槽        self.tableWidget.customContextMenuRequested.connect(self.generateMenu)        # 应用于水平布局        self.setLayout(layout)    # 槽方法    def generateMenu(self,pos):        # pos 为单击鼠标右键的坐标  相对于窗口        # 鼠标右键单击前两行弹出菜单,单击第三行没响应        print(pos)        for i in self.tableWidget.selectionModel().selection().indexes():            # 当前选中的行            rowNum = i.row()        # 如果选择的行索引小于2,弹出上下文菜单        if rowNum  

效果展示:

72.树控件(QTreeWidget)

在table_tree文件夹里新建BasicTreeWidget.py文件,执行代码:

"""树控件(QTreeWidget)的基本用法"""import sysfrom PyQt5.QtWidgets import *from PyQt5.QtGui import QIcon,QBrush,QColorfrom PyQt5.QtCore import Qtclass BasicTreeWidget(QMainWindow):    def __init__(self,parent= None):        super(BasicTreeWidget, self).__init__(parent)        self.setWindowTitle('树控件(QTreeWidget)的基本用法')        self.resize(600,300)        # 创建树控件        self.tree = QTreeWidget()        # 将树控件设为中心控件,充满整个屏幕        self.setCentralWidget(self.tree)        # 为树控件指定列数    让它显示两列        # 每个都只能显示两列        self.tree.setColumnCount(2)        # 指定列标签        self.tree.setHeaderLabels(['key','Value'])        # 根节点        # 类似于表格的创建字段        root = QTreeWidgetItem(self.tree)        # 将根阶段放置在第一列        root.setText(0,'根节点')        # 给根节点设置图标        root.setIcon(0,QIcon('./images/000.jpg'))        # 给第一列设置列宽        self.tree.setColumnWidth(0,160)        # 添加子节点1        # 让子节点child1指向root        child1 = QTreeWidgetItem(root)        # 设置子节点第一列文本        child1.setText(0,'子节点1')        # 设置子节点第二列的文本        child1.setText(1,"子节点1的数据")        # 设置子节点第一列的图标        child1.setIcon(0,QIcon('./images/001.jpg'))        # 给子节点第一列添加复选框        child1.setCheckState(0,Qt.Checked)        # 设置子节点第二列的图标        child1.setIcon(1, QIcon('./images/001.jpg'))        # 添加子节点2        # 让子节点child2指向root        child2 = QTreeWidgetItem(root)        # 设置子节点第一列文本        child2.setText(0,'子节点2')        # 设置子节点第一列设置图标        child2.setIcon(0,QIcon('./images/006.jpg'))        # 为子节点2再添加一个子节点        # 让子节点chil2_指向子节点chil2        child2_ = QTreeWidgetItem(child2)        # 设置子节点第一列文本        child2_.setText(0,'子节点2的子节点的第一列')        # 设置子节点第一列文本        child2_.setText(1, '子节点2的子节点的第二列')        # 设置子节点第一列文本    由于设置了self.tree.setColumnCount(2),所以没有第三列        # child2_.setText(2, '子节点2的子节点的第三列')        # 给子节点的第一列设置图标        child2_.setIcon(0,QIcon('./images/008.jpg'))        # 给子节点的第二列设置图标        child2_.setIcon(1, QIcon('./images/001.jpg'))        # 将节点默认展开        self.tree.expandAll()if __name__ == '__main__':    app = QApplication(sys.argv)    tree = BasicTreeWidget()    tree.show()    sys.exit(app.exec_())

效果展示:

73.为树节点添加响应事件

在table_tree文件夹里新建TreeEvent.py文件,执行代码:

"""为树节点添加响应事件"""import sysfrom PyQt5.QtWidgets import *from PyQt5.QtCore import *class TreeEvent(QMainWindow):    def __init__(self,parent= None):        super(TreeEvent, self).__init__(parent)        self.setWindowTitle('为树节点添加响应事件')        # 创建一个树        self.tree = QTreeWidget()        # 给这个树创建列的数量        self.tree.setColumnCount(2)        # 设置头        # 指定列标签        self.tree.setHeaderLabels(['Key','Value'])        # 创建节点        root = QTreeWidgetItem(self.tree)        root.setText(0,"root")        root.setText(1,'0')        # 创建子节点        # 让子节点child1指向root        child1 = QTreeWidgetItem(root)        # 给子节点第一列设置文本        child1.setText(0,"child1")        # 给子节点第二列设置文本        child1.setText(1,'1')        # 创建子节点        # 让子节点child2指向root        child2 = QTreeWidgetItem(root)        # 给子节点第一列设置文本        child2.setText(0, "child2")        # 给子节点第二列设置文本        child2.setText(1, '2')        # 创建子节点        # 让子节点child3指向child2        child3 = QTreeWidgetItem(child2)        # 给子节点第一列设置文本        child2.setText(0, "child3")        # 给子节点第二列设置文本        child2.setText(1, '3')        # 将树设置为中心控件,充满整个屏幕        # 这样在屏幕上就可以显示        self.setCentralWidget(self.tree)        # 为树添加节点,用单击信号        self.tree.clicked.connect(self.onTreeClicked)    # 槽方法    def onTreeClicked(self,index):        # 获得当前的单击项        item = self.tree.currentItem()        # 当前行        print(index.row())        # 输出当前单击节点的key        print('key=%s,value=%s' % (item.text(0),item.text(1)))if __name__ == '__main__':    app = QApplication(sys.argv)    tree = TreeEvent()    tree.show()    sys.exit(app.exec_())

效果展示:

74.添加,修改和删除树控件中的节点

在table_tree文件夹里新建ModifyTree.py文件,执行代码:

"""添加、修改和删除树控件中的节点"""import sysfrom PyQt5.QtWidgets import *from PyQt5.QtGui import *class ModifyTree(QWidget):    def __init__(self,parent=None):        super(ModifyTree, self).__init__(parent)        self.setWindowTitle('TreeWidget  例子')        self.resize(600,400)        operatorLayout = QHBoxLayout()        # 创建按钮控件        addBtn = QPushButton('添加节点')        updateBtn = QPushButton('修改节点')        deleteBtn = QPushButton('删除节点')        # 把控件添加到水平布局里        operatorLayout.addWidget(addBtn)        operatorLayout.addWidget(updateBtn)        operatorLayout.addWidget(deleteBtn)        # 把这三个按钮绑定到相应的槽上        addBtn.clicked.connect(self.addNode)        updateBtn.clicked.connect(self.updateNode)        deleteBtn.clicked.connect(self.deleteNode)        #  下行代码不需要,一次应用于布局就可以了        # self.setLayout(operatorLayout)        # 创建一个树        self.tree = QTreeWidget()        # 给这个树创建列的数量        self.tree.setColumnCount(2)        # 设置头        # 指定列标签        self.tree.setHeaderLabels(['Key', 'Value'])        #        # 创建节点        root = QTreeWidgetItem(self.tree)        root.setText(0, "root")        root.setText(1, '0')        # 创建子节点        # 让子节点child1指向root        child1 = QTreeWidgetItem(root)        # 给子节点第一列设置文本        child1.setText(0, "child1")        # 给子节点第二列设置文本        child1.setText(1, '1')        # 创建子节点        # 让子节点child2指向root        child2 = QTreeWidgetItem(root)        # 给子节点第一列设置文本        child2.setText(0, "child2")        # 给子节点第二列设置文本        child2.setText(1, '2')        # 创建子节点        # 让子节点child3指向child2        child3 = QTreeWidgetItem(child2)        # 给子节点第一列设置文本        child2.setText(0, "child3")        # 给子节点第二列设置文本        child2.setText(1, '3')        # 将树设置为中心控件,充满整个屏幕        # 这样在屏幕上就可以显示        # self.setCentralWidget(self.tree)        # 为树添加节点,用单击信号        self.tree.clicked.connect(self.onTreeClicked)        # 创建垂直布局        mainLayout = QVBoxLayout(self)        # 把按钮和树都放在垂直布局里        # 此时按钮在水平布局里面        mainLayout.addLayout(operatorLayout)        # # 添加控件        mainLayout.addWidget(self.tree)        # 应用于垂直布局        # self.setLayout(mainLayout)    # 槽方法    def onTreeClicked(self, index):        # 获得当前的单击项        item = self.tree.currentItem()        # 当前行        print(index.row())        # 输出当前单击节点的key        print('key=%s,value=%s' % (item.text(0), item.text(1)))    # 槽方法    def addNode(self):        print('添加节点')        # 获得当前的节点        item = self.tree.currentItem()        print(item)        # 动态创建节点,指定父节点        node = QTreeWidgetItem(item)        # 创建node的第一列        node.setText(0,'新节点')        node.setText(1,'新值')        # 创建node的第二列    def updateNode(self):        print('修改节点')        # 获得当前的节点        item = self.tree.currentItem()        item.setText(0,'修改节点')        item.setText(1,'值已经被修改')    def deleteNode(self):        print('删除节点')        # 获得当前的节点        item = self.tree.currentItem()        # 通过循环  得到当前选中的节点        # 获得不可见的根        root = self.tree.invisibleRootItem()        for item in self.tree.selectedItems():            #  item.parent()和root只要有一个不为空,就不会出错            (item.parent() or root).removeChild(item)if __name__ == '__main__':    app = QApplication(sys.argv)    main = ModifyTree()    main.show()    sys.exit(app.exec_())

效果展示:

75.QTreeView控件与系统定制模式

在table_tree文件夹里新建TreeView.py文件,执行代码:

"""QTreeView控件与系统定制模式与QTreeWidget的不同点: QTreeWiget装载数据的方式是通过Model,比如Model里面的QDirModel 用来显示当前操作系统的目录结构QTreeView  一般用于比较复杂的树"""import sysfrom PyQt5.QtWidgets import *from PyQt5.QtGui import *if __name__ == '__main__':    app = QApplication(sys.argv)    # 创建QDirModel控件    model = QDirModel()    # 创建QTreeView控件    tree = QTreeView()    # 设置model    tree.setModel(model)    # 把树作为一个窗口    tree.setWindowTitle('QTreeView')    # 设置树窗口的尺寸    tree.resize(600,400)    # 显示树    tree.show()    sys.exit(app.exec_())

效果展示:

76选项卡控件(QTableWidget)

新建containers文件夹,在containers文件夹里面新建TabWidgetDemo.py文件,执行代码:

"""选项卡控件:QTabWidget目的:在屏幕上显示更多的控件  在页面中显示多页面"""import sysfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *class TabWidgetDemo(QTabWidget):    def __init__(self,parent=None):        super(TabWidgetDemo, self).__init__(parent)        self.setWindowTitle('选项卡控件:QTabWidget')        self.resize(600,400)        # QTableView的最终父类是QWidget  将整个窗口作为一个tab        # 创建多个窗口  每个窗口可以放置多个控件        # 创建用于显示控件的窗口        # 创建窗口tab1        self.tab1 = QWidget()        # 创建窗口tab2        self.tab2 = QWidget()        # 创建窗口tab3        self.tab3 = QWidget()        # 把每个窗口和选项卡绑定        self.addTab(self.tab1,'选项卡1')        self.addTab(self.tab2,'选项卡2')        self.addTab(self.tab3,'选项卡3')        # 调用        self.tab1UI()        self.tab2UI()        self.tab3UI()    # 为每个选项卡单独编写一个方法    def tab1UI(self):        # 创建表单布局        layout = QFormLayout()        layout.addRow('姓名',QLineEdit())        layout.addRow('地址',QLineEdit())        self.setTabText(0,'联系方式')         # 装载        self.tab1.setLayout(layout)    def tab2UI(self):        layout = QFormLayout()        sex = QHBoxLayout()        sex.addWidget(QRadioButton('男'))        sex.addWidget(QRadioButton('女'))        layout.addRow(QLabel('性别'),sex)        layout.addRow('生日',QLineEdit())        self.setTabText(1,'个人详细信息')        self.tab2.setLayout(layout)    def tab3UI(self):        # 放置水平布局        layout = QHBoxLayout()        layout.addWidget(QLabel('科目'))        layout.addWidget(QCheckBox('物理'))        layout.addWidget(QCheckBox('高数'))        self.setTabText(2,'教育程序')        self.tab3.setLayout(layout)if __name__ == '__main__':    app =QApplication(sys.argv)    demo = TabWidgetDemo()    demo.show()    sys.exit(app.exec_())

效果展示:

77.堆栈窗口控件(QStakedWidget)

在containers文件夹里面新建QStakedWidget.py文件,执行代码:

"""堆栈窗口控件(QStackedWidget)通过切换来显示不同页的控件"""import sysfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *class StackedExample(QWidget):    def __init__(self):        super(StackedExample, self).__init__()        # 从屏幕宽500,高200的位置显示出一个宽300,高200的窗口        self.setGeometry(500,200,300,200)        self.setWindowTitle("堆栈窗口控件(QStackedWidget)")        # 放置列表控件        self.list = QListWidget()        # 在列表的第一列添加 "联系方式"        self.list.insertItem(0,"联系方式")        # 在列表的第二列添加  "个人信息"        self.list.insertItem(1,"个人信息")        # 在列表的第三列添加  "教育程序"        self.list.insertItem(2,"教育程度")        # 创建三个页面        self.stack1 = QWidget()        self.stack2 = QWidget()        self.stack3 = QWidget()        # 调用        self.tab1UI()        self.tab2UI()        self.tab3UI()        # 创建堆栈窗口对象        self.stack = QStackedWidget()        # 把这三个窗口添加到堆栈窗口里面        self.stack.addWidget(self.stack1)        self.stack.addWidget(self.stack2)        self.stack.addWidget(self.stack3)        # 创建水平布局 左侧显示列表  右侧显示堆栈页面        hbox = QHBoxLayout()        hbox.addWidget(self.list)        hbox.addWidget(self.stack)        # 应用于水平布局        self.setLayout(hbox)        # 为列表添加事件  当前行变化 信号 槽绑定        self.list.currentRowChanged.connect(self.display)    # 编写三个槽方法    def tab1UI(self):        layout = QFormLayout()        layout.addRow('姓名',QLineEdit())        layout.addRow('地址',QLineEdit())        self.stack1.setLayout(layout)    def tab2UI(self):        layout = QFormLayout()        sex = QHBoxLayout()        sex.addWidget(QRadioButton('男'))        sex.addWidget(QRadioButton('女'))        layout.addRow(QLabel('性别'),sex)        layout.addRow('生日',QLineEdit())        self.stack2.setLayout(layout)    def tab3UI(self):        layout = QHBoxLayout()        layout.addWidget(QLabel('科目'))        layout.addWidget(QCheckBox('物理'))        layout.addWidget(QCheckBox('高数'))        self.stack3.setLayout(layout)    def display(self,index):        # index 为当前项的变化        # 根据索引切换栈里面的页面        self.stack.setCurrentIndex(index)if __name__ == '__main__':    app = QApplication(sys.argv)    main = QStackedWidget()    main.show()    sys.exit(app.exec_())

效果展示:

windows环境不能展示,待后期填坑

78.停靠控件(QDockWidget)

在containers文件夹里面新建DockWidget.py文件,执行代码:

"""停靠控件 (QDockWidget)这是一个窗口 可以悬浮 可以拖动"""import sysfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *class DockDemo(QMainWindow):    def __init__(self,parent=None):        super(DockDemo, self).__init__(parent)        self.setWindowTitle('停靠控件 (QDockWidget)')        # 水平布局        layout = QHBoxLayout()        # 创建停靠控件        self.items = QDockWidget('Dockable',self)        # 创建列表控件        self.listWidget = QListWidget()        # 为列表控件添加item        self.listWidget.addItem('item1')        self.listWidget.addItem('item2')        self.listWidget.addItem('item3')        # 将列表控件放到停靠(控件)窗口里面        self.items.setWidget(self.listWidget)        # 设置中心窗口        self.setCentralWidget(QLineEdit())        # 添加停靠窗口  在右侧        self.addDockWidget(Qt.RightDockWidgetArea,self.items)        # 默认为停靠状态,可以设置为悬浮        self.items.setFloating(True)if __name__ == '__main__':    app = QApplication(sys.argv)    demo = DockDemo()    demo.show()    sys.exit(app.exec_())

效果展示:

79.容纳多文档的窗口

在containers文件夹里面新建MultiWindows.py文件,执行代码:

"""容纳多文档的窗口QMdiArea  容纳多文档类QMdiSubWindow  多文档窗口类# 父窗口可以创建多个子窗口,子窗口不能离开父窗口"""import sysfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *class MultiWindows(QMainWindow):    # 记录一下当前的窗口    count = 0    def __init__(self,parent=None):        super(MultiWindows, self).__init__(parent)        self.setWindowTitle("容纳多文档的窗口")        # 多文档有两种排列方式 一种是平铺,一种是层叠        # 创建容纳多文档对象        self.mdi = QMdiArea()        # 把多文档对象添加到布局里面        self.setCentralWidget(self.mdi)        # 创建一个菜单        bar = self.menuBar()        # 添加一个文件菜单        file = bar.addMenu("File")        # 给文件菜单添加动作 "New"        file.addAction("New")        # 设置窗口的排列方式        # 层叠        file.addAction("cascade")        # 平铺        file.addAction("Tiled")        # 连接菜单动作,触发信号        file.triggered.connect(self.windowaction)    # 槽方法    def windowaction(self,q):        print(q.text())        # q 是当前单击的菜单项        if q.text() == "New":            # 记录一下            MultiWindows.count = MultiWindows.count + 1            # 创建一个子窗口            sub = QMdiSubWindow()            # 在子窗口里面放置控件            sub.setWidget(QTextEdit())            # 设置子窗口的标题            sub.setWindowTitle('子窗口' + str(MultiWindows.count))            # 添加子窗口            self.mdi.addSubWindow(sub)            # 显示子窗口            sub.show()        elif q.text() == "cascade":            # 设置层叠方式            self.mdi.cascadeSubWindows()        elif q.text() == "Tiled":            # 设置平铺方式            self.mdi.tileSubWindows()if __name__ == '__main__':    app = QApplication(sys.argv)    demo = MultiWindows()    demo.show()    sys.exit(app.exec_())

效果展示:

80.滚动条控件(QScrollBar)

在containers文件夹里面新建ScrollBar.py文件,执行代码:

"""滚动条控件(QScrollBar)本身不是容器,但是可以起到容器的作用QScrollBar的作用:1.通过滚动条值的变化控制其他控件状态的变化2.通过滚动条值的变化控制控件位置的变化"""# 用三个滚动条控件控制文本的颜色变化# 用一个滚动条控件控制QLableEdit控件的上下移动import sysfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *class ScrollBar(QWidget):    def __init__(self):        super(ScrollBar, self).__init__()        self.initUI()    def initUI(self):        # 创建水平布局        hbox = QHBoxLayout()        # 创建label,用来控制文本的颜色以及移动        self.label = QLabel('拖动滚动条去改变文字颜色')        # 把label添加到水平布局里        hbox.addWidget(self.label)        # 创建三个滚动条控件        # 创建第一个滚动条        self.scrollbar1 = QScrollBar()        # 设置第一个滚动条的最大值    最小为0        self.scrollbar1.setMaximum(255)        # 设置信号 滚动条移动  这三个滚动条都使用同一个槽        self.scrollbar1.sliderMoved.connect(self.sliderMoved)        # 创建第二个滚动条        self.scrollbar2 = QScrollBar()        # 设置第一个滚动条的最大值    最小为0        self.scrollbar2.setMaximum(255)        # 设置信号 滚动条移动  这三个滚动条都使用同一个槽        self.scrollbar2.sliderMoved.connect(self.sliderMoved)        # 创建第三个滚动条        self.scrollbar3 = QScrollBar()        # 设置第一个滚动条的最大值    最小为0        self.scrollbar3.setMaximum(255)        # 设置信号 滚动条移动  这三个滚动条都使用同一个槽        self.scrollbar3.sliderMoved.connect(self.sliderMoved)        # 创建第四个滚动条   用来移动位置        self.scrollbar4 = QScrollBar()        # 设置第一个滚动条的最大值    最小为0        self.scrollbar4.setMaximum(255)        # 设置信号 滚动条移动  这三个滚动条都使用同一个槽        self.scrollbar4.sliderMoved.connect(self.sliderMoved1)        # 把这三个滚动条都添加到水平布局里        hbox.addWidget(self.scrollbar1)        hbox.addWidget(self.scrollbar2)        hbox.addWidget(self.scrollbar3)        hbox.addWidget(self.scrollbar4)        # 设置当前窗口的位置坐标        # 距离屏幕宽300,高300的位置,创建一个宽300高200的窗口        self.setGeometry(300,300,300,200)        # 应用于水平布局        self.setLayout(hbox)        # 保留当前的坐标   用来移动位置        self.y = self.label.pos().y()    # 槽方法    def sliderMoved(self):        # 打印当前设的值        print(self.scrollbar1.value(),self.scrollbar2.value(),self.scrollbar3.value())        # 设置调试板        palette = QPalette()        # 设置颜色        c = QColor(self.scrollbar1.value(),self.scrollbar2.value(),self.scrollbar3.value(),255)        palette.setColor(QPalette.Foreground,c)        self.label.setPalette(palette)    # 用button4演示移动    def sliderMoved1(self):        # x轴坐标不变,用来垂直移动        self.label.move(self.label.x(),self.y + self.scrollbar4.value())if __name__ == '__main__':    app = QApplication(sys.argv)    demo= ScrollBar()    demo.show()    sys.exit(app.exec_())

效果展示:

81.动态显示当前时间

涉及到PyQt5的多线程

新建multithread文件夹,在multithread文件夹里新建ShowTime.py文件,执行代码:

"""动态显示当前时间QTimer  定时器  每隔一定时间会调用一次QThread多线程用于同时完成多个任务    在单CPU上是按顺序完成的(时间片切换),从宏观上来看,还是同时完成的                         在多CPU上,是可以真正的同时完成"""import sysfrom PyQt5.QtWidgets import QWidget,QPushButton,QApplication,QListWidget,QGridLayout,QLabelfrom PyQt5.QtCore import QTimer,QDateTimeclass ShowTime(QWidget):    def __init__(self,parent=None):        super(ShowTime, self).__init__(parent)        # 设置窗口标题        self.setWindowTitle("动态显示当前时间")        # 创建QLabel控件        self.label = QLabel('显示当前时间')        # 创建button按扭        self.startBtn = QPushButton('开始')        # 创建button按钮        self.endBtn = QPushButton('结束')        # 通过栅格布局,安排这三个控件的位置        layout = QGridLayout()        # 设置定时器对象        self.timer = QTimer()        # 时间的 信号 槽        self.timer.timeout.connect(self.showTime)        # 把这三个控件放到栅格布局里面        # 在第一行第一列   占用一行  占用两列        layout.addWidget(self.label,0,0,1,2)        # 在第二行第一列        layout.addWidget(self.startBtn,1,0)        # 在第二行第二列        layout.addWidget(self.endBtn,1,1)        # 开始控件的信号 槽        self.startBtn.clicked.connect(self.startTimer)        # 结束控件的信号 槽        self.endBtn.clicked.connect(self.endTimer)        # 应用于栅格布局        self.setLayout(layout)    # 槽方法    # 显示时间    def showTime(self):        # 获取当前的时间        time = QDateTime.currentDateTime()        # 设置时间显示        timeDisplay = time.toString("yyyy-MM-dd hh:mm:ss dddd")        self.label.setText(timeDisplay)    def startTimer(self):        # 开始时间 1s        self.timer.start(1000)        # 开始之后开始按钮关闭        self.startBtn.setEnabled(False)        # 开始之后关闭按钮开始        self.endBtn.setEnabled(True)    def endTimer(self):        self.timer.stop()        # 开始之后开始按钮开始        self.startBtn.setEnabled(True)        # 开始之后关闭按钮关闭        self.endBtn.setEnabled(False)if __name__ == '__main__':    app = QApplication(sys.argv)    demo = ShowTime()    demo.show()    sys.exit(app.exec_())

效果展示:

82.让程序定时关闭

在multithread文件夹里新建AutoCloseWindow.py文件,执行代码:

"""让程序定时关闭QTimer.singleShot    在指定时间后只调用一次"""import sysfrom PyQt5.QtWidgets import *from PyQt5.QtGui import *from PyQt5.QtCore import *if __name__ == '__main__':    app = QApplication(sys.argv)    label =  QLabel("Hello World,窗口在5秒后自动关闭!")    label.setWindowFlags(Qt.SplashScreen | Qt.FramelessWindowHint)    label.show()    # 设置五秒    QTimer.singleShot(5000,app.quit)    sys.exit(app.exec_())

效果展示:

83.使用线程类(QThread)编写计数器

在multithread文件夹里新建Counter.py文件,执行代码:

"""使用线程类(QThread)编写计数器基本原理QThread派生一个子类在这个子类里面定义一个run方法def run(self):    while True:    # 每循环一次,休眠一秒钟        self.sleep(1)        # 当前循环等于5,直接退出        if sec == 5:            break;QLCDNumber控件WorkThread(QThread)用到自定义信号"""import sysfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *# 定义一个变量sec = 0class WorkThread(QThread):    timer = pyqtSignal()   # 每隔1秒发送一次信号    end = pyqtSignal()     # 计数完成后发送一次信号    def run(self):        while True:            self.sleep(1)  # 休眠1秒            if sec == 5:                self.end.emit() # 发送end信号                break            self.timer.emit()  # 发送timer信号class Counter(QWidget):    def __init__(self,parent=None):        super(Counter, self).__init__(parent)        self.setWindowTitle("使用线程类(QThread)编写计数器")        self.resize(300,200)        # 创建垂直布局        layout = QVBoxLayout()        self.lcdNumber = QLCDNumber()        layout.addWidget(self.lcdNumber)        button = QPushButton('开始计数')        layout.addWidget(button)        # 创建工作线程对象        self.workThread = WorkThread()        # 绑定 信号 槽        self.workThread.timer.connect(self.countTime)        self.workThread.end.connect(self.end)        # 槽和按钮的单击事件        button.clicked.connect(self.work)        # 应用于垂直布局        self.setLayout(layout)    # 槽方法    def countTime(self):        # global 声明全局变量        global sec        sec += 1        self.lcdNumber.display(sec)    def end(self):        QMessageBox.information(self,'消息','计数结束',QMessageBox.Ok)    def work(self):        self.workThread.start()if __name__ == '__main__':    app = QApplication(sys.argv)    demo =Counter()    demo.show()    sys.exit(app.exec_())

效果展示:

84.用Web浏览器控制(QWebEngineView)显示网页

新建web文件夹,在web文件夹里新建WebEngineView.py文件,执行代码:

"""用Web浏览器控件(QWebEngineView)显示网页PyQt5和Web的交互技术同时使用Python和web开发程序,混合开发Python + JavaScript + HTML5 + CSSQWebEngineView 控件,用来显示Web交互界面"""import sysfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *from PyQt5.QtWebEngineWidgets import *class WebEngineView(QMainWindow):    def __init__(self):        super(WebEngineView, self).__init__()        self.setWindowTitle('打开外部网页例子')        # 在距屏幕宽5px,高30px的坐标,创建一个宽1355,高730的窗口        self.setGeometry(5,30,1355,730)        self.browser = QWebEngineView()        self.browser.load(QUrl('https://www.baidu.com/'))        self.setCentralWidget(self.browser)if __name__ == '__main__':    app = QApplication(sys.argv)    win = WebEngineView()    win.show()    sys.exit(app.exec_())

运行过程中,遇到了:No module named ‘PyQt5.QtWebEngineWidgets’

解决办法:

【方法一】 指定安装5.10.1版本的pyqt5

pip install pyqt5==5.10.1

【方法二】 单独安装WebEngine,安装命令为:

pip install PyQtWebEngine

效果展示:

85.装载本地Web页面

在web文件夹新建test.html文件,添加代码如下:

        测试    

Hello PyQt5!

晚上好 幸苦了

在web文件夹里新建LocalHTML.py文件,执行代码:

"""装在本地Web页面"""import sysimport osfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *from PyQt5.QtWebEngineWidgets import *class WebEngineView(QMainWindow):    def __init__(self):        super(WebEngineView, self).__init__()        self.setWindowTitle("装载本地Web页面")        self.setGeometry(50,50,1355,730)        url = os.getcwd() + '/test.html'        self.browser =  QWebEngineView()        self.browser.load(QUrl.fromLocalFile(url))        self.setCentralWidget(self.browser)        print(os.getcwd())if __name__ == '__main__':    app =    QApplication(sys.argv)    demo = WebEngineView()    demo.show()    sys.exit(app.exec_())

效果展示:

86.显示嵌入Web页面

在new文件里新建InnerHTML.py文件,执行代码:

"""显示嵌入Web页面"""import sysfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *from PyQt5.QtWebEngineWidgets import *class InnerHTML(QMainWindow):    def __init__(self):        super(InnerHTML, self).__init__()        self.setWindowTitle('显示嵌入Web页面')        self.setGeometry(5,30,1355,730)        self.browsesr = QWebEngineView()        self.browsesr.setHtml(            """                    测试显示    

Hello PyQt5!

显示Web页面 幸苦了 """ ) # 设置成中心控件 self.setCentralWidget(self.browsesr)if __name__ == '__main__': app =QApplication(sys.argv) demo = InnerHTML() demo.show() sys.exit(app.exec_())

效果展示:

总结:PyQt5支持的三种装载web页面的方式:

1.通过标准的QUrl

2.从本地装载Qurl.fromLocalFile(url)

3.用setHtml直接装载HTML

87.PyQt5调用JavaScript代码

在web文件夹里新建demo1.html文件,添加如下代码:

        测试页面            function fullname(value) {            alert("")            var firstname = document.getElementById('firstname').value;            var lastname = document.getElementById('lastname').value;            var fullname = firstname + '' + lastname;            document.getElementById('fullname').value = fullname;            document.getElementById('submit-btn').style.display = "block";            return fullname;        }        First Name:        
First Name:
First Name:

在web文件夹里新建PyQtCallJS.py文件,执行代码:

"""PyQt5调用JavaScript代码PyQt5和JavaScript交互PyQt5和JavaScript互相调用,互相传输数据"""import sysimport osfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWidgets import *from PyQt5.QtWebEngineWidgets import *class PyQtCallJS(QWidget):    def __init__(self):        super(PyQtCallJS, self).__init__()        self.setWindowTitle('PyQt5调用JavaScript')        self.setGeometry(5,30,1355,730)        # 设置垂直布局        self.layout = QVBoxLayout()        # 应用于垂直布局        self.setLayout(self.layout)        # 设置Web页面控件        self.browser =  QWebEngineView()        url = os.getcwd() + '/demo1.html'        self.browser.load(QUrl.fromLocalFile(url))        # 把web控件放到布局里        self.layout.addWidget(self.browser)        button = QPushButton('设置全名')        self.layout.addWidget(button)        # 槽和信号绑定        button.clicked.connect(self.fullname)    # 添加按钮的单击事件    # 前两个框自己输入,最后一个框自动相加    def fullname(self):        self.value = 'hello world'        self.browser.page().runJavaScript('fullname("' + self.value +'");',self.js_callback)    # 通过回调函数返回值    def js_callback(self,result):        print(result)if __name__ == '__main__':    app = QApplication(sys.argv)    demo = PyQtCallJS()    demo.show()    sys.exit(app.exec_())

效果如下:

88.JavaScript调用PythonAPI计算阶乘

在web文件夹里新建qwebchannel.is文件,添加代码:

/******************************************************************************** Copyright (C) 2016 The Qt Company Ltd.** Copyright (C) 2016 Klar채lvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff ** Contact: https://www.qt.io/licensing/**** This file is part of the QtWebChannel module of the Qt Toolkit.**** $QT_BEGIN_LICENSE:LGPL$** Commercial License Usage** Licensees holding valid commercial Qt licenses may use this file in** accordance with the commercial license agreement provided with the** Software or, alternatively, in accordance with the terms contained in** a written agreement between you and The Qt Company. For licensing terms** and conditions see https://www.qt.io/terms-conditions. For further** information use the contact form at https://www.qt.io/contact-us.**** GNU Lesser General Public License Usage** Alternatively, this file may be used under the terms of the GNU Lesser** General Public License version 3 as published by the Free Software** Foundation and appearing in the file LICENSE.LGPL3 included in the** packaging of this file. Please review the following information to** ensure the GNU Lesser General Public License version 3 requirements** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.**** GNU General Public License Usage** Alternatively, this file may be used under the terms of the GNU** General Public License version 2.0 or (at your option) the GNU General** Public license version 3 or any later version approved by the KDE Free** Qt Foundation. The licenses are as published by the Free Software** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3** included in the packaging of this file. Please review the following** information to ensure the GNU General Public License requirements will** be met: https://www.gnu.org/licenses/gpl-2.0.html and** https://www.gnu.org/licenses/gpl-3.0.html.**** $QT_END_LICENSE$******************************************************************************/"use strict";var QWebChannelMessageTypes = {    signal: 1,    propertyUpdate: 2,    init: 3,    idle: 4,    debug: 5,    invokeMethod: 6,    connectToSignal: 7,    disconnectFromSignal: 8,    setProperty: 9,    response: 10,};var QWebChannel = function(transport, initCallback){    if (typeof transport !== "object" || typeof transport.send !== "function") {        console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." +                      " Given is: transport: " + typeof(transport) + ", transport.send: " + typeof(transport.send));        return;    }    var channel = this;    this.transport = transport;    this.send = function(data)    {        if (typeof(data) !== "string") {            data = JSON.stringify(data);        }        channel.transport.send(data);    }    this.transport.onmessage = function(message)    {        var data = message.data;        if (typeof data === "string") {            data = JSON.parse(data);        }        switch (data.type) {            case QWebChannelMessageTypes.signal:                channel.handleSignal(data);                break;            case QWebChannelMessageTypes.response:                channel.handleResponse(data);                break;            case QWebChannelMessageTypes.propertyUpdate:                channel.handlePropertyUpdate(data);                break;            default:                console.error("invalid message received:", message.data);                break;        }    }    this.execCallbacks = {};    this.execId = 0;    this.exec = function(data, callback)    {        if (!callback) {            // if no callback is given, send directly            channel.send(data);            return;        }        if (channel.execId === Number.MAX_VALUE) {            // wrap            channel.execId = Number.MIN_VALUE;        }        if (data.hasOwnProperty("id")) {            console.error("Cannot exec message with property id: " + JSON.stringify(data));            return;        }        data.id = channel.execId++;        channel.execCallbacks[data.id] = callback;        channel.send(data);    };    this.objects = {};    this.handleSignal = function(message)    {        var object = channel.objects[message.object];        if (object) {            object.signalEmitted(message.signal, message.args);        } else {            console.warn("Unhandled signal: " + message.object + "::" + message.signal);        }    }    this.handleResponse = function(message)    {        if (!message.hasOwnProperty("id")) {            console.error("Invalid response message received: ", JSON.stringify(message));            return;        }        channel.execCallbacks[message.id](message.data);        delete channel.execCallbacks[message.id];    }    this.handlePropertyUpdate = function(message)    {        message.data.forEach(data => {            var object = channel.objects[data.object];            if (object) {                object.propertyUpdate(data.signals, data.properties);            } else {                console.warn("Unhandled property update: " + data.object + "::" + data.signal);            }        });        channel.exec({type: QWebChannelMessageTypes.idle});    }    this.debug = function(message)    {        channel.send({type: QWebChannelMessageTypes.debug, data: message});    };    channel.exec({type: QWebChannelMessageTypes.init}, function(data) {        for (const objectName of Object.keys(data)) {            new QObject(objectName, data[objectName], channel);        }        // now unwrap properties, which might reference other registered objects        for (const objectName of Object.keys(channel.objects)) {            channel.objects[objectName].unwrapProperties();        }        if (initCallback) {            initCallback(channel);        }        channel.exec({type: QWebChannelMessageTypes.idle});    });};function QObject(name, data, webChannel){    this.__id__ = name;    webChannel.objects[name] = this;    // List of callbacks that get invoked upon signal emission    this.__objectSignals__ = {};    // Cache of all properties, updated when a notify signal is emitted    this.__propertyCache__ = {};    var object = this;    // ----------------------------------------------------------------------    this.unwrapQObject = function(response)    {        if (response instanceof Array) {            // support list of objects            return response.map(qobj => object.unwrapQObject(qobj))        }        if (!(response instanceof Object))            return response;        if (!response["__QObject*__"] || response.id === undefined) {            var jObj = {};            for (const propName of Object.keys(response)) {                jObj[propName] = object.unwrapQObject(response[propName]);            }            return jObj;        }        var objectId = response.id;        if (webChannel.objects[objectId])            return webChannel.objects[objectId];        if (!response.data) {            console.error("Cannot unwrap unknown QObject " + objectId + " without data.");            return;        }        var qObject = new QObject( objectId, response.data, webChannel );        qObject.destroyed.connect(function() {            if (webChannel.objects[objectId] === qObject) {                delete webChannel.objects[objectId];                // reset the now deleted QObject to an empty {} object                // just assigning {} though would not have the desired effect, but the                // below also ensures all external references will see the empty map                // NOTE: this detour is necessary to workaround QTBUG-40021                Object.keys(qObject).forEach(name => delete qObject[name]);            }        });        // here we are already initialized, and thus must directly unwrap the properties        qObject.unwrapProperties();        return qObject;    }    this.unwrapProperties = function()    {        for (const propertyIdx of Object.keys(object.__propertyCache__)) {            object.__propertyCache__[propertyIdx] = object.unwrapQObject(object.__propertyCache__[propertyIdx]);        }    }    function addSignal(signalData, isPropertyNotifySignal)    {        var signalName = signalData[0];        var signalIndex = signalData[1];        object[signalName] = {            connect: function(callback) {                if (typeof(callback) !== "function") {                    console.error("Bad callback given to connect to signal " + signalName);                    return;                }                object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];                object.__objectSignals__[signalIndex].push(callback);                // only required for "pure" signals, handled separately for properties in propertyUpdate                if (isPropertyNotifySignal)                    return;                // also note that we always get notified about the destroyed signal                if (signalName === "destroyed" || signalName === "destroyed()" || signalName === "destroyed(QObject*)")                    return;                // and otherwise we only need to be connected only once                if (object.__objectSignals__[signalIndex].length == 1) {                    webChannel.exec({                        type: QWebChannelMessageTypes.connectToSignal,                        object: object.__id__,                        signal: signalIndex                    });                }            },            disconnect: function(callback) {                if (typeof(callback) !== "function") {                    console.error("Bad callback given to disconnect from signal " + signalName);                    return;                }                object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];                var idx = object.__objectSignals__[signalIndex].indexOf(callback);                if (idx === -1) {                    console.error("Cannot find connection of signal " + signalName + " to " + callback.name);                    return;                }                object.__objectSignals__[signalIndex].splice(idx, 1);                if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) {                    // only required for "pure" signals, handled separately for properties in propertyUpdate                    webChannel.exec({                        type: QWebChannelMessageTypes.disconnectFromSignal,                        object: object.__id__,                        signal: signalIndex                    });                }            }        };    }    /**     * Invokes all callbacks for the given signalname. Also works for property notify callbacks.     */    function invokeSignalCallbacks(signalName, signalArgs)    {        var connections = object.__objectSignals__[signalName];        if (connections) {            connections.forEach(function(callback) {                callback.apply(callback, signalArgs);            });        }    }    this.propertyUpdate = function(signals, propertyMap)    {        // update property cache        for (const propertyIndex of Object.keys(propertyMap)) {            var propertyValue = propertyMap[propertyIndex];            object.__propertyCache__[propertyIndex] = this.unwrapQObject(propertyValue);        }        for (const signalName of Object.keys(signals)) {            // Invoke all callbacks, as signalEmitted() does not. This ensures the            // property cache is updated before the callbacks are invoked.            invokeSignalCallbacks(signalName, signals[signalName]);        }    }    this.signalEmitted = function(signalName, signalArgs)    {        invokeSignalCallbacks(signalName, this.unwrapQObject(signalArgs));    }    function addMethod(methodData)    {        var methodName = methodData[0];        var methodIdx = methodData[1];        // Fully specified methods are invoked by id, others by name for host-side overload resolution        var invokedMethod = methodName[methodName.length - 1] === ')' ? methodIdx : methodName        object[methodName] = function() {            var args = [];            var callback;            var errCallback;            for (var i = 0; i  

在web文件夹里新建h.html文件,添加代码:

        A Demo Page                    function callback(result) {            alert("计算结果:" + result)        }        document.addEventListener("DOMContentLoaded",function () {            new QWebChannel( qt.webChannelTransport, function (channel) {                window.obj = channel.objects.obj;            });        });            function  onFactorial() {                if ( window.obj) {                    var n = parseInt(document.getElementById('n').value);                    window.obj.factorial(n,callback)                }            }                请输入N:                

在web文件夹里新建factorical.py文件,添加代码:

"""用Python语言编写计算阶乘的类"""from PyQt5.QtCore import *class Factorial(QObject):    @pyqtSlot(int,result=int)    def factorial(self,n):        if n == 0 or n == 1:            return 1        else:            return self.factorial(n-1)* n

在web文件夹里新建PyFactorial.py文件,执行代码:

"""JavaScript调用Python函数计算阶乘基本原理将Python的对象映射到JavaScript中,通过映射到JavaScript的对象,来调用Python对象的方法或者函数将槽函数映射到JavaScript中在Python类中定义若干个槽函数系统就会把槽函数连同JavaScript对象一起映射到JavaScript里面调用JS,都是采用异步的方式 加一个回调  window.obj.factorial(n,callback)"""import sysimport osfrom PyQt5.QtCore import *from PyQt5.QtGui import *from PyQt5.QtWebChannel import QWebChannelfrom PyQt5.QtWidgets import *from PyQt5.QtWebEngineWidgets import *from web.factorial import *channel =QWebChannel()factorial = Factorial()class PyFactorial(QWidget):    def __init__(self):        super(PyFactorial, self).__init__()        self.setWindowTitle('Python计算阶乘')        self.resize(600,300)        layout = QVBoxLayout()        self.browser = QWebEngineView()        url = os.getcwd() + '/h.html'        self.browser.load(QUrl.fromLocalFile(url))        channel.registerObject("obj",factorial)        self.browser.page().setWebChannel(channel)        layout.addWidget(self.browser)        self.setLayout(layout)if __name__ == '__main__':    app = QApplication(sys.argv)    demo = PyFactorial()    demo.show()    sys.exit(app.exec_())

效果展示:

89.绝对布局

新建layout文件夹,在layout文件夹里面新建AbsoluteLayout.py文件,执行代码:

"""绝对布局"""import sys,mathfrom PyQt5.QtWidgets import *class AbsoluteLayout(QWidget):    def __init__(self):        super(AbsoluteLayout, self).__init__()        self.setWindowTitle('绝对布局')        self.label1 = QLabel('欢迎',self)        self.label1.move(15,20)        self.label2 = QLabel('学习',self)        self.label2.move(20,40)        self.label3 = QLabel('PyQt5',self)        self.label3.move(30,80)if __name__ == '__main__':    app = QApplication(sys.argv)    demo = AbsoluteLayout()    demo.show()    sys.exit(app.exec_())

效果展示:

90.水平盒布局

在layout文件夹里面新建HBoxLayout.py文件,执行代码:

"""水平盒布局(QHBoxLayout)"""import sys,mathfrom PyQt5.QtWidgets import *class HBoxLayout(QWidget):    def __init__(self):        super(HBoxLayout, self).__init__()        self.setWindowTitle('水平盒布局')        # 创建水平盒布局        hlayout = QHBoxLayout()        # 往布局里添加按钮控件        hlayout.addWidget(QPushButton('按钮1'))        hlayout.addWidget(QPushButton('按钮2'))        hlayout.addWidget(QPushButton('按钮3'))        hlayout.addWidget(QPushButton('按钮4'))        hlayout.addWidget(QPushButton('按钮5'))        # 此时按钮就会在水平方向等距的排列        # 设置控件之间的间距        hlayout.setSpacing(40)        # 应用水平盒布局        self.setLayout(hlayout)if __name__ == '__main__':    app = QApplication(sys.argv)    demo = HBoxLayout()    demo.show()    sys.exit(app.exec_())

效果展示:

91.设置控件的对齐方式

在layout文件夹里面新建HBoxLayoutAlign.py文件,执行代码:

"""设置控件的对齐方式左对齐  右对齐  顶端对齐  底端对齐"""import sys,mathfrom PyQt5.QtWidgets import *from PyQt5.QtCore import Qtclass HBoxLayoutAlign(QWidget):    def __init__(self):        super(HBoxLayoutAlign, self).__init__()        self.setWindowTitle('设置控件的对齐方式')        # 创建水平盒布局        hlayout = QHBoxLayout()        # 往布局里添加按钮控件        # 按钮1设置左对齐 顶端对齐        hlayout.addWidget(QPushButton('按钮1'),1,Qt.AlignLeft | Qt.AlignTop)        hlayout.addWidget(QPushButton('按钮2'),2,Qt.AlignLeft | Qt.AlignTop)        hlayout.addWidget(QPushButton('按钮3'))        hlayout.addWidget(QPushButton('按钮4'),1,Qt.AlignLeft | Qt.AlignBottom)        hlayout.addWidget(QPushButton('按钮5'),1,Qt.AlignLeft | Qt.AlignBottom)        # 此时按钮就会在水平方向等距的排列        # 设置控件之间的间距        hlayout.setSpacing(40)        # 应用水平盒布局        self.setLayout(hlayout)if __name__ == '__main__':    app = QApplication(sys.argv)    demo = HBoxLayoutAlign()    demo.show()    sys.exit(app.exec_())

效果展示:

92.垂直盒布局

在layout文件夹里面新建VBoxLayout.py文件,执行代码:

"""垂直盒布局(QVBoxLayout)"""import sys,mathfrom PyQt5.QtWidgets import *class HVoxLayout(QWidget):    def __init__(self):        super(HVoxLayout, self).__init__()        self.setWindowTitle('垂直盒布局')        # 创建水平盒布局        hlayout = QVBoxLayout()        # 往布局里添加按钮控件        hlayout.addWidget(QPushButton('按钮1'))        hlayout.addWidget(QPushButton('按钮2'))        hlayout.addWidget(QPushButton('按钮3'))        hlayout.addWidget(QPushButton('按钮4'))        hlayout.addWidget(QPushButton('按钮5'))        # 此时按钮就会在水平方向等距的排列        # 设置控件之间的间距        hlayout.setSpacing(20)        # 应用水平盒布局        self.setLayout(hlayout)if __name__ == '__main__':    app = QApplication(sys.argv)    demo = HVoxLayout()    demo.show()    sys.exit(app.exec_())

效果展示:

92.设置伸缩量(addStretch)

在layout文件夹里面新建Stretch.py文件,执行代码:

"""设置伸缩量(addStretch)有多种方式,HBoxLayoutAlign.py中hlayout.addWidget(QPushButton('按钮1'),1,Qt.AlignLeft | Qt.AlignTop) 中的第二个参数 1 就是伸缩量"""import sysfrom PyQt5.QtCore import *from PyQt5.QtWidgets import *class Stretch(QWidget):    def __init__(self):        super(Stretch, self).__init__()        self.setWindowTitle("设置伸缩量")        self.resize(800,400)        # 添加三个按钮        btn1 = QPushButton(self)        btn2 = QPushButton(self)        btn3 = QPushButton(self)        btn4 = QPushButton(self)        btn5 = QPushButton(self)        # 分别设置文本        btn1.setText('按钮1')        btn2.setText('按钮2')        btn3.setText('按钮3')        btn4.setText('按钮4')        btn5.setText('按钮5')        # 放置水平布局        layout = QHBoxLayout()        # 把三个按钮添加到布局里        layout.addStretch(0)        layout.addWidget(btn1)        layout.addWidget(btn2)        layout.addWidget(btn3)        layout.addWidget(btn4)        layout.addWidget(btn5)        btnOK = QPushButton(self)        btnOK.setText("确定")        layout.addStretch(1)        layout.addWidget(btnOK)        btnCancel = QPushButton(self)        btnCancel.setText("取消")        layout.addStretch(2)        layout.addWidget(btnCancel)        # 应用于水平布局        self.setLayout(layout)if __name__ == '__main__':    app = QApplication(sys.argv)    demo = Stretch()    demo.show()    sys.exit(app.exec_())

效果展示:

93.让按钮永远在窗口的右下角

在layout文件夹里面新建RightBottomButton.py文件,执行代码:

"""让按钮永远在窗口右下角基本原理:一分为二界面上面任意布局按钮放在水平布局里面"""import sysfrom PyQt5.QtWidgets import *from PyQt5.QtCore import *class RightBottomButton(QWidget):    def __init__(self):        super(RightBottomButton, self).__init__()        self.setWindowTitle('让按钮永远在右下角')        self.resize(400,300)        # 添加两个按钮        okButton = QPushButton("确定")        cancelButton = QPushButton("取消")        # 设置水平盒布局        hbox = QHBoxLayout()        hbox.addStretch(1)        hbox.addWidget(okButton)        hbox.addWidget(cancelButton)        # 设置垂直盒布局        vbox = QVBoxLayout()        btn1 = QPushButton('按钮1')        btn2 = QPushButton('按钮2')        btn3 = QPushButton('按钮3')        btn4 =  QPushButton('按钮4')        btn5 = QPushButton('按钮5')        vbox.addStrut(0)        vbox.addWidget(btn1)        vbox.addWidget(btn2)        vbox.addWidget(btn3)        vbox.addWidget(btn4)        vbox.addWidget(btn5)        # 把水平盒布局添加到垂直盒布局里        vbox.addStrut(2)        vbox.addLayout(hbox)        # 应用于垂直盒布局        self.setLayout(vbox)if __name__ == '__main__':    app = QApplication(sys.argv)    demo = RightBottomButton()    demo.show()    sys.exit(app.exec_())

效果展示:

未完待续…

上一篇:发帖为什么要审核?

下一篇:实用指南,撰写卓越在校表现报告的技巧与要点!