Skip to content Skip to sidebar Skip to footer

Pyqt5 And Asyncio: Yield From Never Finishes

I'm trying to create a new application based on PyQt5 and asyncio (with python 3.4, looking forward to eventually upgrade to 3.5 with async/await). My goal is to use asyncio so tha

Solution 1:

Ok, that's one plus of SO: Writing down a question makes you think again about everything. Somehow I just figured it out:

Looking again at the example from the quamash repo, I found that the event loop to use is obtained somewhat differently:

app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)  # NEW must set the eventloop

# ...

withloop:
    loop.run_until_complete(master())

The key seems to be the asyncio.set_event_loop(). It is also important to note that the QEventLoop mentioned there is the one from the quamash package, NOT from Qt5. So my example now looks like this:

import sys
import quamash
import asyncio
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

@asyncio.coroutinedefop():
  print('op()')


@asyncio.coroutinedefslow_operation():
  print('clicked')
  yieldfrom op()
  print('op done')
  yieldfrom asyncio.sleep(0.1)
  print('timeout expired')
  yieldfrom asyncio.sleep(2)
  print('second timeout expired')

  loop.stop()

defcoroCallHelper(coro):
  asyncio.ensure_future(coro(), loop=loop)

classExample(QWidget):

  def__init__(self):
    super().__init__()
    self.initUI()

  definitUI(self):
    defbtnCallback(obj):
      #~ loop.call_soon(coroCallHelper, slow_operation)
      asyncio.ensure_future(slow_operation(), loop=loop)
      print('btnCallback returns...')

    btn = QPushButton('Button', self)
    btn.resize(btn.sizeHint())
    btn.move(50, 50)
    btn.clicked.connect(btnCallback)

    self.setGeometry(300, 300, 300, 200)
    self.setWindowTitle('Async')    
    self.show()

app = QApplication(sys.argv)
loop = quamash.QEventLoop(app)
asyncio.set_event_loop(loop)  # NEW must set the event loopwith loop:
    w = Example()
    w.show()
    loop.run_forever()
print('Coroutine has ended')

And it 'just works' now:

btnCallback returns...
clicked
op()
op donetimeout expired
second timeout expired
Coroutine has ended

Maybe this is of some help for others. I'm happy with it at least ;) Comments on the general pattern are still welcome, of course!

Addendum: Please note that this works with recent Python versions up to Python 3.7.x if quamash is replaced by asyncqt. However, using the same code with Python 3.8 causes the @coroutine decorators to generate RuntimeWarnings and eventually fails with a RuntimeError: no running event loop in asyncio.sleep(). Maybe someone else knows what to change to get this working again. It might just be that asyncqt is not yet compatible with Python 3.8.

Regards, Philipp

Post a Comment for "Pyqt5 And Asyncio: Yield From Never Finishes"