0%

PyQt5踩坑

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
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from mainwidget import Ui_Form #widget的ui
from mainwindow import Ui_MainWindow #mainwindow的ui

# 继承QWidget
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)


def quit_act(self):
# sender 是发送信号的对象
sender = self.sender()
print(sender.text() + '键被按下')
qApp = QApplication.instance()
qApp.quit()

# 继承QMainWindow
class MyMainWindow(QMainWindow, Ui_MainWindow):

def __init__(self, parent = None):
super(MyMainWindow, self).__init__(parent)
self.setupUi(self)

# 创建实例,并添加
q = MyMainWidget()
self.setCentralWidget(q)

# 设置窗口透明
self.setWindowOpacity(0.9)

# self.resize(1000, 700)

# 默认的状态栏
# 可以设置其他按钮点击 参考多行文本显示 然而不行
self.status = self.statusBar()
self.status.showMessage("你在主页面~")

# 标题栏
self.setWindowTitle("建模协会录入信息")

# 窗口居中
self.center()

def center(self):
'''
获取桌面长宽
获取窗口长宽
移动
'''
screen = QDesktopWidget().screenGeometry()
size = self.geometry()
self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2)

if __name__ == "__main__":
# 在shell中执行
app = QApplication(sys.argv)
mywin = MyMainWindow()
mywin.show()
# 开始主循环,直到退出
sys.exit(app.exec())

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
40
from 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
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#!/bin/bash
# -*- coding: UTF-8 -*-
import sys
import time
import PyQt5
# 基本控件都在这里面
from PyQt5.QtWidgets import (QApplication, QMainWindow, QDesktopWidget, QStyleFactory, QWidget,
QMessageBox, QTableView)
from PyQt5.QtGui import QPalette, QColor
from PyQt5.QtCore import Qt
# 导入数据库
from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel
from mainwidget import Ui_Form
from mainwindow import Ui_MainWindow

# 继承主窗口 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)

# 链接sql与查询
self.db = QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName('table/test.db')
self.db.open()
if not self.db.open():
QMessageBox.critical(None, ("无法打开数据库"), ("SQlite支持"), QMessageBox.Cancel)
return False

self.model = QSqlTableModel()

self.pushButton.clicked.connect(self.insert)
self.pushButton_2.clicked.connect(self.query)
self.pushButton_3.clicked.connect(self.insert_oneline)
self.pushButton_4.clicked.connect(self.del_oneline)
self.pushButton_5.clicked.connect(self.find_row)

# 打印行列号
def find_row(self):
print(self.view.currentIndex().row() + 1, self.view.currentIndex().column() + 1)

# 删除一行
def del_oneline(self):
self.model.removeRow(self.view.currentIndex().row())
self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
self.model.select()
self.view.setModel(self.model)
self.view.show()

# 插入一行
def insert_oneline(self):
self.model.insertRows(self.model.rowCount(), 1)
# print(str(ret))

def query(self):
self.model.setTable("people")
self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
# self.model.setFilter("id > 1")
self.model.select()
self.model.setHeaderData(0, Qt.Horizontal, "ID")
self.model.setHeaderData(1, Qt.Horizontal, "Name")
self.model.setHeaderData(2, Qt.Horizontal, "Address")
self.view.setModel(self.model)
self.view.show()

def insert(self):
query = QSqlQuery()
# query.exec_("create table people(id int primary key, name varchar(20), address varchar(30))")
# query.exec_("insert into people values(1, 'one', 'test1')")
# query.exec_("insert into people values(2, 'two', 'test2')")
# query.exec_("insert into people values(3, 'three', 'test3')")
# query.exec_("inse![](pyqt-pit/7.png)rt into people values(4, 'four', 'test4')")
for i in range (6, 100):
query.exec_("insert into people values({}, 'test', 'test{}')".format(i, i))

class MyMainWindow(QMainWindow, Ui_MainWindow):

def __init__(self, parent = None):
super(MyMainWindow, self).__init__(parent)
self.setupUi(self)

self.q = MyMainWidget()
self.setCentralWidget(self.q)

# 设置窗口透明
self.setWindowOpacity(0.9)

# 关闭时,默认关闭数据库
def closeEvent(self, e):
self.q.db.close()


if __name__ == "__main__":
# 在shell中执行
app = QApplication(sys.argv)
mywin = MyMainWindow()
mywin.show()
# 开始主循环,直到退出
sys.exit(app.exec())

pyqtgraph使用

这个库,简直有毒,总以为我装失败了,没想到关机开机后这个库能用了。

这个绘图库是纯python图形GUI库,由于是基于pyqt开发的集成绘图模块,所以PyQtGraph绘图与底层方式实现绘图功能在速度上的区别不大。这个库的好处是,可以写两行代码,点击run example查看所有的绘图效果。

1
2
import pyqtgraph.examples
pyqtgraph.examples.run()

绘图效果,看下图,个人感觉还可以,虽然比不上matlab的工具箱和matplotlib

模仿上述的例子进行简单的集成。这里用到了提升窗口的技术,如果不了解,可以先看后文的提升窗口。基类:QWidget,头文件:pyqtgraph, 名称:GraphicsLayoutWidget,一个字母都不要错哦。在本代码中,使用了提升窗口的对象名为:pyqtgraph2pyqtgraph

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import pyqtgraph as pg 
class MyMainWidget(QWidget, Ui_Form):

def __init__(self, parent = None):

super(MyMainWidget, self).__init__(parent)
# 在初始化之前设置pg,因为初始化会写死配置,无法在更改
pg.setConfigOption('background', '#f0f0f0')
pg.setConfigOption('foreground', 'd')
# 使曲线看起来光滑
pg.setConfigOptions(antialias = True)

self.setupUi(self)
self.setLayout(self.gridLayout)

# 退出窗口
self.quit_btn.clicked.connect(self.quit_act)

# 绘图
self.pushButton.clicked.connect(self.graph_plot)
self.pushButton_2.clicked.connect(self.graph_plot1)

# 多曲线绘图
def graph_plot1(self):
self.pyqtgraph2.clear()
plt = self.pyqtgraph2.addPlot(title="test")
x = np.random.normal(size=20)
y1 = np.sin(x)
y2 = 1.1 * np.sin(x + 1)
bg1 = pg.BarGraphItem(x = x, height = y1, width = 2, brush = 'r')
bg2 = pg.BarGraphItem(x = x, height = y2, width = 2, brush = 'b')
plt.addItem(bg1)
plt.addItem(bg2)
# 新的一列还是新的一行
# self.pyqtgraph2.nextColumn()
self.pyqtgraph2.nextRow()
plt2 = self.pyqtgraph2.addPlot(title="test1")
x = np.linspace(1, 20, 20)
y3 = np.random.normal(size=20)
plt2.plot(x, y3, pen = pg.mkPen(width = 2, color = 'd'))
plt2.showGrid(x=True, y=True)

# 单标题,多曲线绘图
def graph_plot(self):
self.pyqtgraph.clear()
# 两种绘图方式
# self.pyqtgraph.addPlot(y = np.random.normal(size=100),
# pen = pg.mkPen(color='b', width=2))
plt2 = self.pyqtgraph.addPlot(title="multi rules")
plt2.plot(np.random.normal(size=150),
pen = pg.mkPen(color='r', width=2))
plt2.plot(np.random.normal(size=150) + 5,
pen = pg.mkPen(color='b', width=2))

Matplotlib与PyQt的结合

谈到绘图,不得不提一下Matplotlib,很出名的python绘图库。如果可以把Matplotlib绘制的图形嵌入到PyQt中,也就避免了底层绘制图像的繁琐。

说干就干。(官网也有实例)

工具库的引入:

1
2
3
4
from 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
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
# 绘图的空白界面
class MymplCanvas(FigureCanvas):

def __init__(self, parent=None, width=5, height=4, dpi=100):
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = self.fig.add_subplot(111) # 多界面绘图
FigureCanvas.__init__(self, self.fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding, QSizePolicy.Expanding
)
FigureCanvas.updateGeometry(self)

def static_plot(self):
self.axes.clear()
self.fig.suptitle("static FIG")
t = np.linspace(1, 10, 10)
s = np.sin(np.pi * t)
self.axes.plot(t, s)
self.axes.grid(True)
self.draw()

# 为何要加参数
def dynamic_plot(self, *args, **kwargs):
timer = QTimer(self)
timer.timeout.connect(self.update_fig)
timer.start(1000)

def update_fig(self):
self.axes.clear()
self.fig.suptitle("dynamic FIG")
l = np.random.randint(1, 10, 4)
self.axes.plot([0, 1, 2, 3], l, 'r')
self.axes.grid(True)
self.draw()

之后,封装绘图类。将绘图类封装到MatplotlibWidget类,调用MatplotlibWidget类直接实现绘图功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 实现绘图类
class MatplotlibWidget(QWidget):
def __init__(self, parent=None):
super(MatplotlibWidget, self).__init__(parent)

# 封装绘图类
self.gridLayout = QGridLayout()
self.mpl = MymplCanvas(self)
# 添加工具栏
self.mpl_tool = NavigationToolbar(self.mpl, self)
self.setLayout(self.gridLayout)
self.gridLayout.addWidget(self.mpl)
self.gridLayout.addWidget(self.mpl_tool)

def static(self):
self.mpl.static_plot()

def dynamic(self):
self.mpl.dynamic_plot()

之后在窗口QWidget里面调用MatplotlibWidget即可。

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
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)

# 提升后
self.widget = MatplotlibWidget()
self.widget.setVisible(False)
self.widget_2 = MatplotlibWidget()
self.widget_2.setVisible(False)
self.gridLayout.addWidget(self.widget)
self.gridLayout.addWidget(self.widget_2)
self.pushButton.clicked.connect(self.dynamic)
self.pushButton_2.clicked.connect(self.static)

def static(self):
self.widget.setVisible(True)
self.widget.mpl.static_plot()

def dynamic(self):
self.widget_2.setVisible(True)
self.widget_2.mpl.dynamic_plot()

效果如下所示:

提升窗口

已经含有的类

在上文已经提到了一次提升窗口。再说这个之前,确保已经看懂了上面pyqtgraphmatplotlib的例子,不然这里更加看不懂。之后会在说明自定义和引用已有类的角度说明如何提升窗口。(毕竟我之前也以为这个没多大用)

基类: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
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#!/bin/bash
# -*- coding: UTF-8 -*-
import sys
import numpy as np
import PyQt5
# 基本控件都在这里面
from PyQt5.QtWidgets import (QApplication, QMainWindow, QDesktopWidget, QStyleFactory, QWidget,
QSizePolicy, QPushButton, QGridLayout)
from PyQt5.QtGui import QPalette, QColor
from PyQt5.QtCore import Qt, QTimer
from mainwidget import Ui_Form
from mainwindow import Ui_MainWindow

from 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
# 绘图的空白界面
class MymplCanvas(FigureCanvas):

def __init__(self, parent=None, width=5, height=4, dpi=100):
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = self.fig.add_subplot(111) # 多界面绘图
FigureCanvas.__init__(self, self.fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding, QSizePolicy.Expanding
)
FigureCanvas.updateGeometry(self)

def static_plot(self):
self.axes.clear()
self.fig.suptitle("static FIG")
t = np.linspace(1, 10, 10)
s = np.sin(np.pi * t)
self.axes.plot(t, s)
self.axes.grid(True)
self.draw()

# 为何要加参数
def dynamic_plot(self, *args, **kwargs):
timer = QTimer(self)
timer.timeout.connect(self.update_fig)
timer.start(1000)

def update_fig(self):
self.axes.clear()
self.fig.suptitle("dynamic FIG")
l = np.random.randint(1, 10, 4)
self.axes.plot([0, 1, 2, 3], l, 'r')
self.axes.grid(True)
self.draw()

# 实现绘图类
class MatplotlibWidget(QWidget):
def __init__(self, parent=None):
super(MatplotlibWidget, self).__init__(parent)

# 封装绘图类
self.gridLayout = QGridLayout()
self.mpl = MymplCanvas(self)
self.mpl_tool = NavigationToolbar(self.mpl, self)
self.setLayout(self.gridLayout)
self.gridLayout.addWidget(self.mpl)
self.gridLayout.addWidget(self.mpl_tool)

def static(self):
self.mpl.static_plot()

def dynamic(self):
self.mpl.dynamic_plot()

引用

最终呈现的效果和上面matplotlib绘图一样。这里只给出对比部分的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyMainWidget(QWidget, Ui_Form):

def __init__(self, parent = None):
super(MyMainWidget, self).__init__(parent)
self.setupUi(self)

self.setLayout(self.gridLayout)
# 提升前
# self.widget = MatplotlibWidget()
# self.widget.setVisible(False)
# self.widget_2 = MatplotlibWidget()
# self.widget_2.setVisible(False)
# print(type(self.widget_2))

# 提升后 不用创建实例,直接拖一拖,pyuic5一下即可
self.gridLayout.addWidget(self.widget)
self.gridLayout.addWidget(self.widget_2)
self.pushButton.clicked.connect(self.dynamic)
self.pushButton_2.clicked.connect(self.static)

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))

结束语

因为这里给的都是核心代码,没有给全部代码,如果你是在弄不出来,底下留言给下邮箱,我给你代码。

不过还是建议多折腾,多走两步,多研究,伸手党不好。

下一步,自定义信号与槽,类与函数进阶,多线程。

感谢上学期间打赏我的朋友们。赛博乞讨:我,秦始皇,打钱。

欢迎订阅我的文章