PyQt5 学习笔记(tkinter 与 PyQt5 的比对)
本帖最后由 jumpbull 于 2024-11-1 20:38 编辑发帖时没注意,二楼和三楼弄反了,大家将就一下了!
一、tkinter 与 PyQt5 的个人见解
刚开始学 python 时,很多人都会选用 tkinter,因为能直接使用,而 PyQt5 还需要安装,其带的设计器又藏得很深,更是让初学者望而却步。后面用惯了 tkinter 觉得够用,也就不想换了。我就是典型的例子,直到最近空闲了主动去学习 PyQt5 后,才后悔没早点使用。
这篇文章比较适合还在使用 tkinter 的人,当然准备入手 PyQt5 的也可以看看,了解一下它们的差异还是很有必要的。
tkinter 与 PyQt5 的区别网上到处都有说,这里就不再细究了,就直接说自己的感受吧。如果你还在使用 tkinter的,或者准备入手学习 tkinter 的,请抓紧时间向 pyQt5 转型吧,PyQt5 入门的难度并不大,与 tkinter 差不多,但对窗口、小部件的控制能力比 tkinter 强太多了,而且也提供了 QtDesigner 界面设计工具,可以实现界面也代码分离,能随时修改软件界面而不影响逻辑代码。
我用 tkinter 写了不少工具,如果当时使用 PyQt5 的话,肯定能多出更多的功能,界面也更加好用。还是要找时间把工具升级成 pyQt5 版本的了。
二、PyQt5 安装使用步骤
原没有学习 PyQt5 的原因就是找不到好的教程,零零散散地接触了一些,没发现比 tkinter 好用就放弃了。为了不让大家走我的老路,我就 PyQt5 从安装到使用还有一些常用的语句方法进行总结,再与 tkinter 进行对比。
我用的编写环境是微软的 VSCode ,使用其他环境的也没关系,使用方法都一样的。
1、安装PyQt5
安装很简单,在PowerShell 命令行中执行下面命令即可(在开始菜单中右键即可找到,Win11 改为终端管理员)
pip install PyQt5-i https://pypi.tuna.tsinghua.edu.cn/simple
pip install pyqt5-tools-i https://pypi.tuna.tsinghua.edu.cn/simple
第一行是安装 PyQt5 模块,第二行是安装其工具,如 QtDesigner
后面的 -i https://pypi.tuna.tsinghua.edu.cn/simple 表示从国内镜像服务器下载,速度不是一般的快
与软件安装不同的是,QtDesigner 安装完后并不会有快捷键什么的,需要自己手动创建,不然每次使用都要找半天。QtDesigner 位于 Lib\site-packages\qt5_applications\Qt\bin 目录中,找到 designer.exe 文件,可以添加到开始菜单中或在桌面创建快捷键。其完整的路径如下:
C:\Users\xxxx\AppData\Local\Programs\Python\Python311\Lib\site-packages\qt5_applications\Qt\bin
xxxx 表示系统当前的用户名
这时你已经可以使用 PyQt5了,刚开始学习时,不建议马上用 QtDesigner ,建议用代码试着写几个小程序试试,毕竟 PyQt5 与 tkinter 使用的方法差别太大,完全不是一个妈生的。可以从网上找一些简单的例子试运行并尝试修改,这里就不提供了。
2、使用时的注意事项
PyQt5 提供的类、方法、变量很多,几乎每种小部件都需要引用相应的类,为了方便,建议大家初学时在引用 PyQt5 时用都用下面的代码
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
PyQt5.QtWidgets 是定义窗口和小部件的,PyQt5.QtGui 是与界面有关的如图标、图片等,PyQt5.QtCore 为常用的 Qt 变量等,这样引用就不需要每使用一个类就要添加相关的类。
刚开始学习时,建议用代码直接生成界面,这样容易理解各代码的意义。在准备入手编写项目时,就建议使用 QtDesigner 设计器了,使用设计器制作的 ui 文件不能直接使用,需要用命令行转换成 py 代码后才能使用。转换后的 py 文件可以直接修改使用,也可以做为独立的界面文件在主逻辑文件中引用(建议采用这种方式,能随时修改界面且不影响逻辑代码,下一节将介绍如何实现)。
3、 QtDesigner 设计器
前面说了,QtDesigner 设计器安装完后,需要找到后手动添加快捷方式。QtDesigner 设计器功能还是很多的,建议找专门教程学习,靠自己摸索容易错过很多便捷的使用方法。这里提醒大家一点,其默认工具栏上的命令都很有用,特别是编辑信息/槽,用好了事半功倍。
设计好界面后保存为 ui 文件,建议将同项目的多个界面保存在同个目录中,备份项目时容易一起复制。用 QtDesigner 设计器制作的界面有三种用法:① 直接使用界面运行,需要转换为 py 代码文件后修改使用;② 界面与逻辑代码文件分离,将 ui 文件转换为 py 代码,优点是可在代码中做修改;③ 界面与逻辑代码文件分离,直接引用 ui 文件,缺点是只能在 QtDesigner 中修改,无法在代码模式修改。三种方法各有优缺点,大家根据情况选用。
将 ui 文件转换为 py 文件网上也有不少图形界面的工具,但我更建议使用自带的命令行转换工具,反正操作次数也不多,把命令注释在主逻辑文件前,随时可进行转换,也非常方便的
1)可直接运行界面文件
需要用下面的命令将 ui 转换为 py 文件,可打开 py 文件进行修改运行。
pyuic5 -xmain_ui.ui -o main_ui.py
命令中 main_ui.py 为转换后的 py 文件名,main_ui.ui 为 QtDesigner 设计器保存的文件名,这两个文件建议使用完整路径,将不受当前路径的影响。
2)界面与代码分离,转换为 py 文件引用
界面与代码独立分离,修改时互不影响。转换时建议同项目的多个界面保存在同个目录中,与主逻辑代码文件存放一起便于引用,转换后的 py 代码是不能直接运行的,只能由主逻辑代码引用,但可以在界面 py 文件中做一些改动(注意若要 QtDesigner 再修改,原修改的代码会丢失)。
ui 转换为 py 命令,命令参数以上面的相同,建议使用完整路径,将不受当前路径的影响。
pyuic5 -o main_ui.py main_ui.ui
转换好的界面文件由主逻辑文件引用,先是模块引用 ,然后在窗体类初始化时加上关键语句即可,示例如下:
from main_ui import Ui_MainWindow# 主窗体界面 Ui_MainWindow为 main_ui.py 中的类名
from sub_ui import Ui_Dialog# 子对话窗口界面
...
class mainWindow(QMainWindow, Ui_MainWindow): # 主窗体,必须带上 Ui_MainWindow 做为参数
def __init__(self):
super().__init__()
self.setupUi(self)# 加载主窗体界面的关键语句
...
class subDialog(QDialog, Ui_Dialog): # 子对话窗口,必须带上 Ui_Dialog 做为参数
def __init__(self, parent=None):
super(subDialog, self).__init__(parent)
self.setupUi(self)# 加载子对话窗口界面的关键语句
...
3)界面与代码分离,直接引用 ui 文件
同样是主逻辑文件与界面文件分离,但不需要转换 ui 文件,直接由主逻辑文件引用 ui 文件,随时可用 QtDesigner 设计器修改窗体的 ui 文件,主逻辑文件甚至都可以不动,就可以完成界面的改动。主逻辑引用的方法如下:
from PyQt5 import uic
class mainWindow(QMainWindow):# 主界面
def __init__(self):
super(mainWindow,self).__init__()
uic.loadUi("main_ui.ui", self)# 直接引用 ui 文件,可能需要文件完整路径
三、tkinter 与 PyQt5 结构比对
这里将对 tkinter 与 PyQt5 做比较全面的比较,如果你有在用 tkinter ,能根据比对快速掌握 PyQt5 的对应用法,实现快速上手。
1、tkinter 与 PyQt5 的主要差异
这是我总结出它们的主要差异,我也是刚入 PyQt5 的坑,很多方面还比较生疏,应该会有不少遗漏
对比tkinterPyQt5
代码特点 代码不大,很多属性可集中在一行代码中实现 代码量很多,几乎每个属性都需要一行代码实现
窗体控制 控制能力弱,很多功能需要调用 win32 库实现 控制能力强,自带很多功能方法
事件/信号 事件较少 提供信号多,可利用 QtDesigner 快速在小部件之间绑定
小部件控制 控制能力弱 控制能力强,提供很丰富的功能方法
小部件类型 较少,但基本能满足使用 较丰富,能锦上添花
界面设计 没有较理想的工具,基本只能靠代码组(有一些工具是用 place方法放置太过生硬,且不利于窗口尺寸变化) 自带 QtDesigner 设计器,可生成独立的界面代码,可随时修改且不影响逻辑代码
2、常用小部件对比
这里将一些常用的小部件,在两种模块中进行对比,后面会对各小部件进行更详细的比对说明,根据下表可以看出 PyQt5 提供的小部件更多丰富,还有一些不太常用的小部件没列出做对比,PyQt5 也还有一些独有的象 液显数据、日期、时间输入框、快捷键输入框等不列在下表中。
序号tkinter小部件PyQt5小部件说明
1FrameQframe框架面板容器,tkinter 可用来嵌套实现布局
2LabelFrameQGroupBox带标签分组框架面板
3无QDockWidget浮动面板,可实现主窗口中能拖动停靠的小窗口
4无QMdiArea提供多文档界面(MDI)功能,允许多个子窗口在其中打开
5LabelQLabel标签,显示不可编辑的文本、图像
6EntryQLineEdit单行文本输入框
7TextQTextEdit多行文本输入框
8ButtonQPushButton命令按键
9RadiobuttonQRadioButton单选框,在多个选项中只能选取一个
10CheckbuttonQCheckBox复选框,常用来作启用或禁用按键
11ComboboxQComboBox下拉菜单
12SpinboxQSpinBox可调文本框,其右侧有一个上/下调节按钮
13ScaleQSlider可以移动滑块,用来快速调节数值
14无Qdial旋钮控件,靠转动来调节数值
15ListboxQlist列表视图
16ScrollbarQScrollBar滚动条,PyQt5许多小部件会自带滚动条,无需添加,tkinter 则需手动添加
17ttk.TreeviewQTreeWidget、 QTreeView树状视图
18无QTableWidget表格视图,tkinter 只能用 ttk.Treeview 实现
19无QColumnView可分层次的数据结构视图,可以理解为另一种形式的Treeview
20CanvasQpainter画布
21MenuQMenuBar,Qmenu菜单,含窗口主菜单或右键上下文菜单
22无QToolBar工具栏,是可移动的命令面板
23ttk.NotebookQTab多页的标签选项卡
24无QStackedWidget功能类似于Qtab,借助两个左右按键进行页面切换。
25ttk.PanedWindowQSplitter窗口面板拆分器,可调整左右或上下面板大小。
26ttk.ProgressbarQProgressBar进度条
27无QStatusBar对象在底部保留了一个水平栏作为状态栏。它用于显示永久或上下文状态信息。
28无QCalendar日期对话框
29simpledialogQInputDialog简单的文本数据输入对话框
30无QFontDialog字体对话框
31filedialogQFileDialog文件选择对话框,包括打开、保存、选择目录等
PyQt5 甚至能在 QListWidget、QTableWidget 和 QTreeWidget 这些视图控制中添加除文本之外的元素,比如图标、图像甚至是其他小部件,非常灵活,能满足在很多特殊场合的要求。
3、小部件放置对比
PyQt5 对于小部件的放置,与 tkinter 有一定的差别,但也有共通点。PyQt5 采用的是布局方式,可分为水平布局(QHBoxLayout)、垂直布局(QVBoxLayout)、网格布局(QGridLayout)、表单布局(QFormLayout)、堆叠布局(QStackedLayout)等5种方式,比 tkinter 多了表单方式(较适合用于选项对话框的布局)。下面是 tkinter 与 PyQt5 放置小部件的代码对比。
放置方式tkinterPyQt5
水平放置小部件1.pack(side='left')
小部件2.pack(side='left')layout=QHBoxLayout() #水平布局
小部件1=Q小部件类()
小部件2=Q小部件类()
layout.addWidget(小部件1)
layout.addWidget(小部件2)
垂直放置小部件1.pack(side='top')
小部件2.pack(side='top')layout=QVBoxLayout() #垂直布局
小部件1=Q小部件类()
小部件2=Q小部件类()
layout.addWidget(小部件1)
layout.addWidget(小部件2)
网格放置小部件1.grid(row=0,column=0)
小部件2.grid(row=0,column=1)layout=QGridLayout() # 网格布局
小部件1=Q小部件类()
小部件2=Q小部件类()
layout.addWidget(小部件1 , 0 , 0)
layout.addWidget(小部件2 , 0 , 1)
指定位置小部件1.place(x=10,y=10,width=80,height=10)小部件1.setGeometry(10, 10, 80, 20)
嵌套放置使用 Frame 或 LabelFrame 嵌套实现,同时需要考虑要适应随窗口尺寸,需要有较强的想象能力。由布局对象或 QFrame、QGroupBox 等容器嵌套实现,代码比较多,但可以利用 QtDesigner 完成,比较方便 。
表单布局没有专门的方法,一般用 frame 小部件配合 pack 或 grid 方法实现使用表单布局(QFormLayout),可实现不用嵌套按行添加多个小部件,适合制作设置对话框。
切换小部件没有专门的方法,一般用小部件的 forget() 隐藏和 pack() 放置两种方法实现使用堆叠布局(QStackedLayout),可将需要切换的小部件添加该布局,再由其的 setCurrentIndex() 方法切换,对于要切换多个小部件时非常方便。
移动在定义时指定容器后就不能改动,只能在同一容器中移动,移动方法:在pack 方法中用 before、after 属性改变位置同一布局中移动:
布局.removeWidget(小部件) # 先删除
布局.insertWidget(index, button) #再指定插入位置
跨越容器移动,在目的容器上用 addWidget() 方法即可实现
从上表可以看出,PyQt5 代码量会比 tkinter 多出很多
4、小部件间距与尺寸控制
小部件间的边距与间距控制
类型tkinterPyQt5
与容器的边距 靠小部件的容器的pack()、grid() 中的 padx、pady 来决定 X、Y 轴上的边距,如
top01=tkinter.Frame(window)
top01.pack(side='top' , padx=10, pady=3) # 设定 frame 与上层容器边距,使得其中的小部件与跟着调整靠布局的setContentsMargins(上,下,左,右) 来调整与上层容器的边距,如
layout01= QHBoxLayout()
layout01.setContentsMargins(10,10,5,5)
小部件的间距同上
button01=tkinterButton(top01, text="Yes")
button01.pack(side='left' , padx=3) # 水平放置的小部件靠 padx 为指定间距,垂直的则由 pady 指定。同上
button01.setContentsMargins(5,5,0,0)
总结必须是左右或上下对称的调整,不能单独调整一边可随意调整4个方向的距离,非常灵活
小部件的尺寸控制
项目tkinterPyQt5
控制大部分小部件可用下面命令调整
小部件1.config( width = 10, height =2 )小部件1.setFixedSize(100, 30) # 宽,高
或
小部件1.setFixedWidth(100)
小部件1.setFixedHeight(100)
结论宽、高的单位是字符数而不是像素,无法做到微调,非常不方便单位是像素,调整方便
5、小部件样式对比
两种模块样式的语法与 CSS 大同小异,可参考的资料比较容易找,查找时可往 CSS 这方面来搜索。样式的代码比较多,就不制做表格了,下面把它们分开来写
tkinter 样式
tkinter 小部件只能修改字体、字体颜色以及背景颜色等简单的样式,若用想调整其样式,只能改用 ttk 小部件(一些常用的小部件都有相应的 ttk 对象,如 ttk.Label、ttk.Entry、 ttk.Button),可通过修改其 style 属性实现更多样式风格,注意一点是改为 ttk 对象后会失去一些功能,如不能再使用 height 属性。下面就以 Treeview 控件做个例子
def fixed_map(option): # 别问有啥用,想改 Treeview 字体颜色照搬就对了
return != ("!disabled", "!selected")]
style = ttk.Style()
style.configure('Treeview',rowheight=20,font=("宋体",9)) #设置表格行高和字体
style.map("Treeview", foreground=fixed_map("foreground"), background=fixed_map("background")) # 配合上面方法可修改字体和背影色
treeview.tag_configure('odd', background="gray",foreground="black")#倚数行背景色设定,到时修改 item 的 tag="odd" 即可
# 下面代码可隐藏折叠图标,即隐藏多层的树表前的 + 、- 图标,将内容往前移
style.layout('Treeview.Item',
[('Treeitem.padding',
{'sticky': 'nswe',
'children': [('Treeitem.myindicator', {'side': 'left', 'sticky': ''}),
('Treeitem.image', {'side': 'left', 'sticky': ''}),
('Treeitem.focus',
{'side': 'left',
'sticky': '',
'children': [('Treeitem.text', {'side': 'left', 'sticky': ''})]})]})]
)
# treeview.configure(style="Treeview") # 设置所有 Treeview 时可不需要本行
上例中的 “Treeview” 将作用于程序中所有的 Treeview 控件,若只想针对性的改变某一个,可以加上名称,如下:
style.configure('myTree.Treeview',rowheight=20,font=("宋体",9))
treeview.configure(style="myTree.Treeview") # 绑定样式
还可以借助样式的继承性将整个软件的风格改为自己想要的,但改动需要大量资料,我手上也没有比较全的资料,但有一些链接大家可以去参考一下
1、tkinter 手册
2、样式案例教程
3、tkinter 教程
PyQt5 样式
PyQt5 样式基本靠 setStyleSheet 方法实现,比 tkinter 方便的是它批量指定小部件时更加方便,可以单独指定某一小部件,也可指定某一类型的小部件,也可以按小部件的名称,甚至可以是它们的组合方式,如果代码过多,还可能存在 qss文件中,再由代码引用,非常方便。
# 样式示例
# 1、直接指定小部件
button1.setStyleSheet('''min-width: 32px; min-height: 32px;max-width:16px; max-height: 16px;
border-radius: 16px;border:1px solid black;background:red;}''') # 将按钮改为红色圆圈
# 2、指定某一类型的小部件
window.setStyleSheet("""QLabel{color:#ffffff;background-color:#ff0000;border:2px solid black;
height:50px;width:150px;font-size:20px;}""") # 修改 window 窗口中所有的 Label 控件,注意 window 是窗口而不是小部件
# 3、窗口下所有小部件
window.setStyleSheet("*{color:red ;background-color:green;}") # * 表示通配符
# 4、按控件名称(名称不惟一,允许多个小部件同名)
label.setObjectName("lable01") # 设置小部件名称
window.setStyleSheet("#lable01{background-color:red}") # 多个时可用逗号隔开,但每个名称前要加 #
# 5、组合方式
window.setStyleSheet("#lable01,QPushButton{color:red}") # 名称为 lable01 的小部件以及所有按钮
# 6、引用 qss 文件(下面有介绍 qss 文件的结构)
with open('style.qss', 'r', encoding='utf-8') as f:
qss = f.read() # 读 QSS 文件内容
window.setStyleSheet(qss) # 引用内容
QSS 样式表类似于CSS,批量指定小部件时与 setStyleSheet 有点不同,这里以例子进行说明
/*通配符选择器,用于共性样式设置,作用于所有控件 */
*{
color: black;
font-size: 12pt;
font-weight: 100;
}
/*类选择器,设置按钮类小部件样式 */
.QPushButton{
background-color: #f7f0fe;
color:red;
}
/*名称选择器,通过 setObjectName 设置控件名称 */
#label01{
background-color: #fef6f0;
}
/*多选组合式选择器,逗号分割,下面是小部件类型或名称 */
QLabel,#button01{
background-color:yellow;
}
/* qss 还有更多的用法,如父子关系的选择器等,有兴趣的自己在网上查吧*/
对于简单的样式,也可以在 Qt Designer 中进行指定(小部件右键菜单选“改变样式表”),但感觉不太智能,提供的功能也不多。样式方面的控制 PyQt5 比 tkinter 出色很多。
6、事件处理对比
在 tkinter 是以事件方式来绑定方法,并通过特定的 event 变更来传递数据,在 PyQt5 中是按信号与槽方式绑定方法,传递数据的变量也不同,还可以在设计阶段用 QtDesigner 来绑定一些常规的方法,非常方便。
tkinter 中统一使用 小部件.bind(“<事件关键字>”, 方法) 来绑定事件
自定义事件则用 小部件.bind(“<<事件关键字>>”, 方法) 方式来定义
PyQt5 的事件可以通过创建信号和槽来处理,绑定的方法称为槽,可一对多或多对一。小部件除了使用信号和槽的方式处理事件,还可以重写事件处理方法,这种方式更加直观和好理解。
下面以简单的键盘按键事件进行对比,tkinter 需要绑定方法后定义,PyQt5 则直接重写事件方法
tkinter 事件处理PyQt5 事件处理
小部件.bind("<Key>", self.onKeyPress) # 绑定方法
...
def onKeyPress(self, event):
if event.keycode==27:
print("按下 Esc 键")def keyPressEvent(self, event): #重写事件
if event.key() == Qt.Key_Escape:
print("Esc 被按下")
tkinter 键盘事件不能直接处理组合按键,只能重新绑定新的方法,非常不方便,如:
小部件.bind('<Control-a>',self.onSeletedAllI) # Ctrl + A 组合按键事件
...
def onSeletedAllI(self , event):
...PyQt5 处理组合按键也很方便,还是在 keyPressEvent 方法中判断即可,如:
if event.modifiers() & Qt.ControlModifier: # Ctrl 键按下
if event.key() == Qt.Key_A: # 按下 'A'
print("Ctrl + A 被按下")
再以按钮小部件单击事件处理做对比:
kinter 事件处理PyQt5 信号与槽
tkinter.Button(frame , text='确定', command=self.onClickOK) # 绑定self.onClickOK 方法btn = QPushButton("确定")
btn.clicked.connect(self.btn_clicked) #关联信号与槽
最后是自定义事件/信号的对比
tkinter 自定义事件PyQt5 自定义信号槽
小部件.bind("<<CustomName>>",self.customMethon) #定义事件
butStart.event_generate(''<<CustomName>>",data="我是数据") # 模拟触发事件代码量较大,完成模型见下方的代码
只能传递简单的数据,通过事件方法中的 event.data 获取代码量较多,但可接收多种复杂数据,灵活度高
PyQt5 自定义信号槽演示代码
from PyQt5.QtCore import QObject, pyqtSignal #引用关键模块
class MySignals(QObject):
# 定义信号
mySignal = pyqtSignal(int)
class MyClass(QObject):
def __init__(self):
super().__init__()
self.signals = MySignals()
# 连接信号到槽
self.signals.mySignal.connect(self.mySlot)
# 定义槽函数
def mySlot(self, value):
print(f"我接收到的数据: {value}")
def emit_signal(self, value):
# 发射信号
self.signals.mySignal.emit(value)
# 使用
obj = MyClass()
obj.emit_signal(10)# 触发信号,并传递值10
【本人的其他作品】均可在上述链接中下载
蓝秦下载:https://wwx.lanzoux.com/b01hxwx9g
密码: gcf2
1 文件名管理器V2.1.0 【2024-10-09】相关帖子
2 定时组合任务《此刻我要...》【2024-06-25】 相关帖子
3 漫画图管理器 V2.1.0【2023-03-24】 (45.6 MB) 相关帖子
4 ePub 电子书编辑器V3.1.1【2024-4-7】相关帖子
5 音乐内嵌图片管理器 V2.1.1 【2023-01-15】 (23.3 MB) 相关帖子
6 U盘歌单管理器 -V4.0.3 【2023-08-22】 (19.0 MB)相关帖子
7 小说规则下载器 V4.1.2 【2023-07-23】 (18.8 MB) 相关帖子
8 文件时间修改器 V2.1.0 【2023-03-24】 (16.1 MB) 相关帖子
9 批量粘贴神器 V1.2.0 【2024-09-24】 (16.4 MB) 相关帖子
10 剪切板资源捕获器 V2.0.1 【2024-08-08】 (44.6 MB) 相关帖子
11 批量二维码生成器 V2.4.1 【2024-04-16】 (33.8 MB) 相关帖子
12 小说角色更名器 V3.1.1 【2024-05-18】 (16.8MB)相关帖子
13 小说广告清理器 V2.0.2 【2024-05-18】 ( 18.0MB) 相关帖子
14 【Excel 脚本工具】TXT文件合并器 【2022-06-14】 (75.9 KB) 相关帖子
15 小学数学运算题随机生成【第十版】 【2024-4-9】 (371.7 KB) 相关帖子
用 python 写了不少软件,就写一些编程的心得,有兴趣的可以看看,我用 python 写软件的心得分享【附带示范】 本帖最后由 jumpbull 于 2024-11-1 20:33 编辑
四、小部件应用对比
这里将一些常用的小部件进行比对,包括定义、修改、事件等方面,可让大家快速转型上手。
1、共通部分
这里指 tkinter 、PyQt5 中所有小部件共同的设置方式,后面比对将不再单列
类型tkinterPyQt5
修改状态 小部件.config(state=tkinter.DISABLED) #禁用
小部件.config(state=tkinter.NORMAL)#启用
#下拉菜单设为只读
combobox.config(state="readonly") # 只读小部件.setEnabled(False) # 禁用
小部件.setEnabled(True) # 启用
#下拉菜单设为只读
combobox.setEditable(False) # 只读
修改/获取**文本**内容self.str_text_var = tkintetr.StringVar() #tkinter 专用字符变量
小部件=tkinter.控件类(self , textvariable=self.str_text_var ) #绑定变量
self.str_text_var.set("新内容")# 修改文本
print(self.str_text_var.get()) # 获取内容
部分显示用的小部件的 text 可以用 config(text="新内容") 来修改 # 不需要绑定变量
小部件.setText("新内容") # 修改文本
print(小部件. text()) # 获取内容
修改/获取**数值**内容self.int_age_var = tkinter.IntVar() # tkinter 专用整形变量
name=tkinter.Radiobutton(s,text="5岁",variable=self.int_age_var,value=5) #绑定变量
self.int_age_var.set(5)# 修改值
print(self.int_age_var.get())# 获取值 # 不需要绑定变量
小部件.setValue(5) # 修改值
print(小部件.value()) # 获取值
工具提示无小部件.setToolTip("按下<font color='red'>确定</font>")
小部件.setToolTipDuration(5000) # 显示提示5秒
小部件.setStatusTip("在状态栏中显示提示信息")
其中 setToolTip 支持简单的 HTML 语法
滚动条需要手动为有需要的小部件添加大部分小部件会自带滚动条,无需额外添加,但仍提供 QScrollBar 滚动条控件。
1、主窗体(tkinter.Tk、 QMainWindow)
主窗体是指程序的主界面窗口,两种模块中对窗体的创建模型完成不一样,用法也不一样,先对它们进行简单的对比:
类型tkinterPyQt5
模块类名tkinter.TkQMainWindow
主要区别仅提供一个窗体框架,无其他特殊功能,只按需手动模拟添加(用小部件模拟制作,这就考验大家的水平了)提供菜单栏(QMenuBar)、工具栏(QToolBar)、状态栏(QStatusBar)、多子窗体(QMdiArea)、停靠窗口(QDockWidget)等功能,非常方便
事件/信号绑定事件方法,对窗口移动、改变尺寸事件支持不友好重写事件方法
自适应大小(不指定尺寸时)窗口尺寸根据小部件放置决定,对于嵌套 Frame 时需要合理控制 pack() 方法中的 fill 和 expand 属性,有点吃想象力窗口尺寸根据布局及小部件放置决定,小部件多时建议使用 Qt Designer 设计
除了开始学习时,其他情况一般都会把窗口写成类,下面给出两种窗口的演示代码:
tkinter 代码
class main(tkinter.Tk): #主窗口,继承 tkinter.Tk 类
isSaved=Flase # 定义成员变量
def __init__(self):
tkinter.Tk.__init__(self) #初始化窗口对象
...
sef.init_UI() # 调用自定义的窗口 GUI 界面
...
self.mainloop() #显示窗口并进入循环阻尼(保证窗口能影响各种事件)
if __name__ == "__main__":
window = main() #创建主窗口对象
PYQt5 代码
class main(QMainWindow):
def __init__(self):
super().__init__()
...
self.init_UI()# 调用自定义的窗口 GUI 界面
self.show() #显示窗口
def initUI(self):
widget = QWidget() # 创建中央部件
self.setCentralWidget(widget) #绑定中央部件
... #界面布局
if __name__ == '__main__':
app = QApplication(sys.argv)
window = main() #创建主窗口对象
sys.exit(app.exec_()) # 进入窗口循环,关闭窗口后退出程序
同一程序中均支持多个主窗体,可以同时打开,也可以互相调用,PyQt5 在互相调用时要注意一点,创建主窗口对象时一定要设为成员变量,否则窗口不打开后不会被保持,会出现闪退情况。
2、子窗体(Toplevel、QDialog 或 QWidget)
程序中除了主窗体,仍可能会用到这么多对话框,这就是子窗体了, tkinter 中一般用 Toplevel 类来实现,PyQt5 中可以用QDialog 或 QWidget 类,前者常用于设置、选择类的对话框,后者多用于与主窗口有实时数据交换或顶层类的窗口。子窗体也与主窗体一样,一般都会写成类,下面给出演示代码:
tkinter 代码
class appSetup(tkinter.Toplevel): #软件设置对话框
isOK=Flase # 是否按下确定键
def __init__(self , parent , func=None): # parent 为主窗体对象
tkinter.Toplevel.__init__(self , parent) #初始化窗口对象
self.parent = parent
self.func = func # 回调函数,用于将变量传递到主窗口
self.protocol('WM_DELETE_WINDOW' , self.onClose)#拦截窗口关闭事件,方法中用 self.destroy() 真正关闭窗口
self.wm_attributes("-topmost", 1) #窗口置顶设置, 0 时取消置顶状态
...
def onOK(self):# 确定按键
self.isOK=True
if self.func!=None: # 启用回调函数时
datas= # 数据演示
self.func(datas)#调用回调函数把数据传给主窗口
self.destroy() # 关闭窗口
class main(tkinter.Tk): #主窗口
...
def getSetup(self,datas): #设置窗口的回调函数
self.name=datas # 取设置窗口设置的演示
self.age=datas
def openStup(self):# 打开设置窗口
setupBox=appSetup(self , self.getSetup)#创建设置窗口对象,启用回调函数
setupBox.grab_set() # 对话框模式化,即对话框不关闭前不能操作主窗口
setupBox.mainloop() # 显示对话框1,注意用此语句后程序不会被阻尼,会接着执行后面的语句,只能靠回调函数来获取对话框数据
setupBox.wait_window() # 显示对话框2,程序会被阻尼,只有窗口关闭后才执行下一语句,不建议使用,会导致窗口不稳定
print('阻尼测试')
使用模式窗口时,要注意一点,若子窗口上弹出其他窗口后模式状态会被取消,需要再使用 grab_set() 恢复模式窗口状态,弹出的窗口包括消息框、文件对话框(打开、保存、文件夹等)、其他子对话框。
PYQt5 代码
QDialog 和 QWidget 用法一样,建议主窗口、子窗口都由 QtDesigner 设计,再生成独立的界面代码,下面也以这种方式演示代码
class subDialog(QDialog, Ui_Dialog): # 子窗口
def __init__(self, func=None):
super(subDialog, self).__init__()
self.setupUi(self)# 调用写在视图层文件的页面布局函数
self.func = func #调用回调函数把数据传给主窗口
self.setWindowFlags(Qt.WindowStaysOnTopHint) # 子窗口置顶 Qt.Widget 可取消置顶,QDialog 类一样适用
def onOK(self):# 确定按键
self.isOK=True
if self.func!=None: # 启用回调函数时
datas= # 数据演示
self.func(datas)#调用回调函数把数据传给主窗口
self.accept() # 关闭窗口
class mainWindow(QMainWindow, Ui_MainWindow): # 主窗口
def __init__(self):
super().__init__()
self.setupUi(self)
def openStup(self):# 打开设置窗口
sub=subDialog(self.getSetup)#创建子窗口对象,回调函数参考 tkinter 代码,这里就不再写了
sub.show() # 添加此句子窗口可变非模式窗口,删除此句窗口为模式窗口
sub.exec_() # 对话框进行循环阻尼,只有窗口关闭后才会执行后面的代码
print('阻尼测试')
3、标签小部件(Label、QLabel)
类型tkinterPyQt5
定义声明# 同时设置标签的字体、大小、颜色等风格
label01=tkinter.Label(self , text="我是标签", fg="blue", font=("黑体",12))
label01.pack(side='left', padx=2)label = QLabel("我是标签", self)
label .setStyleSheet("background-color:blue;color: rgb(255, 255, 255);") # 设置颜色
label .setFont(QtGui.QFont("黑体", 12)) # 设置字体
layout.addWidget(label) # 添加到布局中
修改属性label01.config( text="我的" , fg=“red") # 可同时修改多个属性label.setText("我的")
以及上面的设置语句,基本每个属性都需要独立的方法完成
4、按钮(Button、QPushButton)
代码有点多,不方便再制比对表格,就分开写了
tkinter 代码
# 定义, command 绑定单击事件 ,要同时显示图标和文字时要设定 compound
button=tkinter.Button(self, text="确定", image=self.img_OK , command=self.onClickOK ,compound=tkinter.LEFT)
button.pack()
# 修改属性(可集中修改多个属性)
button.config(text="取消", image=self.img_cancel , command=self.onClickCancel , fg="red")
tkinter 中要显示的图片需要使用全局变量或类的成员变量,否则不能正常显示
PYQt5 代码
# 定义按钮小部件
button=QPushButton(QIcon("icon.png") , "确定" , self) # 可同时定义图标和文字
layout.addWidget(button) #指定所在布局
# 修改属性(几乎每个属性都要一行语句)
button.setStyleSheet(“color:blue;”) # 设置颜色
button.setText("取消")
button.setIcon(QIcon("cancel.png"))
button.setIconSize(QSize(32,32)) # 改变图标大小
#绑定信息槽(单击事件)
button.clicked.connect(self.pushButton_clicked) # 类似 tkinter 中的 command 属性
tkinter 按钮加载图标时,一般会按原图尺寸加载,而 PyQt5 会按其默认大小进行缩小,需要用
setIconSize 修改,若想按原图大小加载则要加入下面语句
icon=QIcon("icon.png") # 读取图标
button.setIcon(icon) # 加入图标
button.setIconSize(icon.pixmap(icon.availableSizes()).size()) #按原图尺寸显示
5、单行文本框(Entry、QLineEdit)
直接上代码比对(代码中附有相应说明)tkinter 代码
# 定义按钮小部件
self.str_name_var = tkinter.StringVar() #tkinter 专用变量,绑定后获取或设置文本框内容
name=tkinter.Entry(s, width=20,textvariable=self.str_name_var) # 绑定变量
name.pack(side=tkinter.LEFT,padx=2)
self.str_name_var.set("我是初始值") # 设置初始值
print(self.str_name_var.get()) # 获取文本内容
print(name.get()) # 一样可以获取文本内容
本帖最后由 jumpbull 于 2024-11-1 20:33 编辑
6、单选框(Radiobutton、QRadioButton)
PyQt5 在单选框上的使用就没 tkinter 方便,我先列个对比表,再给代码
类型tkinterPyQt5
分组使用 tkinter.IntVar() 变更绑定即可分组需要用 QButtonGroup() 按钮分组对象,再将单选框逐个绑定,代码量多,还好能使用 QtDesigner 在设计时能分组
判断选项直接通过绑定的 tkinter.IntVar() 的值进行判断,非常方便可用绑定的按钮分组的按下信号绑定方法槽,通过判断获取单选框的文本来判断,有点不太方便,后面还会给出自定义信号槽的方式来判断(觉得这个方法更合理)
添加图片支持支持
tkinter 代码
self.int_var=tkinter.IntVar() # 定义 tkinter 专用变量
# 每个 Radiobutton 单选框都绑定单击事件 clickRadiobutton ,用来做单击或切换时的处理
r1=tkinter.Radiobutton(box,text="选项1",variable=self.int_var,value=0,command=clickRadiobutton) #绑定 tkinter 变量,同时也按变量来分组
r1.pack(side=tkinter.LEFT,padx=2)
r2=tkinter.Radiobutton(box,text="选项2",variable=self.int_var,value=1,command=clickRadiobutton) # 第二个选项,注意每个选项的 value
r2.pack(side=tkinter.LEFT,padx=2)
r3=tkinter.Radiobutton(box,text="选项3",variable=self.int_var,value=2,command=clickRadiobutton) # 第三个选项,每个选项的 value 必须不一样
r3.pack(side=tkinter.LEFT,padx=2)
# 初始化选择
self.int_var.set(1) # 根据 Radiobutton 定义时的 value 值,这里是设置为“选项2”
# 选项判断
if self.int_var.get()==0:# 选项1
parint("我选了第一项")
elif self.int_var.get()==1:# 选项2
parint("我选了第二项")
else: # 选项3
parint("我选了第三项")
PYQt5 代码
判断单选框的选择情况,这里介绍两种方法,各有优缺点,大家自行选择或使用其他方式。一种是利用分组对象的单击信号,可以获得当前所选单选框的文本进行判断,代码简单好理解,但由于要判断文本内容,容易误判,特别是在修改单选框文字时常会忘记同步修改判断代码;另一种通过自定义信号、槽,获取当前选择单选框的序号来判断,缺点是代码量大。下面给出两种方式的代码:
self.radio_button1 = QRadioButton("选项1") # 创建单选框
self.radio_button2 = QRadioButton("选项2")
self.radio_button3 = QRadioButton("选项3")
self.button_group = QButtonGroup() # 创建按键分组
self.button_group.addButton(self.radio_button1) # 加入分组
self.button_group.addButton(self.radio_button2)
self.button_group.addButton(self.radio_button3)
self.layout.addWidget(self.radio_button1) # 绑定布局
self.layout.addWidget(self.radio_button2)
self.layout.addWidget(self.radio_button3)
# 第一种方式:判断单选框文本
self.button_group.buttonClicked.connect(self.onButtonClicked) # 绑定按键分组信号槽
...
def onButtonClicked(self, button): # 单击其中一个单选框时触发
if button.text() = "选项1" : #选中第1个单选框时
...
elif button.text() = "选项2" : #选中第2个单选框时
...
else: #选中第3个单选框时
...
# 第二种方式:判断选中单选框的序号
from PyQt5.QtCore import pyqtSignal # 引用自定义信号模块
from functools import partial # 相关模块,因为后面语句使用 lambda 无效
class main(QMainWindow): # 窗口类
customSignal = pyqtSignal(int)# 在成员变量处声明自定义信号
...
radioButtons= # 生成单选框列表
for i range(len(radioButtons)): # 遍历单选框
radioButtons.clicked.connect(partial(self.customSignal.emit , index)) # 定义每个单选框的单击信号,试过使用 lambda 无效
# self.button_group.addButton(radioButtons) # 加入按键分组
# self.layout.addWidget(radioButtons) # 绑定布局
self.customSignal.connect(self.onButtonClicked) # 自定义信号绑定槽
...
def onButtonClicked(self, index): # index 为单选框序号(按 radioButtons 列表顺序)
if index == 0 : #选中第1个单选框时
...
elif index == 1 : #选中第2个单选框时
...
else: #选中第3个单选框时
...
7、复选框(Checkbutton、QCheckBox)
在 tkinter 中通常绑定 tkinter.intVar() 变量来判断是否为选择状态,而 PyQt5 则是直接用 check.isChecked() 来判断。均支持图片、文本或图片加文本方式出现。PyQt5 中也可以用 QButtonGroup() 给多个复选框分组,相当于把单选框换在复选框的样式,也只能选中其中一个且必须选中一个,意义不大。
tkinter 代码
self.int_var=tkinter.IntVar() # tkinter 专用整型变量
check=tkinter.Checkbutton(root,text='选我',variable=self.int_var, command=onClickCheck) # 绑定变量
check.pack(side=tkinter.LEFT,padx=3)
def onClickCheck(): # 单击事件
if self.int_var.get(): # 判断是否选中
...
else:
self.int_var.set(1)# 强制选中,默认选中为 1 ,未选中为 0
PYQt5 代码
self.checkbox = QCheckBox("同意条款")
self.checkbox.stateChanged.connect(self.checkbox_changed)# 设置状态改变信号
def checkbox_changed(self, state): #状态改变槽
if state == Qt.Checked:
print("复选框被勾选")
else:
print("复选框未被勾选")
# 判断状态
if self.checkbox.isChecked() : # 是否选中
print("已启用")
else:
print("未启用")
8、多行文本输入框(Text、QTextEdit)
对于这个控制,tkinter 和 PyQt5 对它的操控还是很丰富的,各有特色,都支持富文本显示,总体来说还是 PyQt5 更强一些,这里就对它的一些常用功能进行对比学习
项目tkinterPyQt5
尺寸 定义时的 width 和 height 属性,注意单位是字符数
text=tkinter.Text(frame, width=30,height=10)text01.setFixedWidth(150) # 宽度(像素
text01.setFixedHeight(150) # 高度(像素)
设置文本text.delete('1.0' , 'end') # 清空内容
text.insert('end' , '文本内容')text.setPlainText('文本内容')
或
text.setText('文本内容')
内容中插入文本text.insert('end' , '从最后面插入文本')
text.insert('1.0' , '从最前面插入文本,1表示第1行,0 表示字符位置')self.text01.append('从最后面追加文本')
其他指定位置插入时,需要用到 QTextCursor 光标对象,代码有点多,见下方演示代码。
或者使用 text.append 插入 html 带格式代码
插入富文本text.tag_config("样式名",foreground="blue",font=("黑体",12,"bold")) # 设置样式标签
text.insert('end' , "文本内容", “样式名”) # 插入格式文本需要 QTextCursor 光标对象配合使用,代码有点多,见下方演示代码。
显示HTML内容不支持 text.setHtml(html) # html 为网页内容文本
print(text.toHtml()) # 返回文本编辑框中的 HTML 内容
插入图片text.image_create('end'.image=image) # image 为 PIL 图像数据cursor.insertImage("image.jpg") # cursor 为光标对象
或
text.append("<img src=\"image.png\" />")
插入列表不支持tlf = QTextListFormat() # 列表样式对象
tlf.setIndent(2) # 缩进值
tlf.setStyle(QTextListFormat.ListDecimal) #列表样式
cl=cursor.insertList(tlf)
cursor.insertText("项目1\n项目2\n项目3")
插入表格不支持table=cursor.insertTable(2,2) # 插入 2行2列的表格
for row in range(2):
for col in range(2):
cursor.insertText('Cell %d,%d' % (row, col))
cursor.movePosition(cursor.NextCell)
插入小部件小部件 = tkinter.Button(text,text="确定", command=onClick_OK) # 定义小部件
text.window_create(‘end’.window=小部件)不支持
PyQt5 光标配合代码
cursor = self.text01.textCursor() #获取文本光标
# cursor= QTextCursor(self.text01.document()) #或者这样获取光标对象
# 在当前光标处插入富文本
char_format = cursor.charFormat() #光标处的字符格式
char_format.setFontFamily("Arial") #字体名
char_format.setFontPointSize(14) #字体尺寸
char_format.setFontWeight(QFont.Bold) # 粗体
char_format.setFontItalic(True) # 斜体
char_format.setFontUnderline(True) # 下划线
cursor.setCharFormat(char_format) # 字体设置
cursor.insertText("我这富文本")
cursor.insertImage("image.jpg") # 在光标处插入图片
# 简单的指定位置插入文本
def insert_text_at_cursor(textWidget, text, position ,charFormat=None): # 在QTextEdit的指定光标位置插入文本
"""
:textWidget: QTextEdit对象
:text: 要插入的文本
:position: 插入位置,可以是'start'(0)、'end'(-1)或者是具体的字符位置号(从0开始)
:charFormat: 插入文本的字符格式
"""
cursor = textWidget.textCursor()
if position == 'start' or position == 0:
cursor.movePosition(cursor.Start)
elif position == 'end' or position == -1:
cursor.movePosition(cursor.End)
elif isinstance(position, int):
cursor.movePosition(cursor.Start)
for _ in range(position): # 跳过位置前的字符
cursor.movePosition(cursor.NextCharacter) #
else:
raise ValueError("position 参数必须是 'start', 'end' 或者一个字符位置整数")
if charFormat!=None:
cursor.insertText(text , charFormat)
else:
cursor.insertText(text)
textWidget.setTextCursor(cursor)
textWidget.ensureCursorVisible()# 确保插入的文本可见
# 删除指定位置内容
def delete_seletion_text(textWidget, start , end):
cursor.setPosition(start) # 标记选择开始
cursor.setPosition(end,QTextCursor.KeepAnchor) #选择指定内容
cursor.removeSelectedText() # 删除选择部分内容
self.text.setTextCursor(cursor)
9、下拉菜单(Combobox、QComboBox)
tkinter 中下拉菜单提供的功能较弱,不能添加图标,而 PyQt5 可以,判断选择时,tkinter 只能通过项目序或项目文本来判断,PyQt5 还提供了用户数据的判断,比较灵活,它们的示例代码如下:
tkinter 代码
self.combobox=ttk.Combobox(self, width=15 , state="readonly") # 只读状态,即只能选择不能输入
self.combobox.pack(side="left", padx=2)
self.combobox['value']=['选项1','选项2','选项3'] # 添加下拉菜单项目
self.combobox.current(0) # 默认选中第一项
self.combobox.bind("<<ComboboxSelected>>",self.onChangeItem) # 绑定选择项目事件
def onChangeItem(self,event):#选择项目事件
print(self.combobox.get()) # 获取当前选中项目的文本
if self.combobox.current()==0 : # 判断当前选项
print("我选了选项1")
elif self.combobox.current()==1:
print("我选了选项2")
else:
print("我选了选项3")
# 非只读时允许输入其他值,这时 self.combobox.current()=-1
PYQt5 代码
self.combobox = QComboBox(self)
self.combobox.addItem('选项1','用户数据1')
icon=QIcon('icon.png') #图标
self.combobox.addItem(icon, '选项2','用户数据2') # 带图标的项目
self.combobox.addItems(["选项 3", "选项 4", "选项 5"]) #不能添加用户数据
self.combobox.setEditable(False) # 设为只读
self.combobox.currentIndexChanged.connect(self.combobox_changed)#选项改变信号
def combobox_changed(self, index): # index 为选择序号,类似 tkinter 中的 current()
# 获取当前选中的文本
text = self.combobox.currentText() # 当前选择内容
print(f"当前选中: {text}")
print(self.combobox.currentData())# 获取用户数据,addItems 时用户数据为 None
10、限制范围输入框(Spinbox、QSpinBox)
限制范围输入框右侧带有上、下两调节按键,支持的数据有浮点、整型,tkinter 还支持字符类型的调整,PyQt5 将整型、浮点分成 QSpinBox 和 QDoubleSpinBox 两种控件,不直接支持字符类型,但可以靠自定义类实现。
tkinter 代码
self.int_var=tkinter.IntVar()
self.str_var=tkinter.StringVar()
spb1=tkinter.Spinbox(root, from_=0, to=100, increment=1, textvariable=self.int_var ,# 最小值0,最大值 100, 步长 1
command=self.onClickSpb1)
spb1.pack(side=tkinter.LEFT,padx=2)
spb2=tkinter.Spinbox(root,textvariable=self.str_var , values=("项目1","项目1","项目1") ,# 定义字符型值
state="readonly", command=self.onClickSpb2) # 只读,即只能通过按键调整值,不能直接输入
spb2.pack(side=tkinter.LEFT,padx=2)
self.int_var.set(5) # 初始化值
def onClickSpb1(self): # 调整值时触发
print(self.int_var.get()) # 获取当前值
PYQt5 代码
self.spin = QSpinBox(self)
# 设置QSpinBox的范围从0到 500
self.spin.setRange(0, 500) # 也可以用 self.spin.setMinimum(0)、self.spin.setMaximum(100) 代替
# 设置QSpinBox的初始值为 50
self.spin.setValue(50)
self.spin.setSingleStep(2) # 设置步长
self.spin.setDecimals(2) # 设置精度(小数位数)
spb.setWrapping(True) #数据循环调整
self.spin.setReadOnly(True) # 设为只读
self.spin.setPrefix("$") # 前缀
self.spin.setSuffix(".00")# 后缀
self.spin.valueChanged.connect(self.handle_spin) # 或是str 类时用 textChanged(带前缀、后缀时视为字符,也要用此信号)
def handle_spin(self, i):
print(i)
实现字符枚举的演示
class strSpinBox(QSpinBox): # 自定义 SpinBox 类
def textFromValue(self, v: int):# 自定义字符枚举内容
week = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六', ]
return week
...
self.mySpinbox= strSpinBox() # 创建自定义对象
self.mySpinbox.setRange(0, 6) # 其中的 6 枚举字符总数 -1 ,即表示范围为 week 列表中的所有项目
感谢分享 感谢分享,学习了{:1_919:} 我以前只学过QT,c++的,pyqt还没写过,多学习学习 PyQt6怎么样,各方面有升级 本帖最后由 ThemanRonin 于 2024-11-1 15:41 编辑
个人开发一些小程序软件还是推荐用.NET吧,简单拖拽就能用,而且生成的程序的体积还小。 PyQt6怎么样 感谢分享
页:
[1]
2