yt-project/yt

Question/Bug: matplotlib Qt backend specification

chrishavlin opened this issue · 4 comments

A question on yt slack reported the following error:

File ~/anaconda3/lib/python3.11/site-packages/yt/visualization/base_plot_types.py:137, in PlotMPL.__init__(self, fsize, axrect, norm_handler, figure, axes)
    134     self.axes = axes
    135 self.interactivity = get_interactivity()
--> 137 figure_canvas, figure_manager = self._get_canvas_classes()
    138 self.canvas = figure_canvas(self.figure)
    139 if figure_manager is not None:

TypeError: cannot unpack non-iterable NoneType object

The selected matplotlib backend in this instance is QtAgg (found with import matplotlib; print(str(matplotlib.get_backend())), so I think because yt's BACKEND_SPECS only contains explicitly versioned Qt entries as keys:

"Qt4Agg": ["backend_qt4agg", "FigureCanvasQTAgg", None],
"Qt5Agg": ["backend_qt5agg", "FigureCanvasQTAgg", None],

_get_canvas_classes is catching a KeyError and returning None, resulting in the above TypeError :

def _get_canvas_classes(self):
if self.interactivity:
key = str(matplotlib.get_backend())
else:
key = "agg"
try:
module, fig_canvas, fig_manager = BACKEND_SPECS[key]
except KeyError:
return

According to https://matplotlib.org/stable/api/backend_qt_api.html, specifying specific Qt backends is now discouraged, so I'm thinking BACKEND_SPECS needs a new QtAgg entry. Also looks like Qt4 support was dropped in matlab 3.5.0 (https://matplotlib.org/stable/users/prev_whats_new/whats_new_3.5.0.html#qt-backends) and 3.5.0 is currently the minimum version for yt so I think we can just replace those 2 lines with a single QtAgg line? That all sound sensible? I'm not super familiar with backends, so wanted to check before getting too deep...

Is it necessary to specify a backend in general? I've heard matplotlib isn't great at finding defaults, but I know I've had to change the backend through an environment variable on at least one system to get (interactive?) plotting to work at all. If setting the backend explicitly is not necessary, maybe it would be better to let users set this if they have to, based on what works/is available on their system.

Well it already is possible for the user to set the matplotlib backend, but internally yt tries to do its own detection in order to get the proper canvas objects (based on whatever matplotlib detects). It may be that this whole architecture could be removed or refactored, but I think that'd be a much larger refactor (throughout many of the yt plot window classes) compared to just getting the generic QtAgg backend to work.

BACKEND_SPECS should definitely be updated in this situation. What I don't understand is why 4-years-ago me thought it was a good idea to silently swallow KeyErrors and return None... Seeing that this does causes issues, I would suggest to just drop the try/except logic.

Ok, great. Thanks for the confirmation, @neutrinoceros . And I can push up a fix later today -- I spent some time yesterday experimenting with the qt back end options in new environments and I understand it much better now.