Skip to content
This repository

use QMainWindow.closeEvent for close events #1498

Merged
merged 1 commit into from over 1 year ago

5 participants

Martin Teichmann Michael Droettboom Benjamin Root Ryan May Thomas A Caswell
Martin Teichmann

The current code uses the QWidget.destroyed signal
to emit a close event into matplotlib. This is a very
bad idea, as it is a heavy abuse of Qt functionality.

When a main window is closed in Qt, it and its children
are deleted if the DeleteOnClose flag is set (which we
did set). This causes the QWidget.destroyed signal to be
called. Setting the DeleteOnClose flag is a very bad
idea, since objects should only be deleted by the
garbage collector. Soon after, PyQt4 will complain
that "the underlying C++ object has been deleted", since
the object is alive for python but dead for C++.

This patch changes the machinery to use the
QMainWindow.closeEvent to emit a close_event, which is
what one actually wants.

Martin Teichmann tecki use QMainWindow.closeEvent for close events
The current code uses the QWidget.destroyed signal
to emit a close event into matplotlib. This is a very
bad idea, as it is a heavy abuse of Qt functionality.

When a main window is closed in Qt, it and its children
are deleted if the DeleteOnClose flag is set (which we
did set). This causes the QWidget.destroyed signal to be
called. Setting the DeleteOnClose flag is a very bad
idea, since objects should only be deleted by the
garbage collector. Soon after, PyQt4 will complain
that "the underlying C++ object has been deleted", since
the object is alive for python but dead for C++.

This patch changes the machinery to use the
QMainWindow.closeEvent to emit a close_event, which is
what one actually wants.
e407926
Michael Droettboom
Owner

Who's our resident Qt4 expert these days? @ddale? git blame shows a lot of people involved. I don't feel terribly qualified to assess whether this change makes sense.

Benjamin Root
Collaborator

This might actually resolve a race condition I have seen when closing an animation with the Qt4 backend. I am not qualified to assess the correctness here, but @dopplershift might be.

Ryan May
Collaborator

I'll claim expertise with C++ Qt4, but not so much expertise in either Qt4 with Python or, more importantly, our (ab)use of it (though I hope to rectify that in the somewhat near future)

I will say that this commit seems to clean things up. However, I'm not using the GUI backends on a common enough basis right now to give it a good shakeout.

Michael Droettboom mdboom merged commit a203d02 into from
Michael Droettboom mdboom closed this
Thomas A Caswell
Collaborator

One (possibly too late) concern is that this completely shadows the closeEvent behavior of QMainWindow. I think the sub-class's closeEvent should include the line QMainWindow.closeEvent(self,event) to make sure that things are properly cascaded up the inheritance chain.

Michael Droettboom
Owner

@tacaswell: That's a good point. As I'm not a Qt expert, can you propose a fix -- ideally with a standalone test -- in a new PR? I'll go ahead and file a new issue for it so it doesn't get lost, in any event.

Thomas A Caswell tacaswell referenced this pull request from a commit in tacaswell/matplotlib
Thomas A Caswell Re-wired signal/slot connections so that the figure in removed from
Gcf when it is closed.

In PR #1498 the attribute WA_DeleteOnClose was no longer set on the
QtMainWindow object in QtFigureManager.  The signal connection that
was being used to remove the figure from Gcf when the window was closed
was tied to the `destroyed()` signal of QtMainWindow, which is no
longer being destroyed.  Thus, gca and gcf would return references
to no-longer visible figures/axes.  _widgetclosed is now called when
MainWindow emits 'closing()'.
121acdc
Thomas A Caswell tacaswell referenced this pull request from a commit in tacaswell/matplotlib
Thomas A Caswell Re-wired signal/slot connections so that the figure in removed from
Gcf when it is closed.

In PR #1498 the attribute WA_DeleteOnClose was no longer set on the
QtMainWindow object in QtFigureManager.  The signal connection that
was being used to remove the figure from Gcf when the window was closed
was tied to the `destroyed()` signal of QtMainWindow, which is no
longer being destroyed.  Thus, gca and gcf would return references
to no-longer visible figures/axes.  _widgetclosed is now called when
MainWindow emits 'closing()'.
45f8701
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Nov 14, 2012
Martin Teichmann tecki use QMainWindow.closeEvent for close events
The current code uses the QWidget.destroyed signal
to emit a close event into matplotlib. This is a very
bad idea, as it is a heavy abuse of Qt functionality.

When a main window is closed in Qt, it and its children
are deleted if the DeleteOnClose flag is set (which we
did set). This causes the QWidget.destroyed signal to be
called. Setting the DeleteOnClose flag is a very bad
idea, since objects should only be deleted by the
garbage collector. Soon after, PyQt4 will complain
that "the underlying C++ object has been deleted", since
the object is alive for python but dead for C++.

This patch changes the machinery to use the
QMainWindow.closeEvent to emit a close_event, which is
what one actually wants.
e407926
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 7 additions and 15 deletions. Show diff stats Hide diff stats

  1. +7 15 lib/matplotlib/backends/backend_qt4.py
22 lib/matplotlib/backends/backend_qt4.py
@@ -208,18 +208,6 @@ def __init__( self, figure ):
208 208 w,h = self.get_width_height()
209 209 self.resize( w, h )
210 210
211   - # JDH: Note the commented out code below does not work as
212   - # expected, because according to Pierre Raybaut, The reason is
213   - # that PyQt fails (silently) to call a method of this object
214   - # just before detroying it. Using a lambda function will work,
215   - # exactly the same as using a function (which is not bound to
216   - # the object to be destroyed).
217   - #
218   - #QtCore.QObject.connect(self, QtCore.SIGNAL('destroyed()'),
219   - # self.close_event)
220   - QtCore.QObject.connect(self, QtCore.SIGNAL('destroyed()'),
221   - lambda: self.close_event())
222   -
223 211 def __timerEvent(self, event):
224 212 # hide until we can test and fix
225 213 self.mpl_idle_event(event)
@@ -377,6 +365,10 @@ def idle_draw(*args):
377 365 self._idle = True
378 366 if d: QtCore.QTimer.singleShot(0, idle_draw)
379 367
  368 +class MainWindow(QtGui.QMainWindow):
  369 + def closeEvent(self, event):
  370 + self.emit(QtCore.SIGNAL('closing()'))
  371 +
380 372 class FigureManagerQT( FigureManagerBase ):
381 373 """
382 374 Public attributes
@@ -391,8 +383,9 @@ def __init__( self, canvas, num ):
391 383 if DEBUG: print('FigureManagerQT.%s' % fn_name())
392 384 FigureManagerBase.__init__( self, canvas, num )
393 385 self.canvas = canvas
394   - self.window = QtGui.QMainWindow()
395   - self.window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
  386 + self.window = MainWindow()
  387 + self.window.connect(self.window, QtCore.SIGNAL('closing()'),
  388 + canvas.close_event)
396 389
397 390 self.window.setWindowTitle("Figure %d" % num)
398 391 image = os.path.join( matplotlib.rcParams['datapath'],'images','matplotlib.png' )
@@ -619,7 +612,6 @@ def draw_rubberband( self, event, x0, y0, x1, y1 ):
619 612 def configure_subplots(self):
620 613 self.adj_window = QtGui.QMainWindow()
621 614 win = self.adj_window
622   - win.setAttribute(QtCore.Qt.WA_DeleteOnClose)
623 615
624 616 win.setWindowTitle("Subplot Configuration Tool")
625 617 image = os.path.join( matplotlib.rcParams['datapath'],'images','matplotlib.png' )

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.