Использование библиотеки Matplotlib. Как рисовать графики с помощью Matplotlib при использовании wxPython
Matplotlib - очень удобная библиотека для рисования графиков. Особенно приятно, что графики, отображаемые с помощью этой библиотеки можно легко встраивать в приложения, написанные с использованием различных библиотек для построения интерфейса. На момент написания этой статьи Matplotlib может встраиваться в приложения, написанные с использованием библиотек wxPython, pyQT и pyGTK. Далее будет показано, как можно рисовать графики внутри окон wxPython.
Для интеграции Matplotlib с другими библиотеками используется пакет matplotlib.backends, в который входят в частности модули matplotlib.backends.backend_wxagg для интеграции с приложениями wxPython, matplotlib.backends.backend_qt4agg для интеграции с pyQT и matplotlib.backends.backend_gtkagg для интеграции с pyGTK. В этот же пакет входит модуль matplotlib.backends.backend_pdf для рисования графиков в файл PDF.
Часто библиотека Matplotlib не используется в объектно-ориентированном стиле, когда просто вызываются функции из модуля pylab: pylab.plot(...), pylab.show(...) и т.д. Однако Matplotlib - это полностью объектно-ориентированная библиотека, где разные части графика представлены своим классом, и при интеграции Matplotlib в ваше приложение нужно будет использовать непосредственно эти классы.
Для примера мы будем делать программу, рисующую функцию Гаусса . При этом интервал по оси X, а также параметры sigma и mu будут задаваться пользователем в окне. Вот как будет выглядеть конечный результат:

Для начала создадим два файла. Первый файл - plotgauss.py содержит класс приложения wxPython, и именно этот файл нужно будет запускать с помощью команды python plotgauss.py. Не будем вдаваться в подробности частей программы, которые не относятся непосредственно к Matplotlib, будем считать, что вы представляете, как работать с библиотекой wxPython.
Итак, файл plotgauss.py:
# -*- coding: UTF-8 -*-
import wx
import pylab
import numpy
from mainwindow import MainWindow
class PlotGauss(wx.App):
def __init__(self, *args, **kwds):
wx.App.__init__ (self, *args, **kwds)
def OnInit(self):
wx.InitAllImageHandlers()
mainWnd = MainWindow(None, -1, "")
mainWnd.Show()
self.SetTopWindow (mainWnd)
return True
if __name__ == "__main__":
application = PlotGauss(False)
application.MainLoop()
Здесь нет никаких особенностей - создается экземпляр класса приложения PlotGauss и запускается цикл обработки сообщений с помощью метода MainLoop().
В качестве главного окна используется экземпляр класса MainWindow из файла mainwindow.py. Именно в нем и содержится все самое интересное.
Чтобы встроить график Matplotlib в приложение wxPython, нужно проделать следующие операции:
- Создать экземпляр класса matplotlib.figure.Figure.
- Из созданной фигуры получить экземпляр класса matplotlib.axes.Axes.
- Создать панель (канву) для рисования с помощью Matplotlib. Для работы из wxPython это будет экземпляр класса matplotlib.backends.backend_wxagg.FigureCanvasWxAgg.
- Нарисовать график, используя методы экземпляр класса matplotlib.axes.Axes, который был создан на втором шаге.
Все это продемонстрировано в файле mainwindow.py. Строки, относящиеся к работе с Matplotlib отмечены комменатрием !!!, чтобы их можно было проще найти. Чтобы не раздувать статью, я не буду приводить полностью текст этого файла, который вы можете и так скачать, разберем только отдельные его части.
Класс главного окна приложения MainWindow является производным от класса wx.Frame, его конструктор выглядит следующим образом:
import wx
import matplotlib.figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
class MainWindow (wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = wx.DEFAULT_FRAME_STYLE
super (MainWindow, self).__init__ (*args, **kwds)
self.SetTitle (u"Matplotlib + wxPython")
# Дискрет, с которым будет строиться график
self._graphStep = 0.01
# Установим размер главного окна
self.SetSize ((600, 500))
# Заполнить главное окно элементами управления
self._createGui ()
self.updateBtn.Bind (wx.EVT_BUTTON, handler=self._onUpdateClick)
# Нарисовать график
self._drawGraph ()
Думаю, что отдельные шаги работы конструктора понятны из комментариев, самое интересное содержится в методах _createGui(), в котором создаются все элементы управления, и _drawGraph(), который занимается непосредственно отображением графика.
Сначала рассмотрим метод _createGui(). В приведенном ниже исходном тексте пропущены строки, не относящиеся к Matplotlib, такие как созданеи кнопки, полей ввода и надписей.
import wx
import matplotlib.figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
class MainWindow (wx.Frame):
...
def _createGui (self):
"""
Заполнить главное окно элементами управления
"""
# Элементы управления для ввода минимального значения по оси X
...
# Элементы управления для ввода максимального значения по оси X
...
# Элементы управления для ввода sigma
...
# Элементы управления для ввода mu
...
# Создание кнопки
...
# !!!
# 1. Создание фигуры
self.figure = matplotlib.figure.Figure ()
# 2. Создание осей
self.axes = self.figure.add_subplot (1, 1, 1)
# 3. Создание панели для рисования с помощью Matplotlib
self.canvas = FigureCanvasWxAgg (self, -1, self.figure)
self.canvas.SetMinSize ((100, 100))
# Размещение элементов управления в окне
mainSizer = wx.FlexGridSizer (0, 1)
mainSizer.AddGrowableCol (0)
mainSizer.AddGrowableRow (5)
# Размещение элементов интерфейса
...
mainSizer.Add (self.canvas, flag=wx.ALL | wx.EXPAND, border=2)
self.SetSizer (mainSizer)
self.Layout()
Сначала создается экземпляр класса matplotlib.figure.Figure, конструктор которого в простейшем случае может не принимать никаких параметров, но при желании можно задавать некоторые параметры, такие как разрешение (актуально для рисования не на экране), цвет фона и некоторые другие, которые вы можете посмотреть в документации.
Чтобы создать оси, в которых будет происходить рисование, используется метод add_subplot() экземпляра класса Figure. Этот метод аналогичен функции subplot(), которая рассматривалась в статье Как нарисовать несколько графиков в одном окне. Этот метод возвращает экземпляр класса matplotlib.axes.Axes. Позже этот экземпляр будет использоваться непосредственно для рисования графика.
После этого создается панель, на которой можно будет рисовать с помощью библиотеки Matplotlib. При использовании wxPython, нужно создать экземпляр класса matplotlib.backends.backend_wxagg.FigureCanvasWxAgg. Конструктор этого класса принимает три параметра:
- Родитель создаваемой панели.
- Идентификатор.
- Экземпляр класса matplotlib.figure.Figure.
В качестве первого параметра используется само главное окно. Т.к. мы не будем обращаться к панели по идентификатору, то в качестве второго параметра передается -1. В качестве третьего параметра передается фигура, созданная ранее, в которой мы создали оси.
Надо отметить, что созданная таким образом канва для рисования является производной от класса wx.Panel, поэтому ее можно использовать как обычный элемент управления wxPython, в том числе и добавлять в сайзеры, что и делается дальше в скрипте.
Если бы мы попытались запустить программу в таком виде (без реализации метода _drawGraph()), то увидели бы следующее окно:

То есть Matplotlib уже работает, библиотека нарисовала оси, осталось только наполнить их содержимым. Это делает метод _drawGraph().
...
def _drawGraph (self):
"""
Нарисовать график согласно введенным настройкам
"""
# Получим введенные значения.
# Т.к. это всего лишь демонстрационный пример, проверка правильности ввода простейшая
try:
xmin = float (self.xminText.Value)
xmax = float (self.xmaxText.Value)
sigma = float (self.sigmaText.Value)
mu = float (self.muText.Value)
except ValueError, e:
print e
return
# Координаты точек по осям X и Y
xvals = numpy.arange (xmin, xmax + self._graphStep, self._graphStep)
yvals = self.gauss (sigma, mu, xvals)
# !!!
# Удалим предыдущий график, если он есть
self.axes.clear()
# Нарисуем новый график
self.axes.plot (xvals, yvals)
# Включим сетку
self.axes.grid ()
# self.axes.legend ([u"Функция Гаусса"], prop={"family": "verdana"})
self.axes.legend ([u"Gaussian"])
# Установим пределы по осям
self.axes.set_xlim ([xmin, xmax])
# Обновим окно
self.canvas.draw()
В начале функции считываются введенные пользователем значения параметров, затем создаются массивы для рисования функции Гаусса:
...
@staticmethod
def gauss (sigma, mu, x):
"""
Функция Гаусса, которую отображаем
"""
return 1.0 / (sigma * numpy.sqrt (2.0 * numpy.pi)) * numpy.exp (-((x - mu) ** 2) / (2 * sigma * sigma))
После этого происходит само рисование с использованием экземпляра класса Axes. Этот класс содержит методы, очень напоминающие функции из модуля pylab. Из используемых в данном примере методов по названию отличается только метод set_xlim(), аналог которого в pylab называется просто xlim().
В завершении рисования надо не забыть обновить канву (панель), вызвав метод draw() для экземпляра класса FigureCanvasWxAgg.
Подобным образом можно рисовать и другие типы графика, а также настраивать их внешний вид, нужно только найти подходящий метод в классах Axes, Figure или других используемых в Matplotlib классах.
Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.