Moving around in a Matplotlib figure before selecting point of interest

We have seen how Matplotlib’s ginput()
function can be used to mark regions of interest on plots. Sometimes, the fine details of a plot can only be viewed by using the zoom and pan features to visualise the data.
Zoom (magnifying glass icon) and pan buttons (square with 4 arrows icon) are available by default in the toolbar of a Matplotlib figure. For example, to zoom into a section of the data, a user would click the zoom button, use the cursor to click-and-hold on the figure, and draw a box around the section to zoom into.
The problem with using zoom and ginput()
to visualise a section before marking a point of interest, is that the user has to first click on the figure to zoom into a section, but ginput()
receives data from the first click as the point of interest itself. How might we plot the data to allow a user to zoom or pan around a figure indefinitely, until they are ready to select a point of interest?
The function below uses Matplotlib’s waitforbuttonpress()
function to wait for a keyboard entry from a user before receiving the point of interest. According to the documentation, waitforbuttonpress()
blocks other functions so a user can interact with the figure. It returns True
if a key is pressed but False
if a mouse button is pressed, so a user can zoom or pan to move around in the figure using the mouse, before using a keyboard press to send data from the next click to ginput()
.
In the example below, we would like a user to select the maximal force from a subject’s maximal voluntary contraction. We first define a function to plot the force signal. The Matplotlib Cursor
widget and useblit=True
parameter is used to set up a horizontal and vertical line that spans the axes and moves with the pointer. This makes it easier for the user to find maximal or minimal points by eye. (Matplotlib widgets are funky – check them out here).
Next, some text is printed to tell the user that they can zoom or pan around the figure, and press the spacebar key when ready to send input to the computer. Once the user is happy that the data are well visualised, they click on the zoom button to un-select it and press the spacebar key. At this point, the user is prompted to click once to select the maximal force. The sample number (i.e. the index number) and force value are then written to the text file index_mvc.txt
.
The function is implemented by reading in data from the text file mvc.lvm
and calling the select_mvc()
function. The text file is available here, and the screenshots show how the user interacts with the figure:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Cursor
def select_mvc(data):
fig = plt.figure(figsize=(11, 7))
ax = fig.add_subplot(1, 1, 1)
ax.plot(data)
plt.ylabel('Force (V)')
plt.xlabel('Sample')
cursor = Cursor(ax, useblit=True, color='k', linewidth=1)
zoom_ok = False
print('\nZoom or pan to view, \npress spacebar when ready to click:\n')
while not zoom_ok:
zoom_ok = plt.waitforbuttonpress()
print('Click once to select MVC force: ')
val = plt.ginput(1)
# print('Selected values: ', val)
plt.close()
open('index_mvc.txt', 'w').close()
with open('index_mvc.txt', 'a') as file:
file.write('index value\n')
file.write(str(int(val[0][0])) + ' ' + str(val[0][1]) + '\n')
# read in force data
infile = open('mvc.lvm', 'r')
line = infile.readlines()[23:]
infile.close()
data = [row.strip().split(',') for row in line]
force = np.array([float(row[1]) for row in data])
# select the maximal force
select_mvc(force)
|
Figure 1: Matplotlib figure is ready for user to zoom or pan. Cursor appears as a horizontal and vertical line that spans the axes.
Figure 2: The user clicks the zoom button (magnifying glass icon), and click-and-holds to draw a box around the section to zoom into.
Figure 3: Once zoomed, the user clicks the zoom button again to un-select it, places the cursor at the maximal force, presses the spacebar, then clicks on the maximal force. The x and y coordinates of this data point (i.e. the index and the force values) will be written to the text file.
Summary
We used Matplotlib’s waitforbuttonpress()
and ginput()
functions to allow a user to interact with a figure before selecting a point of interest and recording the data. Matplotlib’s Cursor
widget can be used to customise the appearance of the cursor on a plot.