prevent matplotlib ginput registering click on widget button

617 views Asked by At

I've written a code to register the x values clicked in a plot window using ginput, it works fine and only registers when you click within the window.

I then wanted to add some control buttons using matplotlib widgets which I did and they work fine too with the methods, all good so far...

But, my problem is that when I click on the button, the coordinates of the button are also registered by ginput, that I don't want. Is there any way to prevent that happening? Making the button area non-active for ginput or somehow detecting that and rejecting those clicks?

I'm also happy to use an alternative to ginput, I'm not really wedded to any particular method (I'm very new to GUI in python and just want to set up a simple example).

Here is my reproducible example, clicking on the test button also adds lines to the list :-(

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button

class Index:
    def test(self, event):
        print ("test")

# fake data
x=np.arange(30)
y=x**2
times=[]

fig,ax=plt.subplots()

ax.plot(x,y)
callback = Index()
buttonname=['test']
colors=['white']
idx=[0.2]
bax,buttons={},{}

# set up list of buttons.
for i,col,button in zip(idx,colors,buttonname):
  bax[button] = plt.axes([0.92, i, 0.07, 0.07])
  buttons[button] = Button(bax[button],button,color=col,hovercolor='green')
  buttons[button].on_clicked(getattr(callback,button))

# register click on plot
while True:
     pts=plt.ginput(1)
     print ("pts is",pts)
     timebegin=pts[0][0]
     times.append(timebegin)
     ax.axvline(x=timebegin)
     print ("adding",timebegin)
     print ("all ",times)
     plt.pause(0.05)

Screenshot of the example:

enter image description here

EDIT: I've thought of a fudge for the moment, where in the button method I add a pop to remove the entry from the times list and also undraw the lines (I also declare times and lines as a global), it works but it is clunky and not very elegant, as the program draw the mistaken line and then deletes it again, ugh.

# in the main code I now append the ax.axvline to a list "lines"
def test(self,event):
    times.pop()
        times.pop()
        lines.pop().remove()
2

There are 2 answers

0
tdy On BEST ANSWER

You can do times.append(nan) in the callback, so then if times[-1] is nan, pop() it and ignore the click. There is some delay in registering the button event, so pause() briefly before checking times[-1].

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button

times = []

# on click event, append nan
class Index:
    def test(self, event):
        global times
        times.append(np.nan)
        print('test')

fig, ax = plt.subplots()
x = np.arange(30)
y = x**2
ax.plot(x, y)

callback = Index()
bax = fig.add_axes([0.92, 0.2, 0.07, 0.07])
button = Button(bax, 'Test', color='white', hovercolor='green')
button.on_clicked(callback.test)

while True:
    pts = plt.ginput(1)

    # allow callback to finish (duration may need to be adjusted)
    plt.pause(0.15)

    # ignore if newest element is nan
    if times and np.isnan(times[-1]):
        print(f'ignoring {times[-1]}')
        times.pop()
    else: # otherwise store and plot
        timebegin = pts[0][0]
        print(f'adding {timebegin}')
        times.append(timebegin)
        ax.axvline(x=timebegin)

    print(f'times = {times}')
    plt.pause(0.05)
0
enoraarg On

A way to avoid that problem would be not to use buttons to terminate the input, but instead use the mouse_stop parameter of the ginput function (described here) which works with a mouse button or the enter key. Then it's possible to add a text to the plot to explicit this method for the user... less interactive than a button, but it works.