如何使用子进程或 QProcess 启动独立的 gnuplot 进程?
Posted
技术标签:
【中文标题】如何使用子进程或 QProcess 启动独立的 gnuplot 进程?【英文标题】:How to start an independent gnuplot process with either subprocess or QProcess? 【发布时间】:2021-06-17 09:59:30 【问题描述】:已经有很多关于subprocess
和QProcess
的问题和答案,但我还没有找到我的问题的答案。所以,如果我在这批部分混乱或过时的答案中没有找到答案,请原谅。
这听起来很简单:在 PyQt GUI 应用程序中,我想启动一个独立的(子)进程。 在我的例子中:gnuplot,一个绘图工具,带有选项“-p”(持久,即绘图完成时保持窗口打开)。启动后,它应继续作为独立(交互式)应用程序运行。
文档
QProcess
和 subprocess
没有进一步帮助我。
代码:(我的精简示例,不能按预期工作)
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 将作为交互式窗口工作,但我的 PyQt 应用程序将冻结(不响应),直到我关闭 gnuplot。
使用subprocess.Popen
似乎有close_fds=True
选项,它可能应该做我想要的,但在Windows 中,这似乎与通过stdin=subprocess.PIPE
发送命令一起使用。
请注意,在 Windows 上,您不能将 close_fds 设置为 true,并且 通过设置 stdin、stdout 或 stderr 重定向标准句柄
那么,我还能用subprocess
做些什么呢?添加proc.terminate()
将无济于事。
通过 QProcess: gnuplot 将冻结并且不能作为交互式窗口工作(无响应)。如果我杀死 gnuplot,我可以重新开始,但 gnuplot 会再次冻结。
我也尝试过类似startDetached()
等...但没有成功...
我可以想象这个问题可以通过代码中的微小变化来解决。但我不知道如何改变。 因此,我确信这是我对文档的有限理解,并且我缺少合适的示例。 我的配置:Win10、Python 3.6.3、PyQt 5.11.3
更新:(上面的代码已更改,添加了第 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
【问题讨论】:
我没有观察到它在 Linux、python 3.9.5 和 PyQt5 5.15.4 中使用 QProcess 或子进程冻结 @eyllanesc 感谢您的检查。嗯,又是一个烦人的 Windows 特例? 您的指示对我来说似乎很奇怪,因为 QProcess 不应该阻止任何东西,您提供的代码是生成冻结的代码还是您使用了其他代码? @eyllanesc 我使用的正是这段代码。那么,这可能是 Windows 下的 gnuplot 特定“问题”吗?我将修改答案并添加另一个观察结果。 嗯,QProcess 不应该阻止任何东西,所以我觉得很奇怪。尝试更新版本的 PyQt5,因为它可能是一个错误 【参考方案1】:好的,@eyllanesc 是对的...我将 PyQt5 从 5.11.3 更新到 5.15.4,问题似乎不再出现。无论这是什么错误或不兼容...
【讨论】:
以上是关于如何使用子进程或 QProcess 启动独立的 gnuplot 进程?的主要内容,如果未能解决你的问题,请参考以下文章
Qt使用QProcess进程间双向通信(linux和win系统)