Reputation: 2119
I have a class which sets up a matplotlib figure with the given data and by default it records clicks on the plot. It records every mouseclick, so zooming always add a new point. To prevent that I want to create a toggle button on the mpl toolbar to enable and disable click recording. Here is the code:
import numpy as np
import matplotlib
matplotlib.rcParams["toolbar"] = "toolmanager"
import matplotlib.pyplot as plt
from matplotlib.backend_tools import ToolToggleBase
from matplotlib.backend_bases import MouseButton
class EditPeak(object):
def __init__(self, x, y, x_extremal=None, y_extremal=None):
# setting up the plot
self.x = x
self.y = y
self.cid = None
self.figure = plt.figure()
self.press()
plt.plot(self.x, self.y, 'r')
self.x_extremal = x_extremal
self.y_extremal = y_extremal
if not len(self.x_extremal) == len(self.y_extremal):
raise ValueError('Data shapes are different.')
self.lins = plt.plot(self.x_extremal, self.y_extremal, 'ko', markersize=6, zorder=99)
plt.grid(alpha=0.7)
# adding the button
tm = self.figure.canvas.manager.toolmanager
tm.add_tool('Toggle recording', SelectButton)
self.figure.canvas.manager.toolbar.add_tool(tm.get_tool('Toggle recording'), "toolgroup")
plt.show()
def on_clicked(self, event):
""" Function to record and discard points on plot."""
ix, iy = event.xdata, event.ydata
if event.button is MouseButton.RIGHT:
ix, iy, idx = get_closest(ix, self.x_extremal, self.y_extremal)
self.x_extremal = np.delete(self.x_extremal, idx)
self.y_extremal = np.delete(self.y_extremal, idx)
elif event.button is MouseButton.LEFT:
ix, iy, idx = get_closest(ix, self.x, self.y)
self.x_extremal = np.append(self.x_extremal, ix)
self.y_extremal = np.append(self.y_extremal, iy)
else:
pass
plt.cla()
plt.plot(self.x, self.y, 'r')
self.lins = plt.plot(self.x_extremal, self.y_extremal, 'ko', markersize=6, zorder=99)
plt.grid(alpha=0.7)
plt.draw()
return
def press(self):
self.cid = self.figure.canvas.mpl_connect('button_press_event', self.on_clicked)
def release(self):
self.figure.canvas.mpl_disconnect(self.cid)
Where get_closest is the following:
def get_closest(x_value, x_array, y_array):
"""Finds the closest point in a graph to a given x_value, where distance is
measured with respect to x.
"""
idx = (np.abs(x_array - x_value)).argmin()
value = x_array[idx]
return value, y_array[idx], idx
This is the button on the toolbar.
class SelectButton(ToolToggleBase):
default_toggled = True
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def enable(self, event):
pass
def disable(self, event):
pass
I would like to activate a function when it's triggered, otherwise disable. My problem is that the enable and disable functions are defined in EditPeak class and I couldn't link them to the SelectButton class.
The easiest way you can try it (even though it doesn't really make sense with random values):
x = np.random.normal(0,1,100)
y = np.random.normal(0,1,100)
EditPeak(x, y, x[::2], y[::2])
Left click will add a new point to the closest x value in the given graph, right clicks will delete the closest.
My goal is to call EditPeak.press when the toggle button is on, and call EditPeak.release otherwise. Any improvement in the code (including style, structure) is appreciated.
Upvotes: 3
Views: 1249
Reputation: 2010
Right after:
tm.add_tool('Toggle recording', SelectButton)
you can do something like:
self.my_select_button = tm.get_tool('Toggle recording')
Now the EditPeak
instance has a reference to the SelectButton
instance created by add_tool
that can be used in EditPeak.on_clicked
. (The remainder is left as an exercise for the reader :-)
Upvotes: 1