Hi everybody, I have been happily using PyQt4 for scientific visualization. For this purpose, the recent ability of PyQt to handle Python commands interactively, as described in
http://www.riverbankcomputing.com/Docs/PyQt4/pyqt4ref.html#using-pyqt-from-th e-python-shell is absolutely marvelous. Unfortunately, interactivity is broken if Python does not have the readline module (for example on Windows), causing PyQT windows to freeze. Below I suggest a modification to PyQt that fixes this bug on Windows, as well as on Unix-like systems without readline. The suggested code also improves the performance on Unix-like systems that do have the readline module. From reading the mailing list, I believe it is the appropriate place to submit PyQt patches; please let me know if it is not. PyQt4 handles GUI messages while Python is waiting for user commands by letting PyOS_InputHook point to the qtcore_input_hook function in QtCore/sipQtCorecmodule.cpp. The qtcore_input_hook function handles all existing GUI events by calling QCoreApplication::processEvents(), and returns immediately after that: static int qtcore_input_hook() { QCoreApplication::processEvents(); return 0; } On Python *with* the readline module, PyOS_InputHook and therefore QCoreApplication::processEvents() is called periodically (ten times per second) as long as no new input is available on stdin. See Modules/readline.c in the Python distribution for details; the exact code depends on which version of the readline library is used. So in the current implementation, interactivity works as follows: Handle events with QCoreApplication::processEvents() Wait 0.1 seconds Handle events with QCoreApplication::processEvents() Wait 0.1 seconds ... breaking out of this loop once the next Python command is available on stdin. On Python *without* the readline module, for example on Windows, PyOS_InputHook is called only once, just before reading input from stdin. So now we have: Handle events with QCoreApplication::processEvents() Wait for input on stdin While waiting for input on stdin, no GUI events are handled. The PyQT windows then freeze until the user types in the next Python command. If you want to see this behavior on a Python with the readline module, temporarily rename readline.so in /usr/lib/python2.5/lib-dynload/ (exact file name and path may be different on your system) and restart Python. You will have a Python without readline. If you then create a QPushButton in a PyQT window, it will not react to mouse clicks (until you enter the next Python command). The cleanest solution to this bug would be to use the same approach as Tkinter, which is to stay in the GUI event loop until the next Python command is available on stdin. Ideally, one would create a QFile for stdin, and use the readyRead signal. Unfortunately, currently in Qt, QFile does not emit the readyRead signal (see http://doc.trolltech.com/4.3/qfile.html). On Unix-like systems, as a workaround we can use a QSocketNotifier on file descriptor 0 (stdin); on Unix-like systems, QSocketNotifier doesn't care if the file descriptor is actually a socket. The following replacement for qtcore_input_hook then fixes the bug: static int qtcore_input_hook() { QCoreApplication* app = QCoreApplication::instance(); if (!app) return 0; 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())); return 0; } So, on the first call to PyOS_InputHook, we enter QCoreApplication::exec() and stay there until we get the SIGNAL(activated) for stdin. At that point, we quit QCoreApplication::exec() and return to Python to handle the next Python command. This guarantees that Qt events continue to be handled. Note that this replacement for qtcore_input_hook also gives better performance on Python with the readline module, as it avoids the ten-times-per-second loop above. On Windows, unfortunately, this won't work, since on Windows sockets are inherently different from the console's stdin. So on Windows, we have to do use a loop, for example as follows: static int qtcore_input_hook() { QCoreApplication::processEvents(); QCoreApplication* app = QCoreApplication::instance(); if (!app) return 0; while (1) { QTimer timer; QObject::connect(&timer, SIGNAL(timeout()), app, SLOT(quit())); timer.start(100); QCoreApplication::exec(); timer.stop(); QObject::disconnect(&timer, SIGNAL(timeout()), app, SLOT(quit())); if (_kbhit()) return 0; } } The _kbhit function is a Windows-specific function that returns TRUE if input is available for reading from stdin. The _kbhit function is in conio.h, which should be #included. This code essentially does the ten-times-per-second loop that is currently being used with readline. If in some future version of Qt we can get a SIGNAL on stdin using QFile, then we can use that instead of this loop to get better performance. Unfortunately, I am not very familiar with sip, and I don't feel confident that I can write a patch against sip/QtCore/qcoreapplication.sip without making errors due to my inexperience with sip. I hope that the suggested code as replacement for qtcore_input_hook() will nevertheless be considered as a solution to get interactivity to work appropriately on Windows and readline-less Unices. Best wishes, and thank you for PyQT, --Michiel. Michiel de Hoon Center for Computational Biology and Bioinformatics Columbia University 1150 St Nicholas Avenue New York, NY 10032 _______________________________________________ PyQt mailing list PyQt@riverbankcomputing.com http://www.riverbankcomputing.com/mailman/listinfo/pyqt