如何使用subprocess或QProcess启动独立的gnuplot进程?

2024-10-03 09:20:04 发布

您现在位置:Python中文网/ 问答频道 /正文

关于subprocessQProcess已经有很多问题和答案,但我还没有找到我的问题的答案。因此,如果我在这一大堆部分令人困惑或过时的答案中没有找到答案,请原谅

这听起来像是一个简单的任务:在PyQt GUI应用程序中,我想启动一个独立的(子)进程。 在我的例子中:gnuplot,一个打印工具,带有选项'-p'(持久,即打印完成时保持窗口打开)。一旦启动,它应该继续作为独立(交互式)应用程序运行

文件 ^{}和{a2}没有进一步帮助我

代码:(我的精简示例,没有按预期工作)

from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QPushButton, QVBoxLayout
from PyQt5.QtCore import pyqtSlot, QProcess
import sys, subprocess

class App(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()
        
    def initUI(self):
        self.setGeometry(0,0,700,500)
        self.layout = QVBoxLayout()

        self.pb_plot_1 = QPushButton("Plot via subprocess")
        self.pb_plot_1.clicked.connect(self.on_click_pb_plot_1)
        self.layout.addWidget(self.pb_plot_1)

        self.pb_plot_2 = QPushButton("Plot via QProcess")
        self.pb_plot_2.clicked.connect(self.on_click_pb_plot_2)
        self.layout.addWidget(self.pb_plot_2)
        
        self.pb_plot_3 = QPushButton("Plot via QProcess and loading a file")
        self.pb_plot_3.clicked.connect(self.on_click_pb_plot_3)
        self.layout.addWidget(self.pb_plot_3)
        
        self.setLayout(self.layout) 
        self.show()

    @pyqtSlot()
    
    def get_plot_code(self):
        return '''
        # some gnuplot code, in reality much longer
        set term wxt
        plot sin(x)
        '''
        
    def on_click_pb_plot_1(self):
        gnuplotPath = r'gnuplot\bin\gnuplot'
        gnuplotCode = self.get_plot_code()
        proc = subprocess.Popen([gnuplotPath, '-p'], stdin=subprocess.PIPE)
        proc.communicate(gnuplotCode.encode())

    def on_click_pb_plot_2(self):
        gnuplotPath = r'gnuplot\bin\gnuplot'
        gnuplotCode = self.get_plot_code()
        proc = QProcess(self)
        proc.start(gnuplotPath, ["-p"])
        proc.write(gnuplotCode.encode())
     
    def on_click_pb_plot_3(self):
        gnuplotPath = r'gnuplot\bin\gnuplot'
        gnuplotFile = 'gnuplotFile.gp'
        proc = QProcess(self)
        proc.start(gnuplotPath, ["-p", gnuplotFile])
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

结果:

如果我按下按钮,每次gnuplot都会打开,并显示图形。到目前为止,好的。 但是,

通过子流程:gnuplot将作为交互式窗口工作,但在关闭gnuplot之前,我的PyQt应用程序将冻结(不响应)。 在^{}中,似乎有close_fds=True选项,它可能会执行我想要的操作,但在Windows中,这似乎与通过stdin=subprocess.PIPE发送命令不起作用

Note that on Windows, you cannot set close_fds to true and also redirect the standard handles by setting stdin, stdout or stderr

那么,我如何使用subprocess来实现它呢?添加proc.terminate()没有帮助

通过QProcess:gnuplot将冻结,不会作为交互窗口工作(无响应)。如果我杀死gnuplot,我可以重新开始,但gnuplot会再次冻结

我也尝试过类似startDetached()的东西,等等。。。没有成功

我可以想象,只要对代码做一点小小的修改,这个问题就可以得到解决。但我不知道如何改变。 因此,我确信这是我对文档的有限理解,我缺少合适的示例。 我的配置:Win10、Python 3.6.3、PyQt 5.11.3

更新:(上面的代码已更改,添加了第三个变体)

通过QProcess和读取文件(而不是将代码写入gnuplot)

a)gnuplot将创建绘图,b)任何内容都不会冻结,c)gnuplot保持交互,d)我可以独立关闭我的应用程序或gnuplot。 然而,我不想从磁盘读取gnuplot文件,但我想将代码发送到gnuplot(我想是发送到stdin

更新2:

也许这可以给能解释这件事的人一个暗示。它似乎取决于gnuplot版本。结果如下:

5.2.0 QWindowsPipeWriter::write failed. (The handle is invalid.)
5.2.2 QWindowsPipeWriter::write failed. (The handle is invalid.)
5.2.3 QWindowsPipeWriter::write failed. (The handle is invalid.)
5.2.4 QWindowsPipeWriter::write failed. (The handle is invalid.)
5.2.5 QWindowsPipeWriter::write failed. (The handle is invalid.)

5.2.6 works
5.2.7 works
5.2.8 works

5.4.0 freezing
5.4.1 freezing

Tags: 代码selfplotondefprocwriteclick