Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qt closeevent fixes for v1.2.x #1705

Merged
merged 10 commits into from Jan 28, 2013
1 change: 1 addition & 0 deletions lib/matplotlib/__init__.py
Expand Up @@ -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',
]


Expand Down
58 changes: 34 additions & 24 deletions lib/matplotlib/backends/backend_qt4.py
Expand Up @@ -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

Expand All @@ -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)
Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)
Expand Down Expand Up @@ -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):
Expand Down
32 changes: 32 additions & 0 deletions 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)