diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 404eb09248fd..2dde8c3a3a8d 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1122,6 +1122,7 @@ def tk_window_focus(): 'matplotlib.tests.test_triangulation', 'matplotlib.tests.test_transforms', 'matplotlib.tests.test_arrow_patches', + 'matplotlib.tests.test_backend_qt4', ] diff --git a/lib/matplotlib/backends/backend_qt4.py b/lib/matplotlib/backends/backend_qt4.py index 305db61c202f..933016f80e03 100644 --- a/lib/matplotlib/backends/backend_qt4.py +++ b/lib/matplotlib/backends/backend_qt4.py @@ -367,11 +367,14 @@ def idle_draw(*args): self._idle = True if d: QtCore.QTimer.singleShot(0, idle_draw) + class MainWindow(QtGui.QMainWindow): def closeEvent(self, event): self.emit(QtCore.SIGNAL('closing()')) + QtGui.QMainWindow.closeEvent(self, event) + -class FigureManagerQT( FigureManagerBase ): +class FigureManagerQT(FigureManagerBase): """ Public attributes @@ -381,29 +384,31 @@ class FigureManagerQT( FigureManagerBase ): window : The qt.QMainWindow """ - def __init__( self, canvas, num ): - if DEBUG: print('FigureManagerQT.%s' % fn_name()) - FigureManagerBase.__init__( self, canvas, num ) + def __init__(self, canvas, num): + if DEBUG: + print('FigureManagerQT.%s' % fn_name()) + FigureManagerBase.__init__(self, canvas, num) self.canvas = canvas self.window = MainWindow() self.window.connect(self.window, QtCore.SIGNAL('closing()'), - canvas.close_event) + canvas.close_event) + self.window.connect(self.window, QtCore.SIGNAL('closing()'), + self._widgetclosed) self.window.setWindowTitle("Figure %d" % num) - image = os.path.join( matplotlib.rcParams['datapath'],'images','matplotlib.png' ) - self.window.setWindowIcon(QtGui.QIcon( image )) + image = os.path.join(matplotlib.rcParams['datapath'], 'images', 'matplotlib.png') + self.window.setWindowIcon(QtGui.QIcon(image)) # Give the keyboard focus to the figure instead of the # manager; StrongFocus accepts both tab and click to focus and # will enable the canvas to process event w/o clicking. # ClickFocus only takes the focus is the window has been # clicked - # on. http://developer.qt.nokia.com/doc/qt-4.8/qt.html#FocusPolicy-enum - self.canvas.setFocusPolicy( QtCore.Qt.StrongFocus ) + # on. http://qt-project.org/doc/qt-4.8/qt.html#FocusPolicy-enum or + # http://doc.qt.digia.com/qt/qt.html#FocusPolicy-enum + self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus) self.canvas.setFocus() - QtCore.QObject.connect( self.window, QtCore.SIGNAL( 'destroyed()' ), - self._widgetclosed ) self.window._destroying = False self.toolbar = self._get_toolbar(self.canvas, self.window) @@ -419,7 +424,7 @@ def __init__( self, canvas, num ): # requested size: cs = canvas.sizeHint() sbs = self.window.statusBar().sizeHint() - self._status_and_tool_height = tbs_height+sbs.height() + self._status_and_tool_height = tbs_height + sbs.height() height = cs.height() + self._status_and_tool_height self.window.resize(cs.width(), height) @@ -428,14 +433,14 @@ def __init__( self, canvas, num ): if matplotlib.is_interactive(): self.window.show() - def notify_axes_change( fig ): + def notify_axes_change(fig): # This will be called whenever the current axes is changed if self.toolbar is not None: self.toolbar.update() - self.canvas.figure.add_axobserver( notify_axes_change ) + self.canvas.figure.add_axobserver(notify_axes_change) @QtCore.Slot() - def _show_message(self,s): + def _show_message(self, s): # Fixes a PySide segfault. self.window.statusBar().showMessage(s) @@ -445,8 +450,9 @@ def full_screen_toggle(self): else: self.window.showFullScreen() - def _widgetclosed( self ): - if self.window._destroying: return + def _widgetclosed(self): + if self.window._destroying: + return self.window._destroying = True try: Gcf.destroy(self.num) @@ -474,15 +480,19 @@ def resize(self, width, height): def show(self): self.window.show() - def destroy( self, *args ): + def destroy(self, *args): # check for qApp first, as PySide deletes it in its atexit handler - if QtGui.QApplication.instance() is None: return - if self.window._destroying: return + if QtGui.QApplication.instance() is None: + return + if self.window._destroying: + return self.window._destroying = True - QtCore.QObject.disconnect( self.window, QtCore.SIGNAL( 'destroyed()' ), - self._widgetclosed ) - if self.toolbar: self.toolbar.destroy() - if DEBUG: print("destroy figure manager") + QtCore.QObject.disconnect(self.window, QtCore.SIGNAL('destroyed()'), + self._widgetclosed) + if self.toolbar: + self.toolbar.destroy() + if DEBUG: + print("destroy figure manager") self.window.close() def get_window_title(self): diff --git a/lib/matplotlib/tests/test_backend_qt4.py b/lib/matplotlib/tests/test_backend_qt4.py new file mode 100644 index 000000000000..7806030e9727 --- /dev/null +++ b/lib/matplotlib/tests/test_backend_qt4.py @@ -0,0 +1,32 @@ +from matplotlib import pyplot as plt +from matplotlib.testing.decorators import cleanup +from matplotlib.testing.decorators import knownfailureif +from matplotlib._pylab_helpers import Gcf +import copy + +try: + import matplotlib.backends.qt4_compat + HAS_QT = True +except ImportError: + HAS_QT = False + + +@cleanup +@knownfailureif(not HAS_QT) +def test_fig_close(): + # force switch to the Qt4 backend + plt.switch_backend('Qt4Agg') + + #save the state of Gcf.figs + init_figs = copy.copy(Gcf.figs) + + # make a figure using pyplot interface + fig = plt.figure() + + # simulate user clicking the close button by reaching in + # and calling close on the underlying Qt object + fig.canvas.manager.window.close() + + # assert that we have removed the reference to the FigureManager + # that got added by plt.figure() + assert(init_figs == Gcf.figs)