Matplotlib画图教程:在QT界面中嵌入三维图片
需求:
做项目报告的时候,有这么一个想法,就是能通过UI随时调用matplotlib进行二维图和三维图的绘制。因此就诞生了做这么一个小模块的想法。
这里先上一下最终结果:
思路:
pyqt5内嵌matploblib画布。
matplotlib画布中有一个FigureCanvasQTAgg的模块可以用于UI绘制
模块设计思路:
1. ui文件
ui文件是通过qtdesigner直接生成的ui源码,与图表绘制相独立。
2. 图表绘制文件
该文件用于设计一个matplotlib图表绘制类,存放我们需要做报告的任何图图表,以及我们的一些数据导入。
3. 运行文件
运行文件继承上面两个文件,运行实例。
UI模块
这个模块没什么好说的,基于QTdesigner进行图形绘制,图形界面已经每个组件定义如下。(这个模块在教程中只是用于演示)
1.1 UI图形类
类: | Ui_matplot_demo | 图形绘制类 |
---|---|---|
实例化方式 | 不需要入参 | |
方法 | ||
self.setupUi | 构建一个UI窗口 |
1.2 测试UI界面及其属性
绘制的界面样式
界面控件属性
1.3 代码
# -*- coding: utf-8 -*-# Form implementation generated from reading ui file 'ui_ut.ui'
#
# Created by: PyQt5 UI code generator 5.15.6
#
# 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_matplot_demo(object):def setupUi(self, matplot_demo):matplot_demo.setObjectName("matplot_demo")matplot_demo.resize(1133, 721)self.gridLayout = QtWidgets.QGridLayout(matplot_demo)self.gridLayout.setObjectName("gridLayout")self.bt_close = QtWidgets.QPushButton(matplot_demo)self.bt_close.setObjectName("bt_close")self.gridLayout.addWidget(self.bt_close, 1, 1, 1, 1)self.bt_open = QtWidgets.QPushButton(matplot_demo)self.bt_open.setObjectName("bt_open")self.gridLayout.addWidget(self.bt_open, 1, 0, 1, 1)self.plt3d_module = QtWidgets.QWidget(matplot_demo)self.plt3d_module.setMinimumSize(QtCore.QSize(1111, 611))self.plt3d_module.setObjectName("plt3d_module")self.gridLayout.addWidget(self.plt3d_module, 0, 0, 1, 2)self.retranslateUi(matplot_demo)QtCore.QMetaObject.connectSlotsByName(matplot_demo)def retranslateUi(self, matplot_demo):_translate = QtCore.QCoreApplication.translatematplot_demo.setWindowTitle(_translate("matplot_demo", "Form"))self.bt_close.setText(_translate("matplot_demo", "按一下就可以关闭图片"))self.bt_open.setText(_translate("matplot_demo", "按一下就可以画图"))
图表绘制模块
这个模块是核心的图表绘制模块,主要结构如下
1.1 依赖包
包名 | 含义 | 安装方式 |
---|---|---|
numpy | 数学运算包 | pip install numpy |
matplotlilb | 数学绘图包 | pip install matplotlib |
1.2 类
类: | MyFigure | 图形绘制类 |
---|---|---|
实例化方式 | 入参 | 含义 |
width=5 | 控制画布宽度 | |
height=4 | 控制画布高度 | |
dpi=100 | 控制画布分辨率 | |
属性 | 含义 | 初值 |
self.fig | 实例化一个matplotlib的Figure类 | |
self.axes | 坐标系,可以是二维,也可以是三维 | |
方法 | ||
plot_sin | 画一个正弦函数 | |
plot_cos | 画一个余弦函数 | |
plot_3d | 画一个3D的图(基于leetcode218 天际线) |
1.3 代码如下
import numpy as np
import matplotlib
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
matplotlib.use("Qt5Agg") # 声明使用QT5# 创建一个matplotlib图形绘制类
class MyFigure(FigureCanvas):def __init__(self, width=5, height=4, dpi=100):# 第一步:创建一个创建Figureself.fig = Figure(figsize=(width, height), dpi=dpi)# 第二步:在父类中激活Figure窗口super(MyFigure, self).__init__(self.fig) # 此句必不可少,否则不能显示图形# 第三步:创建一个子图,用于绘制图形用,111表示子图编号,如matlab的subplot(1,1,1)self.axes = None# 第四步:就是画图,【可以在此类中画,也可以在其它类中画】def plot_sin(self):self.axes = self.fig.add_subplot(111)t = np.arange(0.0, 3.0, 0.01)s = np.sin(2 * np.pi * t)self.axes.plot(t, s)def plot_cos(self):self.axes = self.fig.add_subplot(111)t = np.arange(0.0, 3.0, 0.01)s = np.sin(2 * np.pi * t)self.axes.plot(t, s)def plot_3d(self):self.axes = self.fig.add_subplot(111, projection='3d')x_edges = np.array([[10, 20], [10, 20], [10, 20], [10, 20],[20, 30], [20, 30], [20, 30], [20, 30],[30, 40], [30, 40], [30, 40], [30, 40],[40, 50], [40, 50], [40, 50], [40, 50]])# 设置y轴取值y_edges = np.array([[10, 20], [20, 30], [30, 40], [40, 50],[10, 20], [20, 30], [30, 40], [40, 50],[10, 20], [20, 30], [30, 40], [40, 50],[10, 20], [20, 30], [30, 40], [40, 50],[10, 20], [20, 30], [30, 40], [40, 50]])# 设置X,Y对应点的值。即原始数据。hist = np.array([[3.0], [0.0], [8.0], [4.0],[2.0], [4.0], [5.0], [7.0],[9.0], [2.0], [6.0], [3.0],[0.0], [3.0], [1.0], [0.0]])color_list = ['skyblue', 'lightgreen', 'bisque', 'gold','lightgreen', 'bisque', 'gold', 'lightpink','bisque', 'gold', 'lightpink', 'plum','gold', 'lightpink', 'plum', 'lightgray']for i in range(len(x_edges)):# 设置作图点的坐标xpos, ypos = np.meshgrid(x_edges[i][:-1] - 2.5, y_edges[i][:-1] - 2.5)xpos = xpos.flatten('F')ypos = ypos.flatten('F')zpos = np.zeros_like(xpos)# 设置柱形图大小dx = 5 * np.ones_like(zpos)dy = dx.copy()dz = hist[i].flatten()# 设置坐标轴标签self.axes.set_xlabel('front')self.axes.set_ylabel('side')self.axes.set_zlabel('height')self.axes.bar3d(xpos, ypos, zpos, dx, dy, dz, color=color_list[i], zsort='average')
运行模块
1.1 依赖包
包名 | 含义 | 安装方式 |
---|---|---|
sys | 系统包 | - |
PyQt5 | UI包 | pip install pyqt5 |
项目包 | ||
ui_ut | UI模块 | |
leetcode218_figure | 图表绘制模块 |
1.2 类
类: | myWindow | 运行窗口类 |
---|---|---|
继承父类 | ||
QWidget | ||
Ui_matplot_demo | ||
实例化方式 | 入参 | 含义 |
不需要入参实例化 | ||
属性 | 含义 | 初值 |
self.F | 实例化一个MyFigure的图表绘制类 | |
方法 | ||
open_pic | 在UI中画图,绑定bt_open控件 | |
close_pic | 关闭图片,绑定bt_close控件 |
1.3 最后再封装一个main函数调用,代码如下:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from ui_ut import Ui_matplot_demo
from leetcode218_figure import MyFigureclass myWindow(QWidget, Ui_matplot_demo):def __init__(self):super(myWindow, self).__init__()self.setupUi(self)self.setWindowTitle("显示matplotlib绘制图形")self.setMinimumSize(0, 0)# 第五步:定义MyFigure类的一个实例self.F = MyFigure(width=10, height=6, dpi=100)self.F.plot_cos()# 第六步:在GUI的groupBox中创建一个布局,用于添加MyFigure类的实例(即图形)后其他部件。# 在容器中添加一个groupbox对象,在groupbox对象中创建布局self.groupBox = QGroupBox(self.plt3d_module)self.groupBox.setMinimumSize(QSize(1100, 610))self.groupBox.setTitle("画图demo")def connect_bind():self.bt_open.clicked.connect(self.open_pic)self.bt_close.clicked.connect(self.close_pic)connect_bind()self.glo_plt_figure = QGridLayout(self.groupBox)def open_pic(self):self.F = MyFigure(width=10, height=6, dpi=100)self.F.plot_3d()self.glo_plt_figure.addWidget(self.F, 0, 0)print("here")self.show()self.glo_plt_figure.addWidget(self.F, 0, 0)def close_pic(self):self.glo_plt_figure.removeWidget(self.F)self.show()def main():app = QApplication(sys.argv)win = myWindow()win.show()sys.exit(app.exec_())if __name__ == "__main__":main()