从 Linux 终端执行包含子进程的 PyQt5 GUI 会导致 GUI 出现黑屏并冻结它 [英] Executing a PyQt5 GUI which includes subprocesses from the Linux terminal causes a black screen in the GUI and freezes it

查看:43
本文介绍了从 Linux 终端执行包含子进程的 PyQt5 GUI 会导致 GUI 出现黑屏并冻结它的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我想向您展示目前有效的方法.下面是一个简单的 GUI,其原理与导致问题的原理相同.它有一个按钮,当您单击它时,计数器会增加.

First, I would like to show you what works so far. Below is a simple GUI built with the same principles as the one that causes problems. It has a button and when you click it a counter increases.

#!/usr/bin/python3.5

import sys
from PyQt5 import QtWidgets

class GUI(QtWidgets.QWidget):

    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        self.initGUI()
        self.behaviours()

        self.counter = 0

    def initGUI(self):
        self.button = QtWidgets.QPushButton("Button")
        self.label = QtWidgets.QLabel()

        self.box = QtWidgets.QVBoxLayout()
        self.box.addWidget(self.button)
        self.box.addWidget(self.label)

        self.setLayout(self.box)
        self.show()

    def behaviours(self):
        self.button.clicked.connect(self.add)

    def add(self):
        self.counter = self.counter + 1
        self.label.setText(str(self.counter))

app = QtWidgets.QApplication(sys.argv)
ex = GUI()
sys.exit(app.exec_())

我可以使用以下命令从 Linux 终端执行脚本:

I am able to execute the script from the Linux terminal with the following command:

python3 TestGUI.py

GUI 按预期打开,我可以与按钮交互.

The GUI opens as intended and I can interact with the button.

一旦子进程被包含在脚本中,如下所示,GUI 仍然打开,但它完全是黑色且无响应.

As soon as a subprocess gets included in the script, like the one below, the GUI does still open but it is completely black and non-responsive.

p1 = subprocess.Popen("onedrive", stdout = subprocess.PIPE, shell = True)
(output, err) = p1.communicate()

我认为当您使用终端执行python脚本时,该脚本本身会在终端中执行命令.

I think that there is a problem when you use the terminal to execute a python script which itself executes commands in the terminal.

你知道如何解决这个问题吗?

Do you have any idea on how this problem can be solved?

非常感谢您的支持.

推荐答案

你不应该使用 Popen,因为 communicate() 方法被阻塞,改为使用 QProcess:

You should not use Popen since the communicate() method is blocking, instead use QProcess:

#!/usr/bin/python3.5

from PyQt5 import QtCore, QtWidgets


class GUI(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.initGUI()
        self.behaviours()

    def initGUI(self):
        self.button = QtWidgets.QPushButton("Button")
        self.label = QtWidgets.QLabel()

        box = QtWidgets.QVBoxLayout(self)
        box.addWidget(self.button)
        box.addWidget(self.label)

        self.show()

    def behaviours(self):
        self._onedrive_process = QtCore.QProcess(self)
        self._onedrive_process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
        self._onedrive_process.readyReadStandardOutput.connect(
            self.on_readyReadStandardOutput
        )
        self._onedrive_process.setProgram("onedrive")

        self.button.clicked.connect(self.connect_to_onedrive)

    @QtCore.pyqtSlot()
    def connect_to_onedrive(self):
        self._onedrive_process.start()

    @QtCore.pyqtSlot()
    def on_readyReadStandardOutput(self):
        result = self._onedrive_process.readAllStandardOutput()
        print(result)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    ex = GUI()
    sys.exit(app.exec_())

更新:

如果您想将选项传递给命令,您必须使用 setArguments():

If you want to pass options to the command you must use setArguments():

from PyQt5 import QtCore, QtGui, QtWidgets


class OneDriveManager(QtCore.QObject):
    logChanged = QtCore.pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)
        self._process = QtCore.QProcess(self)
        self._process.readyReadStandardOutput.connect(self.on_readyReadStandardOutput)
        self._process.setProgram("onedrive")

    def launch(self, options=None):
        self._process.setArguments(options)
        if self._process.state() != QtCore.QProcess.NotRunning:
            self._process.kill()
        self._process.start()

    def help(self):
        self.launch(["--help"])

    def synchronize(self):
        self.launch(["--synchronize"])

    @QtCore.pyqtSlot()
    def on_readyReadStandardOutput(self):
        res = self._process.readAllStandardOutput().data().decode()
        self.logChanged.emit(res)


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

        self._onedrive_manager = OneDriveManager(self)

        help_button = QtWidgets.QPushButton("Help")
        help_button.clicked.connect(self._onedrive_manager.help)

        synchronize_button = QtWidgets.QPushButton("Synchronize")
        synchronize_button.clicked.connect(self._onedrive_manager.synchronize)

        log_plaintextedit = QtWidgets.QPlainTextEdit()
        self._onedrive_manager.logChanged.connect(log_plaintextedit.setPlainText)

        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(help_button)
        lay.addWidget(synchronize_button)
        lay.addWidget(log_plaintextedit)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)

    w = Widget()
    w.show()

    sys.exit(app.exec_())

这篇关于从 Linux 终端执行包含子进程的 PyQt5 GUI 会导致 GUI 出现黑屏并冻结它的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆