##// END OF EJS Templates
Backport PR #2294: inputhook_qt4: Use QEventLoop instead of starting up the QCoreApplication...
Backport PR #2294: inputhook_qt4: Use QEventLoop instead of starting up the QCoreApplication I referenced this branch in #2080 and was letting it sit for a little while, but I have decided to make it a full pull request to get some additional visibility. Essentially our Qt event loop mechanism repeatedly starts and quits a `QCoreApplication` object. Unfortunately the `QCoreApplication::quit` slot has a lot of unintended side effects (like emitting an `aboutToQuit` signal which closes all open file dialogs). For our input hook, we _might_ be able to get by with just using a `QEventLoop` whose quit slot is much simpler and less destructive. For a little bit of background on why one might want to just use `QEventLoop::exec`, let's examine what `QCoreApplication::exec` does: ```c++ int QCoreApplication::exec() { if (!QCoreApplicationPrivate::checkInstance("exec")) return -1; // ... [some assertions] threadData->quitNow = false; QEventLoop eventLoop; self->d_func()->in_exec = true; self->d_func()->aboutToQuitEmitted = false; int returnCode = eventLoop.exec(); threadData->quitNow = false; if (self) { self->d_func()->in_exec = false; if (!self->d_func()->aboutToQuitEmitted) emit self->aboutToQuit(); self->d_func()->aboutToQuitEmitted = true; sendPostedEvents(0, QEvent::DeferredDelete); } return returnCode; } ``` As far as I can tell, it's a small wrapper around `QEventLoop::exec` which also: * Sets some variables regarding the current status * Emits an `aboutToQuit` signal right before the function returns (which is the root cause of @denisri's problem in #2080). Historically, our Qt event loop is a python implementation of the (win 32) input hook supplied with the PyQt4 source (see qtcore_input_hook` in `python-qt4/sip/QtCore/qcoreapplication.sip`), which more or less dates to a [mailing list post](http://www.riverbankcomputing.com/pipermail/pyqt/2007-July/016512.html) from July 2007.

File last commit:

r5807:3ba61c59
r9834:71196839
Show More
asyncfrontendbase.py
82 lines | 2.7 KiB | text/x-python | PythonLexer
"""
Base front end class for all async frontends.
"""
__docformat__ = "restructuredtext en"
# Tell nose to skip this module
__test__ = {}
#-------------------------------------------------------------------------------
# Copyright (C) 2008-2011 The IPython Development Team
#
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING, distributed as part of this software.
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# Imports
#-------------------------------------------------------------------------------
# Third-party
from twisted.python.failure import Failure
from zope.interface import implements, classProvides
# From IPython
from IPython.frontend.frontendbase import (FrontEndBase, IFrontEnd,
IFrontEndFactory)
from IPython.kernel.core.history import FrontEndHistory
from IPython.kernel.engineservice import IEngineCore
import uuid
#-----------------------------------------------------------------------------
# Classes and functions
#-----------------------------------------------------------------------------
class AsyncFrontEndBase(FrontEndBase):
"""
Overrides FrontEndBase to wrap execute in a deferred result.
All callbacks are made as callbacks on the deferred result.
"""
implements(IFrontEnd)
classProvides(IFrontEndFactory)
def __init__(self, engine=None, history=None):
assert(engine==None or IEngineCore.providedBy(engine))
self.engine = IEngineCore(engine)
if history is None:
self.history = FrontEndHistory(input_cache=[''])
else:
self.history = history
def execute(self, block, blockID=None):
"""Execute the block and return the deferred result.
Parameters:
block : {str, AST}
blockID : any
Caller may provide an ID to identify this block.
result['blockID'] := blockID
Result:
Deferred result of self.interpreter.execute
"""
if(not self.is_complete(block)):
return Failure(Exception("Block is not compilable"))
if(blockID == None):
blockID = uuid.uuid4()
d = self.engine.execute(block)
d.addCallback(self._add_history, block=block)
d.addCallbacks(self._add_block_id_for_result,
errback=self._add_block_id_for_failure,
callbackArgs=(blockID,),
errbackArgs=(blockID,))
d.addBoth(self.update_cell_prompt, blockID=blockID)
d.addCallbacks(self.render_result,
errback=self.render_error)
return d