Last active
April 11, 2022 10:24
-
-
Save djwbrown/3e24bf4e0c5e9ee156a5 to your computer and use it in GitHub Desktop.
matplotlib.pyplot blackmagic to make CTRL-C work again
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import matplotlib.pyplot as plt | |
try: | |
# Put matplotlib.pyplot in interactive mode so that the plots are shown in a background thread. | |
plt.ion() | |
while(True): | |
plt.show() | |
except KeyboardInterrupt: | |
print "" | |
sys.exit(0) |
doesn't work.
This is what I do to be able to exit the loop:
import matplotlib.pyplot as plt
def handle_close(evt):
handle_close.stop = True
handle_close.stop = False
plt.ion()
plt.show(block=False)
plt.gcf().canvas.mpl_connect('close_event', handle_close)
while(True):
if handle_close.stop:
break
plt.plot([0,1], [0,1])
plt.gcf().canvas.flush_events()
plt.show()
plt.ioff()
Still doesn't react to KeyboardInterrupt
though.
My use-case involved updating a figure in a for-loop and exit the app once the user requests the termination. In the below example, the user can exit the loop prematurely in three ways:
- Hit "e" or "q" (requires the figure to be the active window)
- Close the figure via the close button
- Interrupt by hitting CTRL+C or CMD+C in the console
The nice thing is that figure/axes can be reused over multiple iterations.
The code below requires python3+. I tested with matplotlib 3.4.1 and python3.8.
import sys
import numpy as np
import matplotlib.pyplot as plt
def test():
def handle_key(event):
if event.key in "eEqQ":
nonlocal show
show = False
print("Stop on key press")
return False
def handle_close(event):
nonlocal show
show = False
print("Stop on close")
show = True
t = np.linspace(0, np.pi, 100)
fig, ax = plt.subplots()
fig.canvas.mpl_connect("key_release_event", handle_key)
fig.canvas.mpl_connect("close_event", handle_close)
for i in np.linspace(1,10,101):
if show:
try:
ax.clear()
ax.plot(t, np.sin(i*t))
# Return values of waitforbuttonpress():
# - True if key was pressed
# - False if mouse button was pressed
# - (None if timeout was reached without event)
fig.canvas.draw()
while not fig.waitforbuttonpress() and show:
pass
fig.canvas.flush_events()
except KeyboardInterrupt:
print("Stop on interrupt")
show = False
plt.close(fig)
print("Exit normally")
test()
More simple solution: Add at beginning of your program
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I had to change it to
to make it work. Enabling blocking was necessary to actually show the plots and break to exit the loop in case the user clicks the close button.