PyQt5
的文档相对较少,在自己的实际开发使用中,遇到了些许的坑。网上也没有大把的例子供我参考,还有好多例子停留在Qt4这个版本。所以将调bug的过程记录于此,均为自己探索的较为良好的解决方案。包括:
- 布局相关
- 代码结构
- qtpandas的版本问题
- 切换主题
- 数据库使用
- pyqtgraph绘图
- 提升窗口(matplotlib)
- Html的动态绘图
同样,代码太长,不能全部放上来,只放了核心代码,稍微有GUI开发基础的人都能看懂。或者,你可以按照目录观看自己想要的部分。或者,去给出的github链接内拿代码。
代码链接
https://github.com/muyuuuu/PyQt-learn/tree/master/GUI/Basic-train
布局相关
还是使用Qt designer
进行布局管理较为方便,写代码一行一行的调试实在是费劲。而且,可以在布局管理器中进行嵌套布局,如先对几个小控件进行水平布局,在对另外的空间垂直布局,最后整体网格布局,这样会较为便捷。效果如下所示:
verticalLayout_5
下面有多个布局。此外,Qt designer
里面设置的布局不一定为软件最终的布局,还是要实际运行一下,看看到底软件长啥样。如,在designer里面每个控件都很大,实际运行却可能很小,不过大体相同。
代码结构
QWidget
是所有类的基类,也包括QMainWindow
,而在设置窗口风格时,只能在QWidget
里面使用self.setStyleSheet(self.style)
, 在QMainWindow
里面则无效(这里的无效是指:切换主题后会有一些设置无法更改,导致界面很丑)。
但是,一个完整的窗口程序(包括任务栏,菜单栏,状态栏等)是离不开QMainWindow
的,所以我们可以在QMainWindow
设置中心区域为QWidget
,然后在QWidget
里面添加控件即可,即将控件的实现和布局委托给QWidget
。
所以,我们需要两个ui
文件。与控件相关的东西,如添加控件,修改控件可以在QWidget
里面完成,而有关窗口的设置,如菜单栏,设置透明度,居中显示等可以在QMainWindow
里面设置,最后将QWidget
添加即可。
1 | from mainwidget import Ui_Form #widget的ui |
qtpandas的使用
pandas
是比较好好用的数据分析的库,而与PyQt5
的结合,最方便的方法是使用qtpandas
。这个库会将pandas
的数据显示在QTableWidget
上,自动实现QTableWidget
的各种功能,如增加,排序,保存,删除等。
但是,pip install qtpandas
暂时的版本只支持PyQt4
,如果想支持PyQt5
,需要去github下载最新版。下载好后进入目录,命令行内python setup.py install
即可。
而且qtpandas
依赖于pandas 0.23
,需要提前安装pandas 0.23
。如果直接pip install pandas
是不可以的,因为直接pip会到最新版0.25(写这篇博客的时候),qtpandas
不能使用,会直接报错:
1 | No module named 'pandas.tslib |
因此我创建了虚拟环境,在虚拟环境内直接pip install pandas==0.23
即可,但是还不能放心使用。
通过这几天的使用发现,PyQt5
的依赖库做的都不是很好。
有些库pip
后可以放心使用;
有些库pip
后关机开机电脑才能生效,比如pyqtgraph
;
有些库pip
后关机开机还是用不了,比如qtpandas
。
于是,我在github上下载了qtpandas
,将文件夹放在代码的同级目录下,便可以引用进来,然后使用。期待开发者做后后面的完善吧。
https://github.com/draperjames/qtpandas
库的安装很费劲,但是,真的好用,代码与截图如下:
文件结构:(qtpandas的文件夹在这,调用里面的代码,库的本质还是代码啊)
执行效果:
代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40from qtpandas.views.DataTableView import DataTableWidget
from qtpandas.models.DataFrameModel import DataFrameModel
class MyMainWidget(QWidget, Ui_Form):
def __init__(self, parent = None):
super(MyMainWidget, self).__init__(parent)
self.setupUi(self)
self.setLayout(self.gridLayout)
# 退出窗口
self.quit_btn.clicked.connect(self.quit_act)
# qtpandas
model = DataFrameModel()
# 空模型那个用于存储和处理数据
# print(type(self.widget_2))
self.widget_2.setViewModel(model)
data = {
'A': [10, 11, 12],
'B': [12, 11, 10],
'C': ['a', 'b', 'c']
}
self.df = pandas.DataFrame(data)
self.df['A'] = self.df['A'].astype(np.int8) # 委托,规定某一列的类型
model.setDataFrame(self.df)
# 保存数据
self.quit_btn_7.clicked.connect(self.save_data)
def save_data(self):
self.df.to_csv('data.csv')
def quit_act(self):
# sender 是发送信号的对象
sender = self.sender()
print(sender.text() + '键被按下')
qApp = QApplication.instance()
qApp.quit()
切换主题
对于我这种不会(不想写)前端的人来说,写QSS(Qt Style Sheet)
无疑在要我命,不如用他人写好的代替一下,在代码中引入样式即可。这样也能实现界面和逻辑的代码分离。
别人写好的:
https://blog.csdn.net/liang19890820/article/details/52384042
每个Widget
都可以设置风格,因此建议主要界面使用QWidget
作为窗口的主要区域用于设背景风格。同样,如上文提到的,QMainWindow
里切换风格效果很不好,不如把所有控件放在QWidget
里面,切换风格也方便。black.qss还有white.qss都是从上面的CSDN里的博客下载的。
文件结构如下:
切换风格的代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27# 样式的类
class StyleFile:
def __init__(self):
pass
# @staticmethod
def readQSS(style):
with open (style, 'r') as f:
return f.read()
# 在QWidget里面切换风格才有效
class MyMainWidget(QWidget, Ui_Form):
def __init__(self, parent = None):
super(MyMainWidget, self).__init__(parent)
self.setupUi(self)
self.quit_btn_3.clicked.connect(self.style_change)
self.quit_btn_4.clicked.connect(self.style_change1)
def style_change1(self):
style_file = 'white.qss'
self.style = StyleFile.readQSS(style_file)
self.setStyleSheet(self.style)
def style_change(self):
style_file = 'black.qss'
self.style = StyleFile.readQSS(style_file)
self.setStyleSheet(self.style)
效果如下:
黑色主题
白色主题
而设置窗口的无边框风格、透明度风格则必须要在QMainWindow
里面设置,因为这是窗口的特有方法。
数据库的使用
数据库的使用还是很常见的,对于开发小型的应用程序,使用SQLite
即可(Manjaro环境下已经内置),以及可视化的管理软件SQLiteStudio。PyQt
使用QSqlDataBase
连接数据库,一个实例表示了一次连接(连接到IP地址,本地数据库均可),连接、操作完毕后记得关闭,否则会占用数据库的资源。
此外,QSqlTableModel
提供了可读写的数据模型,链接到数据库后可设置查询的表。设置查询的过滤条件,执行查询语句。之后,将数据模型填充到QTableView
中,可滚动,可编辑(编辑后改变数据库中的存储)。
放一次完整的代码吧,但是copy下来不能运行,因为没有ui
文件,自己画一个或者只看数据库部分的代码吧。
1 | #!/bin/bash |
pyqtgraph使用
这个库,简直有毒,总以为我装失败了,没想到关机开机后这个库能用了。
这个绘图库是纯python
图形GUI
库,由于是基于pyqt
开发的集成绘图模块,所以PyQtGraph
绘图与底层方式实现绘图功能在速度上的区别不大。这个库的好处是,可以写两行代码,点击run example
查看所有的绘图效果。
1 | import pyqtgraph.examples |
绘图效果,看下图,个人感觉还可以,虽然比不上matlab
的工具箱和matplotlib
。
模仿上述的例子进行简单的集成。这里用到了提升窗口的技术,如果不了解,可以先看后文的提升窗口。基类:QWidget
,头文件:pyqtgraph
, 名称:GraphicsLayoutWidget
,一个字母都不要错哦。在本代码中,使用了提升窗口的对象名为:pyqtgraph2
, pyqtgraph
。
1 | import pyqtgraph as pg |
Matplotlib与PyQt的结合
谈到绘图,不得不提一下Matplotlib
,很出名的python绘图库。如果可以把Matplotlib
绘制的图形嵌入到PyQt
中,也就避免了底层绘制图像的繁琐。
说干就干。(官网也有实例)
工具库的引入:1
2
3
4from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5 import NavigationToolbar2QT as NavigationToolbar
创建空白的绘图界面的类,实现静态绘图和动态绘图两种方法
1 | # 绘图的空白界面 |
之后,封装绘图类。将绘图类封装到MatplotlibWidget
类,调用MatplotlibWidget
类直接实现绘图功能。
1 | # 实现绘图类 |
之后在窗口QWidget
里面调用MatplotlibWidget
即可。
1 | class MyMainWidget(QWidget, Ui_Form): |
效果如下所示:
提升窗口
已经含有的类
在上文已经提到了一次提升窗口。再说这个之前,确保已经看懂了上面pyqtgraph
和matplotlib
的例子,不然这里更加看不懂。之后会在说明自定义和引用已有类的角度说明如何提升窗口。(毕竟我之前也以为这个没多大用)
基类:
QWidget
,头文件:pyqtgraph
, 名称:GraphicsLayoutWidget
。
因为pyqtgraph是已经安装好的库,所以可以设置头文件为pyqtgraph
,相当与传统的import
。
而在Qt Designer内,首先将Widget
托入界面,右键,提升为。(不好意思这里没办法截图,linux下的截图可真难用,wine QQ的截图也不能全局有效)
输入:头文件:pyqtgraph
, 名称:GraphicsLayoutWidget
。(不要输入错误,一个字母都不要错)
输入后以此点击添加、提升即可。
之后pyuic5 -o mainwidget.py mainwidget.ui
后,会在辅助的mainwidget.py
文件里发现:from pyqtgraph import GraphicsLayoutWidget
这么一句话,也就是,上面说的传统的import。手工也可写这行代码,为什么大费周折呢?因为提升了窗口,会发现,这个类有了自己的界面,而不在是简单的导入类,之后可以使用这个特殊的界面进行随意的拖动、布局、使用,会比纯代码方便很多。
相当与在库pyqtgraph
中引入了一个布局,并且给了这个布局一个界面,这个界面可以实现绘图功能,那么以后在想绘图的时候就可以复制、拖动这个界面,更加轻松的布局和设置大小。此时提升完毕。
自己写的类
以上是在有相应的库和方法的前提下设置提升类。如果没有相应的库,如上文的类MatplotlibWidget
,如何将自己写的类关联到Widget
里面进行提升,形成容易操作的控件呢?且看下文。
这个时候需要将自己写的类单独拿出来成为一个文件,在同级目录下引用即可。
文件结构:
提升方法
自行托入Widget
,右键提升,输入名称和头文件,名称是类名,文件就是同级目录下的文件,最后添加,提升即可。
相关代码:
MatplotlibWidget.py
1 | #!/bin/bash |
引用
最终呈现的效果和上面matplotlib绘图一样。这里只给出对比部分的代码。
1 | class MyMainWidget(QWidget, Ui_Form): |
Html的动态绘图
直接引入画好的图片即可,不过图片的后缀是html,使用QWebEngineView
加载即可,一个坑是:必须使用绝对路径,相对路径是找不到的。另一个坑是,Qt4 Designer里面没有QWebEngineView
这个类,因为这个类是新的,Qt4 Designer 里面只有旧的类QWebView
。别问我为什么不用新的,因为linux下还没有Qt5 designer。
旧的类不再维护,新的类使用chromium内核,如果不想写代码实现(布局,定位,大小,这里写代码很麻烦的),那么怎么在Qt4 Designer使用新的类呢?
很简单,使用上文说的提升窗口,设置好基类,头文件,名称就很容易在旧界面中使用新的类了。
提升窗口为QWebEngineView
, 同样不要写错一个字母。
Html的绘图技术的强大之处在于炫酷、可交互。而python也不缺少html的绘图库,如plotly
(javascript的绘图库,python调用接口),还有pyecharts,也很炫酷。(pyecharts版本做了更新,之前就版本的代码无法运行,我一年前的时候还用的旧版本,新版本的用法参考github)
echarts的(我写的时候服务器维修,无法下载图片,使用的pyecharts的):
https://gallery.echartsjs.com/explore.html#sort=rank~timeframe=all~author=all
pyecharts的:
https://github.com/pyecharts/pyecharts/tree/master/example
我随意挑选了一个,执行后生成本地的html文件(可交互的图形)。
文件结构:
效果如下所示:
代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 继承主窗口 Qmainwindow 和 自己画的界面 Ui_MainWindow
class MyMainWidget(QWidget, Ui_Form):
def __init__(self, parent = None):
super(MyMainWidget, self).__init__(parent)
self.setupUi(self)
self.setLayout(self.gridLayout)
# html 绘图
self.quit_btn_2.clicked.connect(self.html_plot)
def html_plot(self):
# 必须用绝对路径
url = "/home/lanling/Srcode/python/tools/GUI/practice/Pyecharts/render.html"
# web_view 是 QWebEngineView 对象的一个实例
self.web_view.load(QUrl.fromLocalFile(url))
结束语
因为这里给的都是核心代码,没有给全部代码,如果你是在弄不出来,底下留言给下邮箱,我给你代码。
不过还是建议多折腾,多走两步,多研究,伸手党不好。
下一步,自定义信号与槽,类与函数进阶,多线程。