PyQt now also uses asyncio and its thread contains the main loop

This commit is contained in:
Christoph Stahl 2024-10-10 15:39:53 +02:00
parent bf2e854cdd
commit 089de1ff93
4 changed files with 50 additions and 19 deletions

15
poetry.lock generated
View file

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]] [[package]]
name = "aiohappyeyeballs" name = "aiohappyeyeballs"
@ -1452,6 +1452,17 @@ files = [
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
] ]
[[package]]
name = "qasync"
version = "0.27.1"
description = "Python library for using asyncio in Qt-based applications"
optional = false
python-versions = ">=3.8,<4.0"
files = [
{file = "qasync-0.27.1-py3-none-any.whl", hash = "sha256:5d57335723bc7d9b328dadd8cb2ed7978640e4bf2da184889ce50ee3ad2602c7"},
{file = "qasync-0.27.1.tar.gz", hash = "sha256:8dc768fd1ee5de1044c7c305eccf2d39d24d87803ea71189d4024fb475f4985f"},
]
[[package]] [[package]]
name = "qrcode" name = "qrcode"
version = "7.4.2" version = "7.4.2"
@ -1961,4 +1972,4 @@ server = ["alt-profanity-check"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "a53a034e3cccdb40b71d708df2e82b2e5840639414a3bc727a31cb27b7a0d286" content-hash = "8d19f746a97720e770c47baed26571c2e53aa79a5e7a5a10f62450f519f211a8"

View file

@ -43,6 +43,7 @@ pyyaml = { version = "^6.0.1", optional = true }
alt-profanity-check = {version = "^1.4.1", optional = true} alt-profanity-check = {version = "^1.4.1", optional = true}
pyqt6 = {version="^6.7.1", optional = true} pyqt6 = {version="^6.7.1", optional = true}
mpv = "^1.0.7" mpv = "^1.0.7"
qasync = "^0.27.1"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
types-pyyaml = "^6.0.12.12" types-pyyaml = "^6.0.12.12"

View file

@ -452,6 +452,7 @@ class Client:
:type config: dict[str, Any] :type config: dict[str, Any]
:rtype: None :rtype: None
""" """
print("Starting client")
self.loop = asyncio.get_running_loop() self.loop = asyncio.get_running_loop()
@ -495,7 +496,9 @@ class Client:
def create_async_and_start_client( def create_async_and_start_client(
config: dict[str, Any], queue: Optional[Queue[LogRecord]] = None config: dict[str, Any],
queue: Optional[Queue[LogRecord]] = None,
client: Optional[Client] = None,
) -> None: ) -> None:
""" """
Create an asyncio event loop and start the client. Create an asyncio event loop and start the client.
@ -512,7 +515,8 @@ def create_async_and_start_client(
if queue is not None: if queue is not None:
logger.addHandler(QueueHandler(queue)) logger.addHandler(QueueHandler(queue))
client = Client(config) if client is None:
client = Client(config)
asyncio.run(client.start_client(config)) asyncio.run(client.start_client(config))

View file

@ -1,15 +1,18 @@
import asyncio
from io import BytesIO from io import BytesIO
import sys import sys
import logging import logging
from logging.handlers import QueueListener from logging.handlers import QueueListener
from multiprocessing import Process, Queue
# from multiprocessing import Process, Queue
from queue import Queue
from collections.abc import Callable from collections.abc import Callable
from datetime import datetime from datetime import datetime
import os import os
from functools import partial from functools import partial
import random import random
from typing import TYPE_CHECKING, Any, Optional from typing import TYPE_CHECKING, Any, Optional
import multiprocessing import threading
import secrets import secrets
import string import string
import signal import signal
@ -24,11 +27,11 @@ try:
except ImportError: except ImportError:
pass pass
from qasync import QEventLoop, QApplication
from PyQt6.QtCore import QTimer, Qt from PyQt6.QtCore import QTimer, Qt
from PyQt6.QtGui import QCloseEvent, QIcon, QPixmap from PyQt6.QtGui import QCloseEvent, QIcon, QPixmap
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
QApplication, # QApplication,
QCheckBox, QCheckBox,
QComboBox, QComboBox,
QDateTimeEdit, QDateTimeEdit,
@ -54,7 +57,7 @@ from qrcode.main import QRCode
import platformdirs import platformdirs
from . import resources # noqa from . import resources # noqa
from .client import create_async_and_start_client, default_config from .client import Client, create_async_and_start_client, default_config
from .sources import available_sources from .sources import available_sources
from .config import ( from .config import (
@ -626,8 +629,9 @@ class SyngGui(QMainWindow):
if os.name != "nt": if os.name != "nt":
self.setWindowIcon(QIcon(":/icons/syng.ico")) self.setWindowIcon(QIcon(":/icons/syng.ico"))
self.syng_server: Optional[Process] = None self.loop = asyncio.get_event_loop()
self.syng_client: Optional[Process] = None self.syng_server: Optional[threading.Thread] = None
self.syng_client: Optional[threading.Thread] = None
self.syng_client_logging_listener: Optional[QueueListener] = None self.syng_client_logging_listener: Optional[QueueListener] = None
self.configfile = os.path.join(platformdirs.user_config_dir("syng"), "config.yaml") self.configfile = os.path.join(platformdirs.user_config_dir("syng"), "config.yaml")
@ -752,9 +756,11 @@ class SyngGui(QMainWindow):
return return
if not self.syng_client.is_alive(): if not self.syng_client.is_alive():
print("Client is not running")
self.syng_client = None self.syng_client = None
self.set_client_button_start() self.set_client_button_start()
else: else:
print("Client is running")
self.set_client_button_stop() self.set_client_button_stop()
def set_client_button_stop(self) -> None: def set_client_button_stop(self) -> None:
@ -767,24 +773,28 @@ class SyngGui(QMainWindow):
if self.syng_client is None or not self.syng_client.is_alive(): if self.syng_client is None or not self.syng_client.is_alive():
self.save_config() self.save_config()
config = self.gather_config() config = self.gather_config()
queue: Queue[logging.LogRecord] = multiprocessing.Queue() queue: Queue[logging.LogRecord] = Queue()
self.syng_client_logging_listener = QueueListener( self.syng_client_logging_listener = QueueListener(
queue, LoggingLabelHandler(self.notification_label) queue, LoggingLabelHandler(self.notification_label)
) )
self.syng_client_logging_listener.start() self.syng_client_logging_listener.start()
self.syng_client = multiprocessing.Process( # self.syng_client = multiprocessing.Process(
target=create_async_and_start_client, args=[config, queue] # target=create_async_and_start_client, args=[config, queue]
) # )
self.syng_client.start() self.client = Client(config)
asyncio.run_coroutine_threadsafe(self.client.start_client(config), self.loop)
# self.syng_client = threading.Thread(
# target=create_async_and_start_client, args=[config, queue, self.client]
# )
# self.syng_client.start()
self.notification_label.setText("") self.notification_label.setText("")
self.timer.start(500) self.timer.start(500)
self.set_client_button_stop() self.set_client_button_stop()
else: else:
self.syng_client.terminate() self.client.quit_callback()
self.syng_client.join(1.0) self.syng_client.join(1.0)
self.syng_client.kill()
self.set_client_button_start() self.set_client_button_start()
# def start_syng_server(self) -> None: # def start_syng_server(self) -> None:
@ -853,6 +863,9 @@ def run_gui() -> None:
signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QApplication([]) app = QApplication([])
event_loop = QEventLoop(app)
asyncio.set_event_loop(event_loop)
if os.name == "nt": if os.name == "nt":
app.setWindowIcon(QIcon(os.path.join(base_dir, "syng.ico"))) app.setWindowIcon(QIcon(os.path.join(base_dir, "syng.ico")))
else: else:
@ -861,7 +874,9 @@ def run_gui() -> None:
app.setDesktopFileName("rocks.syng.Syng") app.setDesktopFileName("rocks.syng.Syng")
window = SyngGui() window = SyngGui()
window.show() window.show()
app.exec() # app.exec()
with event_loop:
event_loop.run_forever()
if __name__ == "__main__": if __name__ == "__main__":