This is the first of a series of how-to tutorials, primarily centered around cool things that you can do with Python, particularly the Numpy, Scipy, and matplotlib libraries. Matplotlib (mpl) is a robust and versatile plotting package, with which we can display data in a wide variety of graphical styles. mpl charts are generally initialized by executing Python scripts, or within a Python interpreter session (ie, iPython). However, at times it may be useful or even necessary to display a chart with which the user can interact dynamically via a point-and-click interface, namely, a GUI. There are several GUI frameworks out there with Python bindings, but PyQt4, a set of Python bindings for the native-C++ Qt framework, is the one with which I've worked the most, and it's suited my purposes just fine. For a basic working knowledge of the PyQt4 syntax, I recommend the Zetcode tutorial or the resources at TutorialsPoint; Summerfield provides the best in-depth covereage if/when you want to get serious. This tutorial presupposes at least a cursory familiarity with PyQt.
sudo apt-get install python-qt4
on Debian/Ubuntu, google for further details), every viable PyQt4 application is going to need some fairly standard boilerplate code.
The function of this code will be to import PyQt, define a subclass of the PyQt QMainWindow class (this is used as the container for pretty much any nontrivial PyQt application, in which you want to include multiple widgets),
and then define a main() method, which actually generates the window. We can do this with the following code:
from PyQt4 import QtCore, QtGui, Qt
import sys
class ApplicationWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.initUI()
def initUI(self):
self.main_frame = QtGui.QWidget()
self.setCentralWidget(self.main_frame)
def main():
qApp = QtGui.QApplication(sys.argv)
a=ApplicationWindow()
a.show()
sys.exit(qApp.exec_())
if __name__ == '__main__':
main()
python basic.py
, or within an Python interpreter session via !python basic.py
. The result should be a blank window, with a titlebar displaying the name of the file.
Now for the fun part. In matplotlib (referred to from here on out as mpl for brevity), all objects are drawn onto a basic area called a FigureCanvas. However, there are different instantiations of the FigureCanvas, depending on which GUI framework one wishes to use to display a given mpl creation. Normally, this is not something the user has to worry about, since the pyplot library will automate all of this. However, if we wish to embed a mpl chart in something besides mpl, we need to import the proper library that allows mpl to create an image that will play nice with the GUI framework in which we are embedding it. Therefore, we will redefine the standard FigureCanvas class with the following statement:
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
In addition, we need the Figure class, because this is the primary object that mpl can "paint" onto its backends.
from matplotlib.figure import Figure
Now, we can define a new subclass of the PyQt QMainWindow, in which we use our imported classes. We will need to use the QLayout() containers to tell PyQt how to position the graph we are inserting into the QMainWindow. The code for this is shown below:
from PyQt4 import QtCore, QtGui, Qt
import sys
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import numpy as np
class ApplicationWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.initUI()
def initUI(self):
self.main_frame = QtGui.QWidget()
self.setWindowTitle("Matplotlib Figure in a Qt4 Window")
self.fig=Figure()
self.axes = self.fig.add_subplot(111)
self.x = np.arange(0.0, 1.0, 0.01)
self.y = np.cos(2*np.pi*self.x + 5) + 2
self.axes.plot(self.x, self.y)
self.canvas=FigureCanvas(self.fig)
self.canvas.setParent(self.main_frame)
self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
self.canvas.setFocus()
vbox = QtGui.QVBoxLayout()
vbox.addWidget(self.canvas)
self.main_frame.setLayout(vbox)
self.setCentralWidget(self.main_frame)
def main():
qApp = QtGui.QApplication(sys.argv)
a=ApplicationWindow()
a.show()
sys.exit(qApp.exec_())
if __name__ == '__main__':
main()
This should result in a beautiful graph of a sine wave, as seen below.
However, if you are familiar with mpl, you are probably not yet satisfied, and perhaps are even shaking your fists in anger at the false promises which led you to this page. That is because the graph at which you are staring
has none of the convenient interactive mpl keyboard functionality which you have come to know and love. Never fear, for our next concern will be
We can import the mpl Navigation Toolbar into PyQt4 in a fashion almost completely analogous to how we imported the FigureCanvas:
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
from matplotlib.backend_bases import key_press_handler
self.ntb = NavigationToolbar(self.canvas, self.main_frame)
vbox = QtGui.QVBoxLayout()
vbox.addWidget(self.canvas)
vbox.addWidget(self.ntb)
self.main_frame.setLayout(vbox)
self.setCentralWidget(self.main_frame)
self.canvas.mpl_connect('key_press_event', self.on_key_press)
def on_key_press(self, event):
print('you pressed', event.key)
key_press_handler(event, self.canvas, self.ntb)
from PyQt4 import QtCore, QtGui, Qt
import sys
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
from matplotlib.backend_bases import key_press_handler
class ApplicationWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.initUI()
def initUI(self):
self.main_frame = QtGui.QWidget()
self.setWindowTitle("Matplotlib Figure in a Qt4 Window With NavigationToolbar")
self.fig=Figure()
self.axes = self.fig.add_subplot(111)
self.x = np.arange(0.0, 1.0, 0.01)
self.y = np.cos(2*np.pi*self.x + 5) + 2
self.axes.plot(self.x, self.y)
self.canvas=FigureCanvas(self.fig)
self.canvas.setParent(self.main_frame)
self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
self.canvas.setFocus()
self.ntb = NavigationToolbar(self.canvas, self.main_frame)
self.canvas.mpl_connect('key_press_event', self.on_key_press)
vbox = QtGui.QVBoxLayout()
vbox.addWidget(self.canvas) # the matplotlib canvas
vbox.addWidget(self.ntb)
self.main_frame.setLayout(vbox)
self.setCentralWidget(self.main_frame)
def on_key_press(self, event):
print('you pressed', event.key)
key_press_handler(event, self.canvas, self.ntb)
def main():
qApp = QtGui.QApplication(sys.argv)
a=ApplicationWindow()
a.show()
sys.exit(qApp.exec_())
if __name__ == '__main__':
main()