2

我已经改编了如何从 Python 异步运行外部命令?通过添加 QTextEdit 小部件的更新,但在将文本附加到小部件时进程被阻塞。

async def _read_stream(widget:RunCampaignWidget, stream, cb):
    while True:
        line = await stream.readline()
        if line:
            #cb(line)
            print("_read_stream :: line = " + str(line))
            widget.__run_result_console.append(line)
            widget.__run_result_console.show()
            global log_lines
            log_lines += str(line)
            #print("_read_stream :: log_lines = " + log_lines)
        else:
            break

事实上,我无法在运行异步进程时更新 GUI。任何想法?

4

1 回答 1

3

PySide6(和 PyQt)默认不支持 asyncio,但是有像 qasync 这样的库允许集成事件循环。以下示例是如何使用asyncio.create_subprocess_exec()PySide6的简单示例

import sys
import asyncio

from PySide6.QtCore import QObject, Signal
from PySide6.QtWidgets import QApplication, QTextEdit, QPushButton, QVBoxLayout, QWidget

import qasync

if sys.platform == "win32":
    asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())


class ManagerProcess(QObject):
    data_stdout_changed = Signal(bytes)
    data_stderr_changed = Signal(bytes)
    error_ocurred = Signal(Exception)

    started = Signal()
    finished = Signal()

    def start(self, cmd):
        asyncio.ensure_future(self._start(cmd))

    async def _start(self, cmd):
        try:
            process = await asyncio.create_subprocess_exec(
                *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
            )
            self.started.emit()
            await asyncio.wait(
                [
                    asyncio.create_task(
                        self._read_stream(process.stdout, self.data_stdout_changed)
                    ),
                    asyncio.create_task(
                        self._read_stream(process.stderr, self.data_stderr_changed)
                    ),
                ]
            )
            rc = await process.wait()
            self.finished.emit()
        except OSError as e:
            self.error_ocurred.emit(e)

    async def _read_stream(self, stream, signal):
        while True:
            line = await stream.readline()
            if line:
                signal.emit(line)
            else:
                break


class Widget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.text_edit = QTextEdit(readOnly=True)
        button = QPushButton("Start")

        lay = QVBoxLayout(self)
        lay.addWidget(button)
        lay.addWidget(self.text_edit)

        self.manager = ManagerProcess()

        button.clicked.connect(self.handle_clicked)
        self.manager.data_stdout_changed.connect(self.handle_stdout)
        self.manager.data_stderr_changed.connect(self.handle_stderr)

    def handle_clicked(self):
        cmd = ["ping", "8.8.8.8"]
        self.manager.start(cmd)

    def handle_stdout(self, data):
        self.text_edit.append(f"[STDOUT]: {data}")

    def handle_stderr(self, data):
        self.text_edit.append(f"[STDERR]: {data}")


def main():
    app = QApplication(sys.argv)
    loop = qasync.QEventLoop(app)
    asyncio.set_event_loop(loop)

    w = Widget()
    w.show()

    with loop:
        loop.run_forever()


if __name__ == "__main__":
    main()
于 2021-07-21T17:23:18.060 回答