HI modified one of the examples and it brings up the chart window with the custom toolbar. However, nothing happens with the “pickevent”
“”"
This demo demonstrates how to embed a matplotlib (mpl) plot
into a PyQt4 GUI application, including:
-
Using the navigation toolbar
-
Adding data to the plot
-
Dynamically modifying the plot’s properties
-
Processing mpl events
-
Saving the plot to a file from a menu
The main goal is to serve as a basis for developing rich PyQt GUI
applications featuring mpl plots (using the mpl OO API).
Eli Bendersky (eliben@…287…)
License: this code is in the public domain
Last modified: 19.01.2009
“”"
from future import division
import sys, os, random
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import matplotlib
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
from matplotlib.figure import Figure
import math
import os
import sys
import matplotlib
from matplotlib import verbose
from matplotlib.backend_bases import cursors
from matplotlib.figure import Figure
try:
from PyQt4 import QtCore, QtGui, Qt
except ImportError:
raise ImportError(“Qt4 backend requires that PyQt4 is installed.”)
cursord = {
cursors.MOVE : QtCore.Qt.SizeAllCursor,
cursors.HAND : QtCore.Qt.PointingHandCursor,
cursors.POINTER : QtCore.Qt.ArrowCursor,
cursors.SELECT_REGION : QtCore.Qt.CrossCursor,
}
class NavigationToolbarCustom(NavigationToolbar):
def init(self, *args, **kwargs):
pixmap = QtGui.QPixmap()
pixmap.load(’:/cross.png’)
cursors.SELECT_POINT = pixmap
super(NavigationToolbar, self).init(*args, **kwargs)
def _init_toolbar(self):
self.basedir = os.path.join(matplotlib.rcParams[ ‘datapath’ ],‘images’)
a = self.addAction(self._icon(‘home.svg’), ‘Home’, self.home)
a.setToolTip(‘Reset original view’)
a = self.addAction(self._icon(‘back.svg’), ‘Back’, self.back)
a.setToolTip(‘Back to previous view’)
a = self.addAction(self._icon(‘forward.svg’), ‘Forward’, self.forward)
a.setToolTip(‘Forward to next view’)
self.addSeparator()
a = self.addAction(self._icon(‘move.svg’), ‘Pan’, self.pan)
a.setToolTip(‘Pan axes with left mouse, zoom with right’)
a = self.addAction(self._icon(‘zoom_to_rect.svg’), ‘Zoom’, self.zoom)
a.setToolTip(‘Zoom to rectangle’)
a = self.addAction(self._icon(‘hand.svg’), ‘Select’,
self.selectPointMode)
a.setToolTip(‘Select the nearest data point’)
self.addSeparator()
a = self.addAction(self._icon(‘subplots.png’), ‘Subplots’,
self.configure_subplots)
a.setToolTip(‘Configure subplots’)
a = self.addAction(self._icon(‘filesave.svg’), ‘Save’,
self.save_figure)
a.setToolTip(‘Save the figure’)
self.buttons = {}
Add the x,y location widget at the right side of the toolbar
The stretch factor is 1 which means any resizing of the toolbar
will resize this label instead of the buttons.
if self.coordinates:
self.locLabel = QtGui.QLabel( “”, self )
self.locLabel.setAlignment(
QtCore.Qt.AlignRight | QtCore.Qt.AlignTop )
self.locLabel.setSizePolicy(
QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Ignored))
labelAction = self.addWidget(self.locLabel)
labelAction.setVisible(True)
reference holder for subplots_adjust window
self.adj_window = None
def mouse_move(self, event):
print ‘mouse_move’, event.button
if not event.inaxes or not self._active:
if self._lastCursor != cursors.POINTER:
self.set_cursor(cursors.POINTER)
self._lastCursor = cursors.POINTER
else:
if self._active==‘ZOOM’:
if self._lastCursor != cursors.SELECT_REGION:
self.set_cursor(cursors.SELECT_REGION)
self._lastCursor = cursors.SELECT_REGION
if self._xypress:
x, y = event.x, event.y
lastx, lasty, a, ind, lim, trans = self._xypress[0]
self.draw_rubberband(event, x, y, lastx, lasty)
elif (self._active==‘PAN’ and
self._lastCursor != cursors.MOVE):
self.set_cursor(cursors.MOVE)
self._lastCursor = cursors.MOVE
elif self._active==‘SELECT’:
if self._lastCursor != cursors.SELECT_POINT:
QtGui.QApplication.restoreOverrideCursor()
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(cursors.SELECT_POINT))
self._lastCursor = cursors.SELECT_POINT
if event.inaxes and event.inaxes.get_navigate():
try: s = event.inaxes.format_coord(event.xdata, event.ydata)
except ValueError: pass
except OverflowError: pass
else:
if len(self.mode):
self.set_message(’%s : %s’ % (self.mode, s))
else:
self.set_message(s)
else: self.set_message(self.mode)
def selectPointMode(self, *args):
if self._active == ‘SELECT’:
self._active = None
else:
self._active = ‘SELECT’
if self._idPress is not None:
self._idPress = self.canvas.mpl_disconnect(self._idPress)
self.mode = ‘’
if self._idRelease is not None:
self._idRelease = self.canvas.mpl_disconnect(self._idRelease)
self.mode = ‘’
if self._active:
self._idRelease = self.canvas.mpl_connect(
‘button_press_event’, self.selectPoint)
self.mode = ‘pixel select mode’
self.canvas.widgetlock(self)
else:
self.canvas.widgetlock.release(self)
self.set_message(self.mode)
def selectPoint(self, event):
if event.inaxes and event.inaxes.get_navigate():
print 'selectPoint: ', 1
self.xdatastart=event.xdata
print 'selectPoint: ', 2
self.ydatastart=event.ydata
self.xstart=event.x
self.ystart=event.y
print 'selectPoint: ', 3
self._banddraw = self.canvas.mpl_connect(
‘motion_notify_event’,self.drawband)
print 'selectPoint: ', 4
self._idRelease = self.canvas.mpl_disconnect(self._idRelease)
self._idRelease = self.canvas.mpl_connect(
‘button_release_event’, self.selectSecondPoint)
print 'selectPoint: ', 5
def selectSecondPoint(self, event):
if event.inaxes and event.inaxes.get_navigate():
print 'selectSecondPoint: ', 1
self._banddraw=self.canvas.mpl_disconnect(self._banddraw)
print 'selectSecondPoint: ', 2
self._idRelease = self.canvas.mpl_disconnect(self._idRelease)
print 'selectSecondPoint: ', 3
self._idRelease = self.canvas.mpl_connect(
‘button_press_event’, self.selectPoint)
self.draw_rubberband(event, 0, 0, 0, 0)
print 'selectSecondPoint: ', 4
self.emit(
QtCore.SIGNAL(‘pickEvent’),
self.xdatastart,
self.ydatastart,
event.xdata,
event.ydata
)
print 'selectSecondPoint: ', 5
def drawband(self, event):
self.draw_rubberband(event,self.xstart, self.ystart, event.x, event.y)
def onPick(self, xstart, ystart, xend, yend):
xstart_i, ystart_i = self.getIndices(xstart, ystart)
xend_i, yend_i = self.getIndices(xend, yend)
if xstart_i > xend_i: xstart_i, xend_i = xend_i, xstart_i
if ystart_i > yend_i: ystart_i, yend_i = yend_i, ystart_i
try:
indices = self.indices[ystart_i:yend_i+1, xstart_i:xend_i+1]
print indices
self.emit(QtCore.SIGNAL(‘pickEvent’), indices.flatten())
except TypeError:
pass
def getIndices(self, xdata, ydata):
xIndex = locateClosest(xdata, self.xPixelLocs)
yIndex = locateClosest(ydata, self.yPixelLocs)
return xIndex, yIndex
def locateClosest(point, points):
compare = numpy.abs(points-point)
return numpy.nonzero(numpy.ravel(compare==compare.min()))[0]
class AppForm(QMainWindow):
def init(self, parent=None):
QMainWindow.init(self, parent)
self.setWindowTitle(‘Demo: PyQt with matplotlib’)
self.create_menu()
self.create_main_frame()
self.create_status_bar()
self.textbox.setText(‘1 2 3 4’)
self.on_draw()
def save_plot(self):
file_choices = “PNG (.png)|.png”
path = unicode(QFileDialog.getSaveFileName(self,
‘Save file’, ‘’,
file_choices))
if path:
self.canvas.print_figure(path, dpi=self.dpi)
self.statusBar().showMessage(‘Saved to %s’ % path, 2000)
def on_about(self):
msg = “”" A demo of using PyQt with matplotlib:
-
Use the matplotlib navigation bar
-
Add values to the text box and press Enter (or click “Draw”)
-
Show or hide the grid
-
Drag the slider to modify the width of the bars
-
Save the plot to a file using the File menu
-
Click on a bar to receive an informative message
“”"
QMessageBox.about(self, “About the demo”, msg.strip())
def on_pick(self, event):
The event received here is of the type
matplotlib.backend_bases.PickEvent
···
It carries lots of information, of which we’re using
only a small amount here.
box_points = event.artist.get_bbox().get_points()
msg = “You’ve clicked on a bar with coords:\n %s” % box_points
QMessageBox.information(self, “Click!”, msg)
def on_draw(self):
“”" Redraws the figure
“”"
str = unicode(self.textbox.text())
self.data = map(int, str.split())
x = range(len(self.data))
clear the axes and redraw the plot anew
self.axes.clear()
self.axes.grid(self.grid_cb.isChecked())
self.axes.bar(
left=x,
height=self.data,
width=self.slider.value() / 100.0,
align=‘center’,
alpha=0.44,
picker=5)
self.canvas.draw()
def create_main_frame(self):
self.main_frame = QWidget()
Create the mpl Figure and FigCanvas objects.
5x4 inches, 100 dots-per-inch
self.dpi = 100
self.fig = Figure((5.0, 4.0), dpi=self.dpi)
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self.main_frame)
Since we have only one plot, we can use add_axes
instead of add_subplot, but then the subplot
configuration tool in the navigation toolbar wouldn’t
work.
self.axes = self.fig.add_subplot(111)
Bind the ‘pick’ event for clicking on one of the bars
self.canvas.mpl_connect(‘pick_event’, self.on_pick)
self.canvas.mpl_connect(‘pickevent’, self.onPick)
Create the navigation toolbar, tied to the canvas
self.mpl_toolbar = NavigationToolbarCustom(self.canvas, self.main_frame)
Other GUI controls
self.textbox = QLineEdit()
self.textbox.setMinimumWidth(200)
self.connect(self.textbox, SIGNAL(‘editingFinished ()’), self.on_draw)
self.draw_button = QPushButton("&Draw")
self.connect(self.draw_button, SIGNAL(‘clicked()’), self.on_draw)
self.grid_cb = QCheckBox(“Show &Grid”)
self.grid_cb.setChecked(False)
self.connect(self.grid_cb, SIGNAL(‘stateChanged(int)’), self.on_draw)
slider_label = QLabel(‘Bar width (%):’)
self.slider = QSlider(QtCore.Qt.Horizontal)
self.slider.setRange(1, 100)
self.slider.setValue(20)
self.slider.setTracking(True)
self.slider.setTickPosition(QSlider.TicksBothSides)
self.connect(self.slider, SIGNAL(‘valueChanged(int)’), self.on_draw)
Layout with box sizers
hbox = QHBoxLayout()
for w in [ self.textbox, self.draw_button, self.grid_cb,
slider_label, self.slider]:
hbox.addWidget(w)
hbox.setAlignment(w, QtCore.Qt.AlignVCenter)
vbox = QVBoxLayout()
vbox.addWidget(self.canvas)
vbox.addWidget(self.mpl_toolbar)
vbox.addLayout(hbox)
self.main_frame.setLayout(vbox)
self.setCentralWidget(self.main_frame)
def create_status_bar(self):
self.status_text = QLabel(“This is a demo”)
self.statusBar().addWidget(self.status_text, 1)
def create_menu(self):
self.file_menu = self.menuBar().addMenu("&File")
load_file_action = self.create_action("&Save plot",
shortcut=“Ctrl+S”, slot=self.save_plot,
tip=“Save the plot”)
quit_action = self.create_action("&Quit", slot=self.close,
shortcut=“Ctrl+Q”, tip=“Close the application”)
self.add_actions(self.file_menu,
(load_file_action, None, quit_action))
self.help_menu = self.menuBar().addMenu("&Help")
about_action = self.create_action("&About",
shortcut=‘F1’, slot=self.on_about,
tip=‘About the demo’)
self.add_actions(self.help_menu, (about_action,))
def add_actions(self, target, actions):
for action in actions:
if action is None:
target.addSeparator()
else:
target.addAction(action)
def create_action( self, text, slot=None, shortcut=None,
icon=None, tip=None, checkable=False,
signal=“triggered()”):
action = QAction(text, self)
if icon is not None:
action.setIcon(QIcon(":/%s.png" % icon))
if shortcut is not None:
action.setShortcut(shortcut)
if tip is not None:
action.setToolTip(tip)
action.setStatusTip(tip)
if slot is not None:
self.connect(action, SIGNAL(signal), slot)
if checkable:
action.setCheckable(True)
return action
def main():
app = QApplication(sys.argv)
form = AppForm()
form.show()
app.exec_()
if name == “main”:
main()