【问题标题】:Update GUI while running asynchronous process运行异步进程时更新 GUI
【发布时间】:2021-09-29 00:54:06
【问题描述】:

我已通过添加 QTextEdit 小部件的更新来调整 How can I run an external command asynchronously from Python? 中描述的“async def _read_stream (stream, cb):”代码,但在将文本附加到小部件时进程被阻塞。

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。有什么想法吗?

【问题讨论】:

    标签: python pyside6


    【解决方案1】:

    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()
    

    【讨论】:

    • 感谢 eyllanesc 的回复,您的代码在此修复后工作:“self.error_ocurred.emit(e)”,“e”参数丢失。
    • 但是 QTextEdit 不是实时更新的,而是在进程完成之后。我的进程运行了很长时间(几分钟),每次读取一行时我都需要更新和刷新 QTextEdit,而不是在进程结束时,此时为时已晚。
    • @pinchoonet 1) 我已经纠正了错误,2) 我不知道你的意思,GUI 更新是即时的,现在验证我使用返回结果的“ping”命令每隔一段时间,我观察到它每时每刻都会更新。 3)关于你指出的另一篇有例外的帖子,显然你没有使用我的代码,所以我不会指出任何关于它的事情,因为我只对我的代码负责。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-20
    • 2011-03-21
    • 2013-08-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多