一般的书上介绍matplotlib的绘图功能都主要是介绍matplotlib.pyplot模块中的指令式绘图功能,因为这种方式与MATLAB很相似,使用过MATLAB的人转而使用Matplotlib绘图会比较容易上手。但是这种方式将绘图的效果都固定在程序里了,例如曲线的颜色、线条类型等,如果要修改就需要修改源程序重新运行,所以这种方式只适合做研究时的快速数据可视化,不能做成GUI应用程序进行交互式绘图。
Matplotlib是完全采用面向对象方法设计的,图的各个组成元素,如图、子图、坐标轴、曲线等都有相应的类,还有各种涉及图的操作的类。通过类的接口函数和属性可以对图的各个组成元素进行完全的控制,这种方法称为面向对象(Object-Oriented,OO)方法。面向对象方法适合在GUI应用程序中使用,因为在程序中可以对这些对象实例进行操作。
下面先用一个简单的程序演示在GUI应用程序中使用Matplotlib的面向对象方法绘图的基本方法。示例Demo14_1目录下的文件Demo14_1GUI.py的完整代码如下:
## 程序文件: Demo14_1GUI.py ## 使用matplotlib 面向对象方法在GUI中绘图 import sys import numpy as np import matplotlib as mpl from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar) from PyQt5.QtWidgets import QApplication, QMainWindow from PyQt5.QtCore import Qt class QmyMainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) #调用父类构造函数 self.setWindowTitle("Demo14_1, GUI中的matplotlib绘图") ## rcParams[]参数设置,以正确显示汉字 mpl.rcParams['font.sans-serif']=['KaiTi','SimHei'] #汉字字体 mpl.rcParams['font.size']=12 #字体大小 mpl.rcParams['axes.unicode_minus'] =False #正常显示负号 self.__iniFigure() #创建绘图系统,初始化窗口 self.__drawFigure() #绘图 ##==========自定义函数================= def __iniFigure(self): ##创建绘图系统,初始化窗口 self.__fig=mpl.figure.Figure(figsize=(8, 5)) #单位英寸 self.__fig.suptitle("plot in GUI application") #总的图标题 figCanvas = FigureCanvas(self.__fig) #创建FigureCanvas对象 naviToolbar=NavigationToolbar(figCanvas, self) #创建工具栏 naviToolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.addToolBar(naviToolbar) #添加工具栏到主窗口 self.setCentralWidget(figCanvas) def __drawFigure(self): ##绘图 t = np.linspace(0, 10, 40) y1=np.sin(t) y2=np.cos(2*t) ax1=self.__fig.add_subplot(1,2,1) #matplotlib.axes.Axes 类 ax1.plot(t,y1,'r-o',label="sin", linewidth=1, markersize=5) ax1.plot(t,y2,'b:',label="cos",linewidth=2) ax1.set_xlabel('X 轴') #x轴标题 ax1.set_ylabel('Y 轴',fontsize=14) #y轴标题 ax1.set_xlim([0,10]) ax1.set_ylim([-1.5,1.5]) ax1.set_title("曲线") #子图标题 ax1.legend() #自动创建图例 ax2=self.__fig.add_subplot(1,2,2) #matplotlib.axes.Axes 类 week=["Mon","Tue","Wed","Thur","Fri","Sat","Sun"] sales=np.random.randint(200,400,7) ax2.bar(week,sales) #绘制柱状图 ax2.set_xlabel('week days') #x轴标题 ax2.set_ylabel('参观人数') #y轴标题 ax2.set_title("柱状图") #子图标题 ## ============窗体测试程序 ================================ if __name__ == "__main__": app = QApplication(sys.argv) form=QmyMainWindow() form.show() sys.exit(app.exec_())
程序运行时的界面如图 14-2所示。注意这个程序是用PyQt5的GUI应用程序框架创建的。为了减少程序的复杂度,没有使用可视化方法设计UI窗体,而是采用纯代码的方式。程序中定义了一个基于QMainWindow的窗口类QmyMainWindow,界面构造和绘图都是在QmyMainWindow的构造函数中实现的。
图14-2 使用Matplotlib绘图的PyQt5 GUI程序界面
2.后端(backend)Matplotlib的绘图结果可以有各种输出形式,例如在Python交互式环境中输出绘图结果,嵌入到wxpython、pygtk、Qt等GUI框架中输出绘图结果,将绘图结果输出为图片文件,或在Web应用程序中输出绘图结果。要实现这些不同的输出,Matplotlib需要有不同的处理方法,这些不同的输出功能就称为后端(backend)。而相对的就是前端(frontend),是用户面对的代码。例如,对于一段相同的前端绘图代码,后端就是实现场景后面的工作以生成绘图输出。
有两种后端,一种是用户界面后端(也称为交互式后端),例如用于wxpython、pygtk、tkinter、qt4、qt5、macosx的后端,另一种是用于生成图片文件的后端,如生成PNG、SVG、PDF等文件。
对于用户界面后端,Matplotlib还将渲染器(renderer)和画布(canvas)分离开来,以实现更灵活的定制功能。Matplotlib使用的主要的渲染器是基于Anti-Grain Geometry C 库的Agg渲染器。除了macosx,所有的用户界面都使用Agg渲染器,因而有WXAgg、GTK3Agg、QT4Agg、QT5Agg、TkAgg等。有些用户界面也支持其他的渲染器,如Cairo渲染器,因而有GTK3Cairo、QT4Cairo、QT5Cairo等。
在Matplotlib安装目录的“backends”子目录里是这些后端的模块文件,例如有backend_ gtk3agg.py、backend_gtk3cairo.py、backend_qt5agg.py、backend_qt5cairo.py等。本书只考虑QT5Agg渲染器,所以在程序的import部分有如下的语句:
from matplotlib.backends.backend_qt5agg import ( FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
这是从matplotlib.backends.backend_qt5agg模块中导入了FigureCanvas类和NavigationToolbar2QT类,并将NavigationToolbar2QT类重命名为NavigationToolbar。
追踪查看FigureCanvas类的源程序,可以发现它的上层父类之一是QWidget,所以,它是用于在PyQt5 GUI界面上显示Matplotlib绘图结果的Widget组件。要在PyQt5 GUI窗体上显示Matplotlib绘图结果,必须创建一个FigureCanvas界面组件,就如同使用PyQtChart模块绘制图表时需要先创建一个QChartView界面组件。
3.程序解读(1)为正常显示汉字的设置
在自定义类QmyMainWindow的构造函数里实现窗体界面构造和Matplotlib绘图。
首先对Matplotlib的全局设置做一些修改,以便正确显示汉字。可以修改全局字典变量matplotlib.rcParams里的参数,也可以修改配置文件matplotlibrc的内容。程序中的设置语句如下:
mpl.rcParams['font.sans-serif']=['KaiTi','SimHei'] #汉字字体 mpl.rcParams['font.size']=12 #字体大小 mpl.rcParams['axes.unicode_minus'] =False #正常显示负号
第1行语句是设置字体族(font family)“sans-serif”的参数,第3行语句是为了正常显示负号。
Matplotlib中将字体分为5种字体族,分别是“serif”“sans-serif”“cursive”“fantasy”“monospace”,每一种字体族可以设定多种字体。在默认的matplotlibrc文件中使用的字体族是“sans-serif”,这个字体族的字体不包含汉字字体,所以无法正常显示汉字。
第1行语句将’font.sans-serif’参数设置为[‘KaiTi’, ‘SimHei’],Matplotlib将优先使用字体’KaiTi’,如果找不到这个字体的字体文件,就使用后面的字体’SimHei’,可以为一个字体族设置多个字体。Windows系统中常见的汉字字体名称如下。
- KaiTi =楷体;SimHei=黑体;FangSong=仿宋。
- STSong=华文宋体;STFangsong=华文仿宋;STHeiti=华文黑体。
关于字体的设置可以查看配置文件matplotlibrc的默认内容,在Matplotlib 3.0.0用户手册的第89页。
(2)创建绘图界面组件的函数__iniFigure()
构造函数里调用自定义函数__iniFigure()创建绘图相关的界面组件,此函数的完整代码见前面的程序清单。程序首先创建了一个matplotlib.figure.Figure类对象self.__fig:
self.__fig=mpl.figure.Figure(figsize=(8, 5)) #单位英寸
Figure类就是用于绘图的图表类,是Matplotlib中一个主要的类,它负责管理一个图形窗口中子图、各种图表组件的绘制,其功能类似于PyQtChart模块中的QChart类。但是一个Figure里可以绘制多个子图,而QChart只能绘制一个图表。
创建FigureCanvas对象时必须传递一个Figure类对象,程序中是:
figCanvas = FigureCanvas(self.__fig) #创建FigureCanvas对象
这样,Figure类对象self.__fig就用figCanvas作为图形渲染区域(画布),self.__fig的各种绘图操作都在此画布上显示出来。
创建NavigationToolbar类导航工具栏naviToolbar时传递一个FigureCanvas对象作为参数,即
naviToolbar=NavigationToolbar(figCanvas, self) #创建工具栏
这样创建的导航工具栏的操作就是针对关联的FigureCanvas类对象figCanvas。Navigation- Toolbar的父类是QToolBar,所以可以使用setToolButtonStyle()函数设置按钮显示方式,并且添加它作为主窗口的工具栏。
程序运行时,图14-2中的工具栏与图14-1中的工具栏有些差异,例如图14-2的工具栏中有“Customize”按钮对子图进行设置,而图14-1的工具栏中没有这个按钮。两个工具栏的“Subplots”按钮弹出的对话框的界面也不同,这就是由于使用了不同的后端引起的。
FigureCanvas的父类是QWidget,所以其对象示例可以作为主窗口的中心组件。Navigation- Toolbar和FigureCanvas还有其他的一些功能,在后面再具体介绍。
(3)实现绘图功能的函数__drawFigure()
构造函数里调用自定义函数__drawFigure()绘图,其完整代码参见前面的代码清单。
函数__drawFigure()实现的绘图功能与程序Demo14_1Script.py中的几乎相同,但是实现的方法不同。在文件Demo14_1GUI.py的import部分没有导入matplotlib.pyplot,它完全使用面向对象的方法绘图。
在使用NumPy的功能准备好数据后,程序首先创建了一个子图,代码是:
ax1=self.__fig.add_subplot(1,2,1) #子图1
self.__fig是matplotlib.figure.Figure类对象,是整个图。使用Figure.add_subplot()函数创建了一个对象ax1。函数Figure.add_subplot()与matplotlib.pyplot.subplot()参数格式和功能完全相同,这里不再对参数做详细的说明,读者可以通过内建函数help()获取该函数的详细帮助信息,输入指令是:
>>> from matplotlib.figure import Figure >>> help (Figure.add_subplot)
这里创建的对象ax1是matplotlib.figure.Axes类型,它是管理一个子图区域绘图的类。通过Axes类的接口函数在子图区域画图,例如ax1使用plot()函数绘制了两条曲线,Axes.plot()函数的使用方法与matplotlib.pyplot.plot()函数相同。
Axes类通过属性和接口函数对子图的各个组成元素,如曲线、坐标轴范围、标题、网格线、图例等进行操作。一般通过一组set_和get_函数对一个属性进行设置和获取,例如Axes.set_xlim()函数设置x轴坐标范围,Axes.get_xlim()函数返回x轴的坐标范围。这与matplotlib.pyplot模块中的操作方法不同,pyplot.xlim()函数既可以设置x轴坐标范围,也可以返回x轴坐标范围。
Axes类是Matplotlib绘图中最主要的一个类,一个子图上的所有元素都由Axes管理,所以在GUI中进行Matplotlib绘图主要就是Axes类及其管理的各个子对象的操作,如坐标轴(Axis类)、曲线(Line2D类)、文本(Text类)、图例(Legend类)的操作。这些操作都使用面向对象的方法,与matplotlib.pyplot中的指令式操作的方法不同,但实现的功能相同。
本节先通过一个简单示例演示了在GUI程序中使用Matplotlib绘图的基本方法,下一节再对Matplotlib绘图时常用到的各个类的使用方法进行详细介绍。
本文截选自《Python Qt GUI与数据可视化编程》
王维波,栗宝鹃,张晓东 著
- PyQt5教程书籍,pyqt5快速开发实例教程
- PythonQt5GUI快速编程
- 示例丰富的PythonGUI编程和数据可视化编程的实用指南
本书介绍在Python中使用PyQt5和其他模块进行GUI和数据可视化编程的方法。第一部分介绍PyQt5设计GUI程序的基本框架,包括GUI应用程序的基本结构、窗体UI可视化设计与窗体业务逻辑的设计、信号与槽的特点和使用等。第二部分介绍GUI程序设计中一些主要功能模块的使用,包括基本界面组件、事件处理、数据库、绘图、多媒体等。第三部分先介绍使用PyQtChart和PyQtDataVisualization进行二维和三维数据可视化设计的方法,再介绍将Matplotlib嵌入PyQt5 GUI应用程序窗口界面中进行数据可视化的编程方法。通过研读本书,读者可以掌握使用PyQt5、PyQtChart、Matplotlib等模块进行GUI应用程序和数据可视化设计的方法。
本书适合具有Python编程基础,并想通过Python设计GUI应用程序或在GUI应用程序中实现数据可视化的读者阅读和参考。
,