Matplotlib key_press_event never fires in macosx backend - matplotlib

I'm using Matplotlib with the macosx backend. I'm following the example here to hook up a simple event handler:
import matplotlib.pyplot as plt
def _on_keypress(event):
print('Hit on_keypress()')
plt.connect('key_press_event', _on_keypress)
# Call plt.plot() a few times ...
plt.show()
When I run this script from the terminal a figure is shown, but pressing keys shows the pressed key in the terminal, and _on_keypress is never called.
It seems like the plot window is somehow not in focus? Yet I get the same behavior (characters go to terminal) whether I click on the figure or the terminal.
If I open a debugger I can verify the handler is attached to the canvas:
(Pdb) fig = plt.gcf()
(Pdb) fig.canvas.key_press_event('a')
Hit on_keypress()
What is going on?

Related

Temporarily set Matplotlib backend for one cell, reverting afterwards

Normally when using IPython + Jupyter + Matplotlib, I choose a Matplotlib backend using the %matplotlib magic in IPython.
However, sometimes I want to change to a different backend for one specific cell, and then immediately revert to the backend that I was using before. For example, I sometimes want to use the widget backend for a single plot, but I don't want to have it active for the rest of my notebook.
I could of course put %matplotlib widget at the top of the cell and then %matplotlib inline at the bottom of the cell, but is there a nicer solution? Either using a single "temporary" %matplotlib magic (happy to write my own if it's not complicated), or using a context manager.
Is it as simple as this, or is there more to it?
#contextmanager
def mpl_backend(backend):
original = matplotlib.get_backend()
yield matplotlib.use(backend)
matplotlib.use(original)

Debugging FuncAnimation

Consider
def animate(k):
# do stuff, update plots
anm = FuncAnimation(myFig, animate, interval=1, repeat=False)
Why is it not possible to debug anything inside animate?
I can run the same code without visualization in barebones Python and perfectly debug it, but somehow FuncAnimation is totally "opaque".
My IDE is Spyder 4.0.1
UPDATE
I am still struggling to debug FuncAnimation.
Does anyone know how to do that?
I can't see what's happening inside animate.

Using matplotlib with Qt5 backend with an existing QApplication running, in the same process

We have a Qt5 application that uses PySide2. Recently, we wanted to show plots using matplotlib while our PySide2 application is running in the same process (in a different thread) but then matplotlib crashes (when using PySide2) or freezes before drawing (when using PyQt5).
Here is a minimal sample, uncomment the 23rd line to make matplotlib crash or freeze:
from threading import Thread
from PySide2.QtWidgets import QApplication, QLabel
import matplotlib
matplotlib.use('QT5Agg')
import matplotlib.pyplot as plt
def start_qt_app():
t = Thread(target=qt_app_thread_func)
t.start()
def qt_app_thread_func():
app = QApplication()
label = QLabel("Hello World")
label.show()
app.exec_()
# Uncomment the line below to make matplotlib crash.
#start_qt_app()
plt.plot([1, 2, 3, 4])
plt.show()
input("Press enter to quit.")
print("Finished.")
My guess is that this is related to the limitation that we can only have 1 QApplication running in a process. So this causes something to go wrong in matplotlib.
How can I solve this problem? One solution that comes to my mind is to create a proxy object for matplotlib that runs matplotlib in another process but I am not sure if this would be the least labor intensive solution. Maybe I can somehow make matplotlib use the existing QApplication? I cannot run our PySide2 app in another process since it uses dynamically created numpy arrays to be passed from main thread to the GUI, and it would cost performance to start it in another process.
As #ImportanceOfBeingErnest points out, matplotlib can live with Qt as the official example shows: Embedding in Qt.
Example:
from PySide2.QtWidgets import QApplication, QLabel
import matplotlib
matplotlib.use('QT5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
if __name__ == '__main__':
app = QApplication()
label = QLabel("Hello World")
label.show()
canvas = FigureCanvas(Figure(figsize=(5, 3)))
ax = canvas.figure.subplots()
ax.plot([1, 2, 3, 4])
canvas.show()
app.exec_()
you should use asyncqt to manage ui and application threads.from asyncqt QEventLoop is a way you can accomplish with
await loop.run_in_executor(exec, method_name)
asyncqt is a spin off library from quamash which is written for PyQt5. example codes are same. So upon your preference, you may use asyncqt with PySide2 or quamash with PyQt5 to make your app responsive when background tasks are running.
asyncqt examples
asyncqt 0.7.0 pypi
Quamash 0.6.1

Pylab / matplotlib magic in IPython: supress loading message

Currently in IPython, when you call %pylab inline or %matplotlib inline the following message displays under the code block.
"Populating the interactive namespace from numpy and matplotlib"
How I can suppress this message from being displayed?
I don't think there's a builtin way of suppressing that message since if you look at the %pylab magic function in this file you can see that the print statement is hard coded in there.
If this is a one-off kind of thing you can simply comment/remove that print line from your local library. (Typically it would be found at /usr/local/lib/python2.7/dist-packages/IPython/core/magics/pylab.py.) Or possibly redirect stdout to devnull or something like that.

ipython pylab figures not inline

I've installed iPython + SciPy Superpack on a Mac (OSX Lion).
If I plot using matplotlib.pyplot, it will pop up a window with the graph, and I close this for the ipython kernel to continue.
from matplotlib import pyplot as plt
plt.plot([1, 2, 3], [3, 6, 9])
plt.show()
However, If I try the inline (starting with --pylab inline or --pylab=inline) and import pylab, instead of a plot inside the notebook (which I expect), I get an external window, which never renders anything.
Still in an external window:
import pylab
pylab.plot([1, 2, 3], [3, 6, 9])
pylab.show()
Since I've started the notebook with ipython notebook --pylab=inline it should already be so, but if I use %pylab inline in a cell and run it, I get the help, and the same code above creates a blank window, and hangs the kernel - I can only force kill the window.
How should this work?
Okay - the problem was that the original ipython notebook process was still running (I'd not killed it) and the new one with the inline flag was running on a different port.
If you are having this issue - first save all your notebooks, then check you haven't got other processes running and kill any that shouldn't be running.
If you want to avoid this confusion, you can set NotebookApp.port_retries=0 in your configuration, in which case later notebook calls will give up, rather than listen on a new port. (Credit to minrk, in comments)