How can I use spacebar to close Matplotlib/Pyplot plots? - python

In Close a matplotlib figure using keyboard I learned that you can add keybinds to various matplotlib commands, including key.quit which exits the plot.
The keybinds can be assigned via:
plt.rcParams["keymap.quit"] = ['ctrl+w', 'cmd+w', 'q']
However, when I add space or spacebar to the list, space doesn't work. I know that list is defaults, so either I'm not modifying them at all or I have the name for spacebar wrong.
Does anyone know how to do this?

There is a paucity of documentation on exactly what the key mappings are for this functionality, but you can find out with this code snippet:
>>> from matplotlib import pyplot as plt
>>> fig = plt.figure()
>>> fig.canvas.mpl_connect('key_press_event', lambda evt: print(repr(evt.key)))
8
Then
>>> plt.show()
will open an empty plot window. Pressing keys in the window will print to your terminal the associated keymap code.
When pressing the space bar I get:
' '
Confirmed that setting plt.rcParams['keymap.quit'].append(' ') works--pressing the space bar closes the window.

Related

Matplotlib - remove RectangleSelector widget from the plot

I am creating a visual PyQt5 application which creates and shows Matplotlib plots. I want to be able to select some points on the plot with a mouse. I use RectangleSelector widget for it. The problem is, the drawn rectangle stays on plot after I release the mouse. Using interactive=False makes the rectangle invisible, but it is still there and breaks plot auto scale when I try to change the data to display on the plot. Here is the example with interactive=True :
After I load some other data to plot:
Is there any way to remove the RectangleSelector from the plot? Or at least to make ax.autoscale() ignore it?
You can use the "escape" key to clear the current shape. This can be redefined in the argument state_modifier_keys. For details see here.
A other way of doing it would be to this:
rectangle = RectangleSelector(...)
for artist in rectangle :
artist.set_visible(False)
rectangle.update()
this is what is done when the escape button is pressed and it will remove the current rectangle selector from your graph.
Not sure if you've resolved this yet.
I use this
def line_select_callback(self, eclick, erelease):
# At the beginning
toggle_selector.RS.set_active(False)
toggle_selector.RS.to_draw.set_visible(False)
toggle_selector.RS.update()
#....line_select_callback code....
# Last line of method/function
toggle_selector.RS.set_active(True)

Guizero's TextBox.value stops updating after pyplot.figure() is called

I want to create simple GUI using guizero library, but I found out that TextBox.value stops updating when matplotlib.pyplot.figure() is called.
I have not tried almost anything since I have no idea how to solve it. I just found the problem.
from guizero import App, TextBox
import matplotlib.pyplot as plt
plt.figure()
def print_value():
print(text_box.value)
app = App()
text_box = TextBox(app, command=print_value)
app.display()
I want to use TextBox as input for values (floats). If the plt.figure() is not in the code, I just enter the value in the UI and by calling text_box.value I can read the value (and print it immediately as in the example). However, I also want to use matplotlib.pyplot to plot the data. The problem is when the plt.figure() is in the code the text_box.value stops being updated. I can enter whatever I want in the UI but the text_box.value remains unchanged (it is either empty string as in this case or just default string value if I define it). Is there anything I'm doing wrong or is it a bug?

Python matplotlib: Removing "Configure subplots" tool from toolbar in matplotlib causes an error

I have a plot using matplotlib and I want to remove some of the buttons from the toolbar.
I have achieved removing the forward, back, home, zoom and help buttons, but when I try to remove the "Configure subplots" button I get an error.
Why can't I remove this one but I can remove the others?
My code is something like this:
import matplotlib.pyplot as plt
plt.rcParams['toolbar'] = 'toolmanager'
fig = plt.figure()
fig.canvas.manager.toolmanager.remove_tool('forward')
fig.canvas.manager.toolmanager.remove_tool('back')
fig.canvas.manager.toolmanager.remove_tool('home')
fig.canvas.manager.toolmanager.remove_tool('zoom')
fig.canvas.manager.toolmanager.remove_tool('help')
fig.canvas.manager.toolmanager.remove_tool('subplots')
If I comment the last line, the code runs without problem and I get the toolbar without the specified buttons, but if I add the last line I get the following error:
AttributeError: 'NoneType' object has no attribute 'destroy'
I have tried all type of combinations from 'subplots', in case the name is not correct like
'Subplot',
'Subplots',
'subplot',
'configure',
'configure_subplots',
'Configure subplots'

Matplotlib - Force plot display and then return to main code

This is a MWE of what I'm after, adapted from this question:
from matplotlib.pyplot import plot, draw, show
def make_plot():
plot([1,2,3])
draw()
print 'continue computation'
print('Do something before plotting.')
# Now display plot in a window
make_plot()
answer = raw_input('Back to main and window visible? ')
if answer == 'y':
print('Excellent')
else:
print('Nope')
show()
What I want is: I call the function to make the plot, the plot window appears, and then I get to go back to the prompt so I can input some value (based on that image that just displayed) and carry on with the code (the window can then close or remain there, I don't care).
What I get instead is that the window with the plot only appears after the code is completed, which is no good.
Add 1
I've tried the following with the same results, the plot window appears at the end of the code and not before:
from matplotlib.pyplot import plot, ion, draw
ion() # enables interactive mode
plot([1,2,3]) # result shows immediately (implicit draw())
# at the end call show to ensure window won't close.
draw()
answer = raw_input('Back to main and window visible? ')
if answer == 'y':
print('Excellent')
else:
print('Nope')
The same happens if I change draw() for show().
Add 2
I've tried the following approach:
from multiprocessing import Process
from matplotlib.pyplot import plot, show
def plot_graph(*args):
for data in args:
plot(data)
show()
p = Process(target=plot_graph, args=([1, 2, 3],))
p.start()
print 'computation continues...'
print 'Now lets wait for the graph be closed to continue...:'
p.join()
which results in a Python kernel has crashed error in Canopy with the message:
The kernel (user Python environment) has terminated with error code -6. This may be due to a bug in your code or in the kernel itself.
Output captured from the kernel process is shown below.
[IPKernelApp] To connect another client to this kernel, use:
[IPKernelApp] --existing /tmp/tmp9cshhw.json
QGtkStyle could not resolve GTK. Make sure you have installed the proper libraries.
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python: ../../src/xcb_io.c:274: poll_for_event: La declaración `!xcb_xlib_threads_sequence_lost' no se cumple.
I should mention I'm running Canopy in elementary OS which is based in Ubuntu 12.04.
Add 3
Also tried solution posted in this question:
import numpy
from matplotlib import pyplot as plt
if __name__ == '__main__':
x = [1, 2, 3]
plt.ion() # turn on interactive mode
for loop in range(0,3):
y = numpy.dot(x, loop)
plt.figure()
plt.plot(x,y)
plt.show()
_ = raw_input("Press [enter] to continue.")
This displays empty plot windows as the code advances (ie: the user hits [enter]) and only displays the images after the code is finished.
This solution (also in the same question) doesn't even display the plot windows:
import numpy
from matplotlib import pyplot as plt
if __name__ == '__main__':
x = [1, 2, 3]
plt.ion() # turn on interactive mode, non-blocking `show`
for loop in range(0,3):
y = numpy.dot(x, loop)
plt.figure() # create a new figure
plt.plot(x,y) # plot the figure
plt.show() # show the figure, non-blocking
_ = raw_input("Press [enter] to continue.") # wait for input from the user
plt.close() # close the figure to show the next one.
You may use plt.show(block=False), which gets rid of the blocking directly.
For your example, this could read
from matplotlib.pyplot import plot, show
def make_plot():
plot([1,2,3])
show(block=False)
print('continue computation')
print('Do something before plotting.')
# Now display plot in a window
make_plot()
answer = input('Back to main and window visible? ')
if answer == 'y':
print('Excellent')
else:
print('Nope')
None of the presented solutions work for me. I tested them with three different IDEs PyCharm, Spyder and Pyzo, using the (currently) latest Matplotlib 2.1 under Python 3.6.
What works for me, although not optimal, is to use a plt.pause command:
import matplotlib.pyplot as plt
def make_plot():
plt.plot([1, 2, 3])
# plt.show(block=False) # The plot does not appear.
# plt.draw() # The plot does not appear.
plt.pause(0.1) # The plot properly appears.
print('continue computation')
print('Do something before plotting.')
# Now display plot in a window
make_plot()
answer = input('Back to main and window visible? ')
if answer == 'y':
print('Excellent')
else:
print('Nope')
I couldn't get this to work with Canopy (not yet at least) but I could get the code to run sort of like I wanted to using the Geany IDE. This is the code that works for me, it's a very minor modification to the first block of code in the question where the show() command is moved above from the end of the file to just below the make_plot() command:
from matplotlib.pyplot import plot, draw, show
def make_plot():
plot([1,2,3])
draw()
print 'Plot displayed, waiting for it to be closed.'
print('Do something before plotting.')
# Now display plot in a window
make_plot()
# This line was moved up <----
show()
answer = raw_input('Back to main after plot window closed? ')
if answer == 'y':
print('Move on')
else:
print('Nope')
It doesn't do exactly what I want but it's close enough: it shows a plot to the user, waits till that plot window is closed and then moves on with the code. Ideally it shouldn't have to wait until the plot window is closed to move on with the code, but it's better than nothing I guess.
The code in the Add 2 section above also works in the same way and with no modifications needed in Geany, but I prefer this one because it's simpler. I'll update this answer If (when?) I get this to work with Canopy.

How can I open two different pylab figures from the same button from a wxpython gui?

I am new to python and I have a serious problem that I cannot overcome. I have created a gui using wxpython that has two text fields and a button. When this button is pressed I call a function that call another function which creates a pie figure according to the input in text boxes. The problem is that if the user do not close the figure and enter new values to the textboxes and press again the button, the program crash instead of showing a second figure. I tried to create different threads when the button is pressed the result is the same.
More specifically:
this are the functions that are called when the button is clicked:
def statistics_button(self,event):
t=threading.Thread(target=self.m_button21OnButtonClick,args=(event,))
t.start()
print t
def m_button21OnButtonClick( self, event ):
'''some code here'''
fig=statistics.mmr_dist(mmr1,mmr2)
show()
the statistics_button is called first and then this calls the m_button21OnButtonClick.
the statistics.mmr_dist function is the following:
'''some code'''
fig=pylab.figure(tit,figsize=(8,8),frameon=False)
ax=pylab.axes([0.1, 0.1, 0.8, 0.8])
pita=pylab.pie(db.values(), labels=tuple(db.keys()), autopct='%1.1f%%', shadow=True)
ti="%s-%s\n Maximum Distance = %s m\n Minimum Distance = %s m" % (ddf1.label,ddf2.label,maxdist,mindist)
title(ti, bbox={'facecolor':'0.8', 'pad':5})
'''some code'''
return fig
So far I have understood that the show() command blocks the function m_button21OnButtonClick from finishing so it cannot be called again when the button is clicked unless the figure is closed. But this is the reason I implemented different threads. Though it doesn't seem to work.
See this page for advice on getting pylab to work with wxPython--which you probably shouldn't really try (see next paragraph). The problem is that pylab uses Tkinter which is incompatible with a running copy of wxPython.
Ultimately, you should just embed your plots in wxPython. That works very well and is a better user experience anyway.
Try issuing the command pylab.ion() after you import pylab, and see if that lets you show multiple plots. That has always been my approach when needing to repeatedly show updating plots without closing the window.
Note that you'll need to create new figure and axes objects for each different plot window, otherwise plots will overwrite old plots.
For example, the following code works to produce two windows with different plots for me:
import pylab
pylab.ion()
fig1 = pylab.figure()
fig1.add_subplot(111).plot([1,2],[3,4])
pylab.draw()
fig2 = pylab.figure()
fig2.add_subplot(111).plot([5,6],[10,9])
pylab.draw()
Added
Given your follow-up comments, here is a new script that does use show(), but which displays the different plots each time pylab.draw() is called, and which leaves the plot windows showing indefinitely. It uses simple input logic to decide when to close the figures (because using show() means pylab won't process clicks on the windows x button), but that should be simple to add to your gui as another button or as a text field.
import numpy as np
import pylab
pylab.ion()
def get_fig(fig_num, some_data, some_labels):
fig = pylab.figure(fig_num,figsize=(8,8),frameon=False)
ax = fig.add_subplot(111)
ax.set_ylim([0.1,0.8]); ax.set_xlim([0.1, 0.8]);
ax.set_title("Quarterly Stapler Thefts")
ax.pie(some_data, labels=some_labels, autopct='%1.1f%%', shadow=True);
return fig
my_labels = ("You", "Me", "Some guy", "Bob")
# To ensure first plot is always made.
do_plot = 1; num_plots = 0;
while do_plot:
num_plots = num_plots + 1;
data = np.random.rand(1,4).tolist()[0]
fig = get_fig(num_plots,data,my_labels)
fig.canvas.draw()
pylab.draw()
print "Close any of the previous plots? If yes, enter its number, otherwise enter 0..."
close_plot = raw_input()
if int(close_plot) > 0:
pylab.close(int(close_plot))
print "Create another random plot? 1 for yes; 0 for no."
do_plot = raw_input();
# Don't allow plots to go over 10.
if num_plots > 10:
do_plot = 0
pylab.show()
If you want to create pie charts or other types of graphs in wxPython, then you should use PyPlot (included in wx) or matplotlib, which can be embedded in wxPython. The wxPython demo has an example of PyPlot. For matplot see here or here.

Categories

Resources