PyQt5
和PySide2
都可以让python调用Qt,区别是PyQt5出现的更早,更有名【2015年】,PySide2【2012年】
PyQt5
和PySide2
高度相似建议:如果程序要发布给客户使用,建议使用32位的Python解释器,这样最终发布的程序可以兼容32位的Windows
一、工具使用
1 pyqt 的安装
1 | 安装PyQt5 |
2 pyqt工具在PyCharm中的配置
Qt Designer
PyUIC
然后重启PyCharm即可
3 使用 Qt Designer
3.1 .ui
转换为 .py
方法一(命令行): python -m PyQt5.uic.pyuic demo.ui -o demo.py
方法二(Pycharm):
QtDesigner 可以创建窗口,生成.ui
文件,我们可以将它生成.py
文件,只需要在Pycharm中,选中xx.ui
,然后选择 External Tools中的 PyUIC,即可在同目录下生成.py
文件
但是生成的.py
直接运行并不能显示窗口,我们要怎么做呢?
3.2 四种布局
水平布局、垂直布局
- 水平布局


也可以先拖动布局,然后
Dialog with Buttons Bottom:按钮在底部的对话框窗口,效果如图7.3所示。
Dialog with Buttons Right:按钮在右上角的对话框窗口,效果如图7.4所示。
Dialog without Buttons:没有按钮的对话框窗口,效果如图7.5所示。
Main Window:一个带菜单、停靠窗口和状态栏的主窗口,效果如图7.6所示。
Widget窗口和Main Window窗口看起来是一样的,但它们其实是有区别的,Main Window窗口会自带一个菜单栏和一个状态栏,而Widget窗口没有,默认就是一个空窗口。
1 | if __name__ == '__main__': |
3.3 主窗口
二、开发过程
2.1 界面代码逻辑
代码详解:
1 | # 一个小demo |
业务逻辑和界面怎么结合?
在 Qt 系统中, 当界面上一个控件被操作时,比如 被点击、被输入文本、被鼠标拖拽等, 就会发出
信号
,英文叫signal
。就是表明一个事件(比如被点击、被输入文本)发生了。我们可以预先在代码中指定 处理这个 signal 的函数,这个处理 signal 的函数 叫做
slot
。
比如我们点击按钮后需要一个回馈
1 | def handleCalc(): |
我们要怎么做?
只需要把下面这句话放在代码中即可单击调用此方法
1 | button.clicked.connect(handleCalc) |
现在只是出现在了console上,我们希望有个弹窗怎么做?
1 | from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton, QPlainTextEdit, QMessageBox |
完整的Demo
1 | from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton, QPlainTextEdit,QMessageBox |
这段代码也有问题,就是没有封装,windows啥的散落在外面作为全局变量,不利于维护
封装后是这样的
1 | from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton, QPlainTextEdit,QMessageBox |
2.2 进度条
1 | import time |
遇到的问题:主窗口程序
mainwindow.py
,实际运行程序test.py
进度条在
mainwindow.py
,而具体的进度在test.py
,如何控制进度条?(或者说如何不中断循环返回值)答:用
yield
来存储,这个东西可以一边存储,另外一边调用。
2.3 时间命名文件
1 | import time |
2.4 打包可执行文件
1 | # 安装 |
技巧:第一次最好用 -c 打包,可以查报错
注意:由于 .ui 文件不会被打包进去,需要手动复制到对应位置
问题: 体积太大

解决办法: 建立虚拟环境,在虚拟环境里 pyinstaller
1 | # conda 创建虚拟环境 |
2.5 嵌入Matplotlib
依托于FigureCanvas画图,加载的图形(FigureCanvas)不能直接放到graphicview控件中,必须先放到graphicScene,然后再把graphicscene放到graphicview中
即 FigureCanvas -> graphicScene -> graphicview
1、 设计一个GraphicsView 和 一个 PushButton
2、 定义一个类,继承FigureCanvas
1 | import matplotlib |
然后就可以开始绘图了
这里 self.ui.graphicsView.width() / 101 的作用:
因为直接使用默认绘制出来的图的大小,一般都会与我们窗口里 GraphicsView 的大小不一致,会造成图像显示不完全,需要拖动滚动条看图像。这里我们将绘图的大小设置为与GraphicsView相匹配的大小,就可以显示出全部图像。
之所以 “/101”,我感觉可能是 graphicsView.width() 得到的结果 和 plt的figsize里设置图大小的参数 的单位是不一样的,所以要除以一个数,让图像能刚好全部显示在GraphicsView里
1 | # 初始化 gv_visual_data 的显示,建议把这部分紧跟着功能写,要不重复点击按钮无法刷新显示 |
3、写好连接槽,点击按钮显示功能
连接槽
1 | self.ui.btn_sin.clicked.connect(self.draw_fps) |
写函数
1 | def draw_fps(self): |
遇到的坑
- 好像有个奇怪的报错,现在没办法触发了,总之就是因为 PyQt5 和 PySide2 的问题,应该统一,不能一会用这个,一会用那个
- 重复点击按钮无法重复显示的问题:主要是把
实例化一个FigureCanvas
写在了初始化__init__
里,导致只能在打开程序时实例化一次,无法重新实例化。解决办法就是放在画图的函数附近即可。
2.6 Axes 画图
之前用 matplotlib 都是用matplotlib.pyplot下的方法,类似 plt.scatter() 这样的写法
现在因为要嵌入 GraphicsView,要用到 matplotlib 下的 FigureCanvasQTAgg
需要用 figure 下面的方法,类似于fig.add_subplot() 这样的写法
现在积累一下新写法。
plt.**和ax.**的区别
我认为所有不先讲清楚plt.和ax. 两种画图方式的区别的教程都是耍流氓。一上来就告诉你,plt.figure(), plt.plot(), plt.show(),这么画就对了的,都是不负责任的表现!
在matplotlib中,有两种画图方式:
**plt.figure()**:plt.***系列。通过plt.xxx来画图,其实是取了一个捷径。这是通过matplotlib提供的一个api,这个plt提供了很多基本的function可以让你很快的画出图来,但是如果你想要更细致的精调,就要使用另外一种方法。
fig, ax = plt.subplots(): 这个就是正统的稍微复杂一点的画图方法了。指定figure和axes,然后对axes单独操作。等下就讲figure和axes都神马意思。


1 | fig.add_subplot(1,1,1) #一行一列第一个图 |
1 | fig.add_subplot(1,2,2) # 一行两列第二个图 |
1 | fig.add_subplot(2,1,1) # 两行一列第一个图 |
查看 ax 下的所有方法
1 | dir(ax01) |
给子图画 colorbar
Colorbar 主要通过 figure.Figure.colorbar
方法绘制,先介绍常用的几个参数
mappable
:直译为“可映射的”,要求是matplotlib.cm.ScalarMappable
对象,能够向 colorbar 提供数据与颜色间的映射关系(即 colormap 和 normalization 信息)。主图中使用contourf
、pcolormesh
和imshow
等二维绘图函数时返回的对象都是ScalarMappable
的子类。cax
:colorbar 本质上也是一种特殊的Axes
,我们为了在画布上决定其位置、形状和大小,可以事先画出一个空Axes
,然后将这个Axes
提供给cax
参数,那么这个空Axes
就会变成 colorbar。ax
:有时我们懒得手动为 colorbar 准备好位置,那么可以用ax
参数指定 colorbar 依附于哪个Axes
,接着 colorbar 会自动从这个Axes
里“偷”一部分空间来作为自己的空间。orientation
:指定 colorbar 的朝向,默认为垂直方向。类似的参数还有location
。extend
:设置是否在 colorbar 两端额外标出归一化范围外的颜色。如果 colormap 有设置过set_under
和set_over
,那么使用这两个颜色。ticks
:指定 colorbar 的刻度位置,可以接受数值序列或Locator
对象。format
:指定 colorbar 的刻度标签的格式,可以接受格式字符串,例如'%.3f'
,或Formatter
对象。label
:整个 colorbar 的标签,类似于Axes
的xlabel
和ylabel
。
此外 colorbar 还有些设置不能在初始化的时候一次性搞定,需要接着调用方法才能完成。

1 | # 原型 |
其中第一种用在没有子图的时候,也就是
plt.colorbar()
可以不用输入参数后面三个一般用在有子图的时候,这里有一个必须的参数
mappable
参数
mappable
理解起来就是我们需要提供一个可以映射颜色的对象,这个对象就是我们作的图所以例子中6、7行我们需要获取这两个子图对象,然后将其传给
colorbar()
,colorbar
则会获取这个图所用的渐变颜色种类,之后的一个参数ax
用来指示colorbar()
获取到的渐变色条在哪里显示,我们设置ax=ax[0]
那它就在ax[0]
的区域显示,我们也可以改变渐变色条出现的位置,请对比下面和上面的区别:
2.7 文本框获取文件/文件夹
选择目录
通过 getExistingDirectory 静态方法
选择目录。
该方法,第一个参数是父窗口对象,第二个参数是选择框显示的标题。
比如
1 | from PySide2.QtWidgets import QFileDialog |
返回值即为选择的路径字符串。
如果用户点击了 选择框的 取消选择按钮,返回 空字符串。
选择已存在文件
如果你想弹出文件选择框,选择一个 已经存在
的文件,可以使用 QFileDialog 静态方法 getOpenFileName
,比如
1 | from PySide2.QtWidgets import QFileDialog |
该方法返回值 是一个元组,第一个元素是选择的文件路径,第二个元素是文件类型,如果你只想获取文件路径即可,可以采用上面的代码写法。
如果用户点击了 选择框的 取消选择按钮,返回 空字符串。
选择保存文件路径
如果你想弹出文件选择框,选择路径和文件名,来 保存一个文件
,可以使用 QFileDialog 静态方法 getSaveFileName
,比如
1 | from PySide2.QtWidgets import QFileDialog |
选择多个文件
如果要选择多个文件,使用 getOpenFileNames 静态方法
1 | from PySide2.QtWidgets import QFileDialog |
上例中 filePaths 对应的返回值是一个列表,里面包含了选择的文件。
如果用户点击了 选择框的 取消选择按钮,返回 空列表。
2.8 封装相似功能的方法
比如
浏览
找到文件夹
,其实只需要知道 将获取到的内容放入哪个textlabel
即可,但是问题是如何将参数内容
作为方法名之一
?![]()
1 | eval() |

解决用变量内容代替方法名后又出现了新的问题:
1 | self.ui.tab1_browse_filepass_button.clicked.connect(self.filepass_browse("tab1_browse_filepass_text")) |
程序会在初始化时直接运行这个!!不管你点不点击,就很烦
因为正常的写法是不加括号的比如下面这样
1 | self.ui.tab1_browse_savepass_button.clicked.connect(self.tab1_savepass_browse) |
那怎么传递额外参数呢?查了资料后发现了lambda表达式
用法就是在带括号的方法前加上lambda:
就行,比如下面这样

问题解决!
但是为什么这样?目前还没看懂
使用Pyqt编程过程中,经常会遇到给槽函数传递额外参数的情况。但是信号-槽机制只是指定信号如何连接到槽,信号定义的参数被传递给槽,而额外的参数(用户定义)不能直接传递。
而传递额外参数又是很有用处。你可能使用一个槽处理多个组件的信号,有时要传递额外的信息。
一种方法是使用lambda表达式。
解释一下,on_button是怎样处理从两个按钮传来的信号。我们使用lambda传递按钮数字给槽,也可以传递任何其他东西—甚至是按钮组件本身(假如,槽打算把传递信号的按钮修改为不可用)
第2个方法是使用functools里的partial函数。
1
2 button1.clicked.connect(partial(self.on_button, 1))
button2.clicked.connect(partial(self.on_button, 2))哪个办法好一点?这个属于风格的问题。个人观点,喜欢lambda,条理清楚,而且灵活。
2.9 清空 graphicsView
没找到方法

2.10 保存graphicsView生成的图片

1 | m_image.save(pictrureName, “JPG”, 100); |
遇到问题,jpg不可以生成文件,png可以生成文件
1 | pixmap = QPixmap(self.chartview_.sceneRect().size().toSize()) |
2.11 设置QLineEdit只允许输入数字
1 | self.regExp1 = QtCore.QRegExp('^\d{1,1}\$') # 设置校验-只允许输入1位数字 |
2.12 numpy 数组排序
二维数组排序


1 | # 按照第一列排序,与之对应的其他列相应变化 |

1 | # 按照第二列排序,与之对应的其他列相应变化 |

2.13 绘制盒图
1 | plt.boxplot(x, notch=None, sym=None, vert=None, |
● x :绘图数据。
● notch :是否以凹口的形式展现箱线图,默认非凹口。
● sym:指定异常点的形状,默认为+号显示。
● vert :是否需要将箱线图垂直放,默认垂直放。
● whis :指定上下须与上下四分位的距离,默认为1.5倍的四分位差。
● positions :指定箱线图位置,默认为[0,1,2.…]。
● widths :指定箱线图宽度,默认为0.5。
● patch _ artist :是否填充箱体的颜色。
● meanline :是否用线的形式表示均值,默认用点表示。
● showmeans :是否显示均值,默认不显示。
● showcaps :是否显示箱线图顶端和末端两条线,默认显示。
● showbox :是否显示箱线图的箱体,默认显示。
● showfliers :是否显示异常值,默认显示。
● boxprops :设置箱体的属性,如边框色、填充色等。
● labels :为箱线图添加标签,类似于图例的作用。
● filerprops :设置异常值的属性,如异常点的形状、大小、填充色等。
● medianprops :设置中位数的属性,如线的类型、粗细等。
● meanprops :设置均值的属性,如点的大小、颜色等。
● capprops :设置箱线图顶端和末端线条的属性,如颜色、粗细等。
● whiskerprops :设置须的属性,如颜色、粗细、线的类型等。
1 | import matplotlib.pyplot as plt |
2.14 加入文本框
QTextBrowser
是 只能查看文本
控件。通常用来显示一些操作日志信息、或者不需要用户编辑的大段文本内容。
在末尾添加文本
通过 append
方法在编辑框末尾添加文本内容,比如
1 | textBrowser.append('你好,世界') |
有时,浏览框里面的内容长度超出了可见范围,我们在末尾添加了内容,往往希望控件自动翻滚到当前添加的这行,
可以通过 ensureCursorVisible
方法来实现
1 | textBrowser.append('你好,世界') |
注意:这种方法会在添加文本后 自动换行
在光标处插入文本
通过 insertPlainText
方法在编辑框末尾添加文本内容,比如
1 | edit.insertPlainText('你好,世界') |
注意:这种方法 不会
在添加文本后自动换行