CabbageDevelopment / qasync

Python library for using asyncio in Qt-based applications.
BSD 2-Clause "Simplified" License
307 stars 43 forks source link

creating task from the MainUi (PyQT5 class) init works fine in v0.24.0 but not with v0.24.2 and above #116

Open stephanerey opened 3 months ago

stephanerey commented 3 months ago

The code below where I'm creating an async task "counter" from the MainUI class init function was working fine with version 0.24.0 but is not working anymore from 0.24.2. The task is never created while the loop is running. Creating the same task in the same loop from the main() function works fine with 0.24.0 up to lastest 0.27.1 (commented lines in the main() ) Is there something wrong in my task creation code ?

import functools
import sys, traceback
import asyncio, qasync
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMainWindow, QLabel, QVBoxLayout, QWidget

class MainUi(QMainWindow):
    def __init__(self):
        super().__init__()
        self.count = 0

        # Pyqt GUI init
        layout = QVBoxLayout()
        self.label = QLabel()
        layout.addWidget(self.label)
        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

        # create thread
        # Only work with QASYNC 0.24.0 but not with newer versions
        self.loop = asyncio.get_event_loop()
        self.loop.create_task(self.counter())

    async def counter(self):
        while True:
            self.count += 1
            self.label.setText(str(self.count))
            await asyncio.sleep(1)

async def main():
    app = QtWidgets.QApplication(sys.argv)
    app.setQuitOnLastWindowClosed(True)

    loop = qasync.QEventLoop(app)
    asyncio.set_event_loop(loop)

    ui = MainUi()
    ui.show()

    # The line below works with both QASYNC 0.24.0 and above
    # loop.create_task(ui.counter())

    with loop:
        # Run the event loop until the UI is closed
        await asyncio.gather(loop.create_task(loop.run_forever()),
                             loop.run_in_executor(None, functools.partial(ui.show)))
    # Cleanup and close the application
    loop.close()
    app.exec_()

if __name__ == '__main__':
    try:
        asyncio.run(main())
    except Exception as e:
        print("An error occurred:", e)
        traceback.print_exc()
        sys.exit(1)
kormax commented 3 months ago

When you're calling asyncio.run, it creates and runs default asyncio loop on the main thread.

You're later attempting to replace already running loop with a newly instantiated QAsync loop, inside of the initial loop, without stopping and closing it first.

Weird that this code worked in the first place, because it shouldn't have.

Normally, you'd need to instantiate qasync loop outside of asynchronous context, set it as the main loop, and then start the asynchronous task (with loop running via run_forever or run_until_complete).

stephanerey commented 3 months ago

thanks for your reply. I'm probably confusing and I'm sorry. I thought the asyncio.get_event_loop() was used in order to get the current running loop so I do not understand why I'm instantiating a new loop in my code. Next if one can't instantiate a new thread other than in the main thread how to you do to start a new thread that is triggered by an event from anywhere in the code other than the main thread ? Does it mean all the threads should be created and doing nothing until something unlock the code inside ?