Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using PyQt with gevent

Does anyone used PyQt with gevent? How to link PyQt loop to the gevent?

http://www.gevent.org/ - coroutine-based Python networking library that uses greenlet to provide a high-level synchronous API on top of libevent event loop.

like image 372
Andrey Nikishaev Avatar asked Feb 02 '23 23:02

Andrey Nikishaev


2 Answers

You can use a Qt IDLE "timer" to allow gevent for processing its microthreads while no Qt events handled for a short period of time, for example 10 milliseconds. It is still not perfect, since it does not give the "smoothest" possible integration. It is because we don't use a single event loop for both Qt and gevent, just "interleaving" them in time.

The correct solution would be to allow libevent to listen on new Qt events somehow, but I haven't been able to figure out how to do that in practice yet. Maybe having Qt to send something to gevent via a socket when a GUI event arrives into the event queue would help. Has anybody solved that?

Working example:

""" Qt - gevent event loop integration using a Qt IDLE timer
"""

import sys, itertools

import PySide
from PySide import QtCore, QtGui

import gevent

# Limit the IDLE handler's frequency while still allow for gevent
# to trigger a microthread anytime
IDLE_PERIOD = 0.01

class MainWindow(QtGui.QMainWindow):

    def __init__(self, application):

        QtGui.QMainWindow.__init__(self)

        self.application = application

        self.counter = itertools.count()

        self.resize(400, 100)
        self.setWindowTitle(u'Counting: -')

        self.button = QtGui.QPushButton(self)
        self.button.setText(u'Reset')
        self.button.clicked.connect(self.reset_counter)

        self.show()

    def counter_loop(self):

        while self.isVisible():
            self.setWindowTitle(u'Counting: %d' % self.counter.next())
            gevent.sleep(0.1)

    def reset_counter(self):

        self.counter = itertools.count()

    def run_application(self):

        # IDLE timer: on_idle is called whenever no Qt events left for processing
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.on_idle)
        self.timer.start(0)

        # Start counter
        gevent.spawn(self.counter_loop)

        # Start you application normally, but ensure that you stop the timer
        try:
            self.application.exec_()
        finally:
            self.timer.stop()

    def on_idle(self):

        # Cooperative yield, allow gevent to monitor file handles via libevent
        gevent.sleep(IDLE_PERIOD)

def main():

    application = QtGui.QApplication(sys.argv)
    main_window = MainWindow(application)
    main_window.run_application()

if __name__ == '__main__':
    main()
like image 74
fviktor Avatar answered Feb 05 '23 15:02

fviktor


I tried the following approach: to have a "PyQt backend" for gevent, ie. an implementation of the gevent loop making use of PyQt constructs like QSocketNotifier, QTimer, etc. instead of the libev loop. Finally I found it much easier than doing the opposite, and performance is very good (Qt's loop is based on the glib under Linux, it's not so bad).

Here is the link to the project on github for those interested: https://github.com/mguijarr/qtgevent

This is just a start, but it works well for the tests I did. I would be happy if people with more experience with gevent and PyQt could contribute.

like image 29
mguijarr Avatar answered Feb 05 '23 14:02

mguijarr