@PyQt5记录笔记之使用Matplotlib绘图(交互功能)
PyQt5上创建Matplotlib绘图的基本流程
1,要在PyQt5界面上显示,首先要创建界面组件FigureCanvas,一个画布(相当于用于显示Matplotlib的Widget组件);
2,接着需要创建绘图相关组件图表类Figure,表示画布上的整个图,管理图形窗口上的子图以及各种图表组件的绘制;
3,然后创建Axes子图类绘制子图,一个Figure上可以有多个子图,一个子图上所有元素都是由Axes管理,包括坐标轴类(Axis),曲线类(Line2D),文本类(Text),图例类等(Legend),当创建一个Axes对象时,会同时自动创建x轴y轴,对子图上各个子对象进行操作。
例如:
axes = self.MainUI.widget.figure.add_subplot(1,1,1)#在已经创建的UI上创建一个子图
axes.set_xlim(0,9)#x轴范围
axes.set_xlabel(“distance[m]”)#横坐标标签
axes.grid(b = True,which = ‘major’,axis = ‘both’)网格
t = []
s = []
for i in range(10):
t.append(i)
s.append(random.randint(0,1500))
axes.plot(t, s,picker=True)#画曲线图
axes.cla()#清除子图内容
self.MainUI.widget.redraw()#对子图执行完各种操作之后,及时刷新
#self.figure.canvas.draw()
具体接口以及说明见:matplotlib.axes接口使用说明
交互功能
NavigationToolbar工具栏
创建NavigationToolbar工具栏时,传递一个FigureCanvas对象作为参数,关联FigureCanvas类对象。
naviBar = NavigationToolbar(figCanvas,self)#创建工具栏
NavigationToolbar父类是QToolbar,可以使用QToolbar的一些接口函数改造工具栏。
FigureCanvas提供的事件
事件名称 | 触发动作 |
---|---|
button_press_event | 鼠标按键按下 |
button_release_event | 鼠标按键释放 |
motion_notify_event | 鼠标移动 |
scroll_event | 鼠标滚轮动作 |
key_press_event | 键盘按键按下 |
key_release_event | 键盘按键释放 |
pick_event | 对象被拾取 |
figure_enter_event | 鼠标进入一个Figure对象 |
figure_leave_event | 鼠标离开一个Figure对象 |
axes_enter_event | 鼠标进入一个子图 |
axes_leave_event | 鼠标离开一个子图 |
鼠标和键盘操作产生的事件,有如下属性:
x,y:画布上x,y的位置,单位像素;
inaxes:产生鼠标事件的Axes子图对象;
xdata,ydata:鼠标光标处x,y的坐标值
FigureCanvas类中,有两个函数用来创建和解除事件与槽函数之间的关联:
1,将事件与槽函数关联mpl_connect(eventName,function),eventName类型为字符串,为上述事件名称,function为连接到的槽函数,返回一个编号,用来标志此事件:
self.__cid = figCanvas.mpl_connect(“scroll_event”,self.do_scrollZoom)#支持鼠标滚轮缩放
2,解除事件关联mpl_disconnect(self.__cid)
自定义一个QmyFigureCanvas类
下面代码自定义了一个QmyFigureCanvas类,继承于QWidget类,作为一个绘图组件,即在Qt Designer设计界面时,将一个QWidget组件提升为QmyFigureCanvas即可。
from PyQt5.QtWidgets import QWidget
from PyQt5 import QtCore
from matplotlib.backends.backend_qt5agg import (FigureCanvasQTAgg as FigureCanvas,NavigationToolbar2QT as NavigationToolbar)#用户界面后端渲染,用来以绘图的形式输出
from PyQt5.QtWidgets import QVBoxLayout
from matplotlib.figure import Figure#图表类
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy
class QmyFigureCanvas(QWidget):
mouseMove = QtCore.pyqtSignal(numpy.float64,mpl.lines.Line2D)#自定义触发信号,用于与UI交互
# mousePress = QtCore.pyqtSignal(numpy.float64,numpy.float64)
# mouseRelease = QtCore.pyqtSignal(numpy.float64,numpy.float64)
def __init__(self,parent=None,toolbarVisible=True,showHint=False):
super().__init__(parent)
# self.figure = mpl.figure.Figure()#公共属性figure
self.figure = Figure()#公共属性figur
figCanvas = FigureCanvas(self.figure)#创建FigureCanvas对象
self.naviBar = NavigationToolbar(figCanvas,self)#创建工具栏
actList = self.naviBar.actions()
count = len(actList)
self.__lastActtionHint = actList[count-1]
self.__showHint = showHint#是否显示坐标提示
self.__lastActtionHint.setVisible(self.__showHint)
self.__showToolbar = toolbarVisible#是否显示工具栏
self.naviBar.setVisible(self.__showToolbar)
layout = QVBoxLayout(self)
layout.addWidget(self.naviBar)#添加工具栏
layout.addWidget(figCanvas)#添加FigureCanvas对象
layout.setContentsMargins(0,0,0,0)
layout.setSpacing(0)
self.__cid = figCanvas.mpl_connect("scroll_event",self.do_scrollZoom)#支持鼠标滚轮缩放
self.__cid1 = figCanvas.mpl_connect("pick_event",self.do_series_pick)#支持曲线抓取
# self.__cid2 = figCanvas.mpl_connect("button_press_event",self.do_pressMouse)#支持鼠标按下
self.__cid3 = figCanvas.mpl_connect("button_release_event",self.do_releaseMouse)#支持鼠标释放
self.__cid4 = figCanvas.mpl_connect("motion_notify_event",self.do_moveMouse)#支持鼠标移动
self.mouseIsPress = False
self.pickStatus = False
#公共函数接口
def setToolbarVisible(self,isVisible=True):#是否显示工具栏
self.__showToolbar = isVisible
self.naviBar.setVisible(isVisible)
def setDataHintVisible(self,isVisible=True):#是否显示坐标提示
self.__showHint = isVisible
self.__lastActtionHint.setVisible(isVisible)
def redraw(self):#重绘曲线,快速调用
self.figure.canvas.draw()
def do_scrollZoom(self,event):#通过鼠标滚轮缩放
ax = event.inaxes #产生事件axes对象
if ax == None:
return
self.naviBar.push_current()
xmin,xmax = ax.get_xbound()
xlen = xmax - xmin
ymin,ymax = ax.get_ybound()
ylen = ymax - ymin
xchg = event.step * xlen / 20
xmin = xmin + xchg
xmax = xmax - xchg
ychg = event.step * ylen / 20
ymin = ymin + ychg
ymax = ymax - ychg
ax.set_xbound(xmin,xmax)
ax.set_ybound(ymin,ymax)
event.canvas.draw()
def do_series_pick(self,event):#picker事件获取抓取曲线
self.series = event.artist
# index = event.ind[0]
# print("series",event.ind)
if isinstance(self.series,mpl.lines.Line2D):
self.pickStatus = True
def do_releaseMouse(self,event):#鼠标释放,释放抓取曲线
if event.inaxes == None:
return
if self.pickStatus == True:
self.series.set_color(color = "black")
self.figure.canvas.draw()
self.pickStatus = False
# self.mouseRelease.emit(event.xdata,event.ydata)
def do_moveMouse(self,event):#鼠标移动,重绘抓取曲线
if event.inaxes == None:
return
if self.pickStatus == True:
self.series.set_xdata([event.xdata,event.xdata])
self.series.set_color(color = "red")
self.figure.canvas.draw()
self.mouseMove.emit(event.xdata,self.series)#自定义触发信号,用于与UI交互
代码部分创建了一个包含工具栏的Figure()图表组件,并且自定义了鼠标滚轮缩放事件,以及当鼠标抓取直线后,进行直线在坐标范围内平移操作。
应用效果事例
将自定义的QmyFigureCanvas应用于UI中,在UI中随机绘制曲线,并添加axvline直线作为标志线,可拖动标志线进行其他操作显示,如图:
参考图书:Python Qt GUI与数据可视化编程