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
Event loop issues with IPython 0.12 and PyQt4 (QDialog.exec_ and more) #2080
Comments
Did you try instanciating QApplication before ? Note that you might need to do something like :
as PyQt/PySide do not clear everything at application exit. |
Yes I did, but as in this example I was running the example from ipython --gui=qt, the QApplication is already instantiated by IPython, and the event loop is already running. |
@jdmarch, you guys have the most in-house Qt expertise of the team; any thoughts on this one? |
Two precisions: |
Bummer, I suspect this is going to be a tough one to fix. These event loop bugs are typically very hard to track down. Please post any additional insights you may have, as I'm not optimistic about fast progress on this one. |
Sorry, we have not had time to look at this. |
No worries, I don't expect it will be easy :) |
Thanks for the test script.
|
OK, so (without looking very thoroughly), I guess it's the "app.quit" called from a timer, which probably quits all event loops, including local event loops of modal dialogs. Then restarting with "app.exec_()" does not restore local loops where they were, but only the main loop. |
I would expect that the Python-implemented input hook should mostly mimic the C-level input hook, except with our tweaks regarding waiting keyboard input and reduced timeouts. However reading through For example, compare static int qtcore_input_hook()
{
QCoreApplication *app = QCoreApplication::instance();
if (app && app->thread() == QThread::currentThread())
{
#if defined(Q_OS_WIN32)
QTimer timer;
QObject::connect(&timer, SIGNAL(timeout()), app, SLOT(quit()));
do
{
timer.start(100);
QCoreApplication::exec();
timer.stop();
}
while (!_kbhit());
QObject::disconnect(&timer, SIGNAL(timeout()), app, SLOT(quit()));
#else
QSocketNotifier notifier(0, QSocketNotifier::Read, 0);
QObject::connect(¬ifier, SIGNAL(activated(int)), app, SLOT(quit()));
QCoreApplication::exec();
QObject::disconnect(¬ifier, SIGNAL(activated(int)), app, SLOT(quit()));
#endif
}
return 0;
} with https://github.com/ipython/ipython/blob/rel-0.13/IPython/lib/inputhookqt4.py#L88 |
I've mocked up what I think may be a fix in bfroehle/ipython@inputhook_qt4. In Win32 I've kept the original code, but added This seems to address the issue in my Ubuntu 12.04 installation, but I've only done limited testing. You can read some background on the whole readline inputhook problem at http://www.riverbankcomputing.com/pipermail/pyqt/2007-July/016512.html |
I have tried the same in the meantime, and it also fixes the problem for me (Ubuntu 12.04 also). |
Yes, this sounds like a reasonable interpretation since typing in the |
I'm no expert on Qt4 here, but do we need to launch an actual QCoreApplication? Or can we just create a QEventLoop and run that? |
We're only after the event loop control, so if it's safe to do that only with an event loop, I don't think we need an app. What I don't know is if not creating an app will make things better or worse with user code that does try to create an app. In any case, if we make changes to this logic, the examples in |
For a little bit of background on why one might want to just use 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
However I'm not Qt expert, so this approach might not be valid at all... shrug |
…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.
Using IPython 0.12 (or 0.12.1) and PyQt4 (or even Qt widgets compiled in C++ run from IPython), I get some strange behaviours of the event loop. Typically, QFileDialog.exec_() returns immediately when exec_ is called (and returns 0).
I also get the main event loop quitting (and finishing with a segfault) in some Qt event processing, but I could not reproduce this problem in a simple program.
For the dialog problem, I have found someone else having the same problem:
http://stackoverflow.com/questions/10683594/ipython-0-12-pyqt4-qdialog-qmenu-exec-does-not-block-returns-immedi
Strangely, the problem does not show up if the QFileDialog is built directly from the Ipython shell, but only when it is built from within a Qt slot (for instance a menu callback).
I have this problem on Linux (Ubuntu 10.04, using system python 2.6.5, system Qt 4.6.2, system PyQt 4.7.2, and a custom IPython install).
I do not get the same problem using IPython 0.11.
Here is a small program example displaying the problem. It can be run from a ipython shell (ipython --gui=qt):
The text was updated successfully, but these errors were encountered: