I have run into this trouble with show()
over and over again, and I'm sure I'm doing something wrong but not sure of the 'correct' way to do what I want.
And [I think] what I want is some way to block in the main thread until an event happens in the GUI thread, something like this works the first time:
from matplotlib import pyplot as p
from scipy import rand
im = (255*rand(480,640)).astype('uint8')
fig = p.figure()
ax = fig.add_subplot(111)
ax.imshow(im)
# just any mutable container for storing a click
s = [-1, -1]
def onclick(event):
if event.xdata is not None and event.ydata is not None:
s[0] = event.xdata
s[1] = event.ydata
p.close()
cid = fig.canvas.mpl_connect('button_press_event', onclick)
p.show()
print s
the p.show()
blocks until p.close()
is called in the event handler. But when run the same code the second time, it races past the p.show()
and prints that original s, [-1, -1]
.
I have read conflicting information on whether or not p.show()
can or should be called more than once from the same program. It seems it was designed to be used once, and only once at the end of a script. Other use cases seem to break pyplot
somehow (state machine?).
I've tried to use combinations of p.draw()
and p.ion()
and p.ioff()
, but could not get the behaviour I wanted (either things weren't blocking properly or the plots weren't appearing at the right times).
I'm also confused about how the event handler is able to see s
at all here, and whether this is a poor way of passing in/out information. If I don't use a mutable container like an array or list, the information I want set by the event handler just gets lost as a local variable. is there some other method I'm missing out on , where the GUI thread can pass signals back to the main thread? is there a way to block in main, without periodic polling or busy waiting , for a signal from the event handler before continuing?
So I guess ultimately my main question is:
Is there a neat replacement for p.show()
, that does what I want (the same behaviour as p.show()
has the first time), or does this kind of code require a complete rethink/rewrite ?
I was able to resolve my issue today. if anyone else is interested in changing the behaviour of
show()
, read on for how you can do it:I noticed this paragraph titled multiple calls to show supported on the what's new part of the matplotlib webpage:
This was in 'what's new' for version
1.0.1
, at time of writing the version in synaptic is still back on0.99.3
. I was able to download and build from sourcev1.0.1
. Additional packages I also required to satisfy dependencies werelibfreetype6-dev tk-dev tk8.5-dev tcl8.5-dev python-gtk2-dev
.Now with
matplotlib.__version__ == 1.0.1
, the following code blocks how I would expect:Couple ideas of varying quality:
If you don't like s being a global variable, you could make onclick() a callable object attach it to that.
Your callback could acquire/release a lock to control program flow (little dirty).
You could actively poll s to control program flow (very dirty).
You can manually control the drawing of your figures via fig.canvas.draw()
I noticed a difference between running the code
Directly within the Python interpretor (command line)
Putting it in a Python script and running it from the command line ("python script.py")
Both give a blocking behavior, which is ok.
From the interpretor both images appear, from the command line only the first appears.