Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

use QMainWindow.closeEvent for close events #1498

Merged
merged 1 commit into from

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
Thomas A Caswell
Owner

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
Commits on Nov 14, 2012
  1. Martin Teichmann

    use QMainWindow.closeEvent for close events

    tecki authored
    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.
This page is out of date. Refresh to see the latest.
Showing with 7 additions and 15 deletions.
  1. +7 −15 lib/matplotlib/backends/backend_qt4.py
22 lib/matplotlib/backends/backend_qt4.py
View
@@ -208,18 +208,6 @@ def __init__( self, figure ):
w,h = self.get_width_height()
self.resize( w, h )
- # JDH: Note the commented out code below does not work as
- # expected, because according to Pierre Raybaut, The reason is
- # that PyQt fails (silently) to call a method of this object
- # just before detroying it. Using a lambda function will work,
- # exactly the same as using a function (which is not bound to
- # the object to be destroyed).
- #
- #QtCore.QObject.connect(self, QtCore.SIGNAL('destroyed()'),
- # self.close_event)
- QtCore.QObject.connect(self, QtCore.SIGNAL('destroyed()'),
- lambda: self.close_event())
-
def __timerEvent(self, event):
# hide until we can test and fix
self.mpl_idle_event(event)
@@ -377,6 +365,10 @@ 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()'))
+
class FigureManagerQT( FigureManagerBase ):
"""
Public attributes
@@ -391,8 +383,9 @@ def __init__( self, canvas, num ):
if DEBUG: print('FigureManagerQT.%s' % fn_name())
FigureManagerBase.__init__( self, canvas, num )
self.canvas = canvas
- self.window = QtGui.QMainWindow()
- self.window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
+ self.window = MainWindow()
+ self.window.connect(self.window, QtCore.SIGNAL('closing()'),
+ canvas.close_event)
self.window.setWindowTitle("Figure %d" % num)
image = os.path.join( matplotlib.rcParams['datapath'],'images','matplotlib.png' )
@@ -619,7 +612,6 @@ def draw_rubberband( self, event, x0, y0, x1, y1 ):
def configure_subplots(self):
self.adj_window = QtGui.QMainWindow()
win = self.adj_window
- win.setAttribute(QtCore.Qt.WA_DeleteOnClose)
win.setWindowTitle("Subplot Configuration Tool")
image = os.path.join( matplotlib.rcParams['datapath'],'images','matplotlib.png' )
Something went wrong with that request. Please try again.