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
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
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)) + ' ' + str(val) + '\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) for row in data]) # select the maximal force select_mvc(force)
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.
We used Matplotlib’s
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.