Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
Eric L. Frederich
@ericfrederich
I'm wondering how it would be possible using something like Qt's networking module within a coroutine to asynchronously wait for a reply to be finished. Would that be possible?
Eric L. Frederich
@ericfrederich
Okay, I saw some example from a closed issue on GitHub about the modal dialogs and think I figured it out. The only issue I have with this now is that I seem to have two methods to have an async slot. The first one is synchronous and calls asyncio.ensure_future, the other is async. Is there any way to do this without 2 methods. I'm new to asyncio.
Mark Harviston
@harvimt
yeah, that's more or less what you need to do
in the past I've created an asyncify decorator
so you can do
def asyncify(fn):
    @functools.wraps(fn)
    def inner_fn(*args, **kwargs):
          return asyncio.ensure_future(fn(*args, **kwargs))
    return inner_fn

class ...:
    @asyncify
    async def on_click(self, ...):
        pass # whatever
that avoids the on_click + _on_click overhead
Mark Harviston
@harvimt
async def wait_for_signal(signal):
     fut = asyncio.Future()
     signal.connect(lambda *args: fut.set_result(args))
     return fut
You could also use aiohttp though there have been some integration problems with that. (if you just need HTTP)
Eric L. Frederich
@ericfrederich
Thanks... that asyncify looks good. I might take it and modify it to also act like the pyqtSlot decorator and call it asyncSlot.
Mark Harviston
@harvimt
yeah that's what I was thinking of calling it if I made a quamash.utils module
(the asyncify name comes from when asyncio.ensure_future was called asyncio.async)
Eric L. Frederich
@ericfrederich

I noticed that self.sender()doesn't work within these async slots. I thought about setting an attribute async_sender like this...

def asyncify(fn):
    @functools.wraps(fn)
    def inner_fn(self, *args, **kwargs):
        self.async_sender = self.sender()
        return asyncio.ensure_future(fn(self, *args, **kwargs))
    return inner_fn

... but that would only be valid until the the slot awaited something.
Maybe I'd have to explicitly pass the sender as an argument to fn.

Eric L. Frederich
@ericfrederich
If you do write a utils module consider this...
def async_slot(*slot_types, with_sender=False):
    def asyncify(fn):
        @pyqtSlot(*slot_types)
        @functools.wraps(fn)
        def inner_fn(self, *args, **kwargs):
            if with_sender:
                return asyncio.ensure_future(fn(self, self.sender(), *args, **kwargs))
            else:
                return asyncio.ensure_future(fn(self, *args, **kwargs))
        return inner_fn
    return asyncify
Eric L. Frederich
@ericfrederich
@harvimt , that "wait_for_signal" you defined above. Did you mean to have that be an "async def" and not a regular def? How would you use it?
Mark Harviston
@harvimt
it is an async def, and its used like your async_reply function here:
except, you don't pass in the signal's name, you pass in the signal object.
oh, right, it does need to be a def
(I suppose you could make it async def and then do return await future, but that probably doesn't make sense)
Eric L. Frederich
@ericfrederich
Any recipes or best practices out there for creating an async API from a synchronous one? I'm guessing some queues or threads would be involved
Mark Harviston
@harvimt
Yeah, the only thing I've seen in that regard is aiofiles and it just wraps various calls in loop.run_in_executor
Eric L. Frederich
@ericfrederich
This message was deleted
Wondering what is going on here and what I'm doing wrong. Any help is appreciated.
Here is a complete example followed by the error I get.
#!/usr/bin/env python3
import sys

from PyQt5.QtWidgets import QApplication, QWizard, QVBoxLayout, QProgressBar, QWizardPage, QDialog
import asyncio
from quamash import QEventLoop


class MyWizardPage(QWizardPage):
    def __init__(self, parent=None):
        super().__init__(parent)
        layout = QVBoxLayout()
        self.progress = QProgressBar()
        layout.addWidget(self.progress)
        self.setLayout(layout)
        self.complete = False

    def initializePage(self):
        # kick off a coroutine that will eventually make this page complete
        asyncio.ensure_future(self.do_stuff())

    async def do_stuff(self):
        self.progress.setMinimum(0)
        self.progress.setMaximum(7)
        for i in range(8):
            self.progress.setValue(i)
            await asyncio.sleep(.2)
        self.complete = True
        self.completeChanged.emit()

    def isComplete(self):
        return self.complete

async def amain():
    """
    asynchronous main
    """
    wiz = QWizard()
    wiz.addPage(MyWizardPage())
    if wiz.exec() == QDialog.Accepted:
        print('okay')
    else:
        print('bailing')

def main():
    """
    synchronous main
    """
    app = QApplication(sys.argv)
    loop = QEventLoop(app)
    asyncio.set_event_loop(loop)
    with loop:
        loop.run_until_complete(amain())

if __name__ == '__main__':
    main()
okay
Exception in callback Task._step()
handle: <Handle Task._step()>
Traceback (most recent call last):
  File "/opt/Python-3/lib/python3.5/asyncio/events.py", line 125, in _run
    self._callback(*self._args)
  File "/opt/Python-3/lib/python3.5/asyncio/tasks.py", line 285, in _step
    self.__class__._current_tasks.pop(self._loop)
KeyError: <QEventLoop running=True closed=False debug=False>
Nikita Melentev
@pohmelie
@ericfrederich, it looks like you don't need exec function.
def main():
    """
    synchronous main
    """
    app = QApplication(sys.argv)
    loop = QEventLoop(app)
    asyncio.set_event_loop(loop)

    wiz = QWizard()
    wiz.addPage(MyWizardPage())
    wiz.show()
    with loop:

        # loop.run_until_complete(amain())
        loop.run_forever()
also, can't find documentation for exec for qt QWizard
Eric L. Frederich
@ericfrederich
@pohmelie , I want the exec so that I can branch off of the result and only do stuff if it was accepted (not canceled). The exec function comes from QDialog which is inherited by QWizard
Mark Harviston
@harvimt
run_forever() pretty much just calls exec under the hood.
for dialogs you need to use signals/slots
There's a good example somewhere that I will dig up later.
short answer is nested execs aren't really supported at the moment, but could be with a little effort.
Eric L. Frederich
@ericfrederich

Okay... so to clarify, I came about this error because I called exec() on a dialog while the event loop was running. That causes nested execs which aren't currently supported? I have to use something like this...

# from https://github.com/harvimt/quamash/issues/41#issuecomment-137995457
def function modalDialog(dialog):
    future = asyncio.Future()
    dialog.finished.connect(lambda r: future.set_result(r))
    dialog.open()
    return future

... and instead of calling exec I would result = await modalDialog(wiz)

James Stidard
@jamesstidard
If I were to start a new pyQT application from scratch is this still a good way of doing it if I want asyncio support? I noticed the builds on the github badges are failing and the commits are from a year ago
Nikita Melentev
@pohmelie
It is good project, many fixes are done and I see no reasons to not use it :+1:
James Stidard
@jamesstidard
Okie doke, I'll play around with it a bit. Thanks for the response
Mark Harviston
@harvimt
the build failures are mostly due to hitting a moving target with regards to deps
the CI builds could definitely use some love.
James Stidard
@jamesstidard
@harvimt Ah ok, that's reassuring, thanks.
Russell-Jones
@Russell-Jones
Hello all. Is there a way to get a list of running tasks from quamash? I'd like to close my app when a button is pushed, but not until all tasks have completed.
Russell-Jones
@Russell-Jones
asyncio.Task.all_tasks() seems to do the job.
Alexandr Zarubkin
@me21
Hello everyone, I'm the author of PR #99 which adds PySide2 support. I've received one comment for the PR recently, I think I've answered it, but I would like to know if there's something I could do to get this PR accepted.
Nikita Melentev
@pohmelie
@me21 Wow, great! I feel that PySide2 will became new standard for python+qt, since previous leader: PyQt5 do not allow closed source without payment. I hope this will be merged ASAP! :muscle:
Alexandr Zarubkin
@me21
That depends on repo maintainers ;)
There are three more PRs which do the same, but they all have merge conflicts. I just combined them into one and solved the conflicts.
TurBoss
@TurBoss
Hello
I'm trying to build my app using quamash
Nikita Melentev
@pohmelie
As mentioned above @harvimt disappear somehow, I suggest you to use https://github.com/gmarull/asyncqt
With pyside2 of course