Here is the problem. I need to create ''click catcher''. I need to turn on click catching mode by clicking on the button (Mark) and then, when I click on my plot want my program to remember points list, coordinates, etc. What is especially important - other buttons must work fine when the click catching mode is on. For example I implemented Print button. I would like to click couple of times in my plot, then, without leaving click catching mode, click on the Print button, and then see the list of cought points.
The problem is that my program sees matplotlib.widgets
buttons as an axes
and it catches points from them whenever I use any button.
I tried to use event.inaxes which
which is supposed to check if the cursor is in the axes area. So I've implemented some logic. Unfortunately it works fine only for the outside of the plot, but doesn't work at all for buttons. It seems like matplotlib sees buttons as an axes object, so it completely doesn't work.
Please help me with it, I'm quite beginner in programming and lerned all by myself (so any code review is welcome).
* tl;dr: You can see the bug very well when you turn on catching mode, click on the plot, and then click multiple time on the Print button. Each click on the Print will add to the points list additional coords.*
Here goes the code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
class ClickCatcher:
def __init__(self, window_object):
self.window_object = window_object
self.fig = self.window_object.fig
self.ax = self.window_object.ax
self.window_object.is_click_catcher_working = True
self.cid = window_object.fig.canvas.mpl_connect('button_press_event', self)
self.points, = self.ax.plot([], [], marker='.', ls='None', color='red')
self.data_x = []
self.data_y = []
def __call__(self, event):
if event.button == 1 and event.inaxes:
self.data_x.append(event.xdata)
self.data_y.append(event.ydata)
self.update_plot()
print('{:8.2f} {:8.2f}'.format(event.xdata, event.ydata))
self.window_object.click_data = (self.data_x, self.data_y)
def update_plot(self):
self.points.set_data(self.data_x, self.data_y)
self.fig.canvas.draw()
class Window:
def __init__(self):
self.is_click_catcher_working = False
self.click_data = ()
self.fig, self.ax = plt.subplots()
self.configure_appearance()
self._plot, = plt.plot(np.arange(0, 10, 0.1), np.sin(np.arange(0, 10, 0.1)),
drawstyle='steps-mid', color='black', lw=1)
self._add_buttons()
def configure_appearance(self):
self.fig.set_size_inches(10, 5)
self.ax.tick_params(axis='both', labelsize=14)
self.ax.set_xlabel('Energy (keV)', fontsize=12)
self.ax.set_ylabel('Number of counts', fontsize=12)
plt.subplots_adjust(bottom=0.25)
def _add_buttons(self):
self.button_mark = Button(plt.axes([0.4, 0.05, 0.1, 0.075]), 'Mark')
self.button_mark.on_clicked(self.activate_marking)
self.button_print = Button(plt.axes([0.6, 0.05, 0.1, 0.075]), 'Print')
self.button_print.on_clicked(self.print_points)
def catch_coordinates(self):
click = ClickCatcher(self)
def activate_marking(self, event):
if self.is_click_catcher_working:
pass
else:
self.is_click_catcher_working = True
self.catch_coordinates()
def print_points(self, event):
output = '({:.2f}, {:.2f}) ; '*len(self.click_data[0])
print(output.format(*self.click_data[0], *self.click_data[1]))
if __name__ == '__main__':
win = Window()
plt.show()