Использование PyQt с Gevent
Кто-нибудь использовал PyQt с gevent? Как связать цикл PyQt с Gevent?
http://www.gevent.org/ - сетевая библиотека Python на основе сопрограмм, которая использует greenlet для обеспечения высокоуровневого синхронного API поверх цикла событий libevent.
5 ответов
Вот как вы могли бы изменить pyqt для примера session1, чтобы сотрудничать: https://github.com/traviscline/pyqt-by-example/commit/b5d6c61daaa4d2321efe89679b1687e85892460a
Вы можете использовать "таймер" Qt IDLE, чтобы gevent
для обработки своих микропотоков, в то время как события Qt не обрабатывались в течение короткого периода времени, например, 10 миллисекунд. Это все еще не идеально, так как это не дает "самой гладкой" возможной интеграции. Это потому, что мы не используем один цикл обработки событий как для Qt, так и для gevent, а просто "чередуем" их во времени.
Правильным решением было бы позволить libevent как-то прослушивать новые события Qt, но я пока не смог понять, как это сделать на практике. Возможно, поможет использование Qt для отправки чего-либо в gevent через сокет, когда в очередь событий поступит событие GUI. Кто-нибудь решил это?
Рабочий пример:
""" 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()
Я попробовал следующий подход: иметь "PyQt backend" для gevent, т.е. реализация цикла gevent с использованием конструкций PyQt, таких как QSocketNotifier, QTimer и т. д. вместо цикла libev. Наконец, я обнаружил, что это намного проще, чем делать обратное, и производительность очень хорошая (цикл Qt основан на glib под Linux, он не так уж и плох).
Вот ссылка на проект на github для интересующихся: https://github.com/mguijarr/qtgevent
Это только начало, но оно хорошо работает для тех тестов, которые я проводил. Я был бы счастлив, если бы люди с большим опытом работы с Gevent и PyQt могли внести свой вклад.
Я выпустил проект с именем eventlet-pyqt. Я надеюсь, что это может быть полезно для тех, кто хочет использовать greenlet в своем приложении PyQt. Я также попробовал gevent, но мне было трудно написать плагин для libevent из-за моего плохого опыта в языке Си. Основная проблема с использованием QApplicaton::processEvents()
или нулевой интервал QTimer
программа запускается в бесконечном цикле, вызывает 100% использование ядра процессора. Чтобы избежать этого, я написал новый хаб для замены select()
функция с PyQt's QSocketNotifier
, Надеюсь, что это сообщение может помочь кому-то.
Вы должны избегать использования app.exec_(), это функция цикла, которая использует эту функцию для обработки событий:
http://doc.qt.nokia.com/stable/qcoreapplication.html
так что вы можете напрямую вызывать processEvents.