[英]How can I add a path to the QProcess PATH environment variable? (PyQt5 on Python 3.7)
I instantiate a QProcess()
-object just before the application shows its main window. 我在应用程序显示其主窗口之前实例化了
QProcess()
。 The QProcess()
-instance is stored in the self.__myProcess
variable, and stays alive as long as you can see the main window. QProcess()
self.__myProcess
存储在self.__myProcess
变量中,只要您可以看到主窗口, self.__myProcess
保持活动状态。
The main window looks like this: 主窗口如下所示:
When you click on the button, the following code executes: 当您单击按钮时,将执行以下代码:
def __btn_clicked(self):
self.__add_openocd_to_env()
command = "openocd.exe" + '\r\n'
self.__myProcess.start(command)
The last two lines are quite clear: the command openocd.exe
is passed to self.__myProcess
and executes. 最后两行很清楚:将
openocd.exe
命令传递给self.__myProcess
并执行。 What this executable actually does is not important here. 此可执行文件的实际作用在这里并不重要。 In fact, I could use any random executable.
实际上,我可以使用任何随机可执行文件。 The point is: if the executable is in my Windows
PATH
environment variable, it gets found and executed. 关键是:如果可执行文件在我的Windows
PATH
环境变量中,则会找到并执行它。
Imagine the executable is NOT in the PATH
environment variable. 假设可执行文件
PATH
环境变量中。 Then the function self.__add_openocd_to_env()
should fix that issue: 然后,函数
self.__add_openocd_to_env()
应该解决该问题:
def __add_openocd_to_env(self):
env = self.__myProcess.processEnvironment()
env.insert("PATH", "C:\\Users\\Kristof\\programs\\openocd_0.10.0\\bin;" + env.value("PATH"))
self.__myProcess.setProcessEnvironment(env)
However, I've noticed it has no effect at all. 但是,我注意到它根本没有作用。 I have tried a lot of different things in this function, but it just won't have any effect.
我已经在此函数中尝试了许多不同的方法,但是它没有任何作用。
You can find the full code here: 您可以在此处找到完整的代码:
If you have Python 3 installed with PyQt5, you can simply copy-paste the code into a .py module and run it. 如果您在PyQt5上安装了Python 3,则只需将代码复制粘贴到.py模块中并运行它。 You should see the little window with the pushbutton.
您应该会看到带有按钮的小窗口。 Of course you should change the path "C:\\Users\\Kristof.." to something valid on your computer.
当然,您应该将路径“ C:\\ Users \\ Kristof ..”更改为计算机上有效的名称。 You can choose any executable you like for this test.
您可以选择此测试所需的任何可执行文件。
import sys
import os
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class CustomMainWindow(QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# -------------------------------- #
# QProcess() setup #
# -------------------------------- #
self.__myProcess = QProcess()
self.__myProcess.setProcessChannelMode(QProcess.MergedChannels)
self.__myProcess.readyRead.connect(self.__on_output)
self.__myProcess.errorOccurred.connect(self.__on_error)
self.__myProcess.finished.connect(self.__on_exit)
# -------------------------------- #
# Window setup #
# -------------------------------- #
self.setGeometry(100, 100, 800, 200)
self.setWindowTitle("QProcess test")
self.__frm = QFrame(self)
self.__frm.setStyleSheet("QWidget { background-color: #ffffff }")
self.__lyt = QVBoxLayout()
self.__lyt.setAlignment(Qt.AlignTop)
self.__frm.setLayout(self.__lyt)
self.setCentralWidget(self.__frm)
self.__myBtn = QPushButton("START QPROCESS()")
self.__myBtn.clicked.connect(self.__btn_clicked)
self.__myBtn.setFixedHeight(70)
self.__myBtn.setFixedWidth(200)
self.__lyt.addWidget(self.__myBtn)
self.show()
def __add_openocd_to_env(self):
env = self.__myProcess.processEnvironment()
env.insert("PATH", "C:\\Users\\Kristof\\programs\\openocd_0.10.0\\bin;" + env.value("PATH"))
self.__myProcess.setProcessEnvironment(env)
def __btn_clicked(self):
self.__add_openocd_to_env()
command = "openocd.exe" + '\r\n'
self.__myProcess.start(command)
def __on_output(self):
data = bytes(self.__myProcess.readAll()).decode().replace('\r\n', '\n')
print(data)
def __on_error(self, error):
print("")
print("Process error: {0}".format(str(error)))
print("")
def __on_exit(self, exitCode, exitStatus):
print("")
print("ExitCode = {0}".format(str(exitCode)))
print("ExitStatus = {0}".format(str(exitStatus)))
print("")
if __name__ == '__main__':
app = QApplication(sys.argv)
QApplication.setStyle(QStyleFactory.create('Fusion'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
I know I could simply add "C:\\Users\\Kristof\\programs\\openocd_0.10.0\\bin" to my Windows PATH
environment variable before instantiating the QProcess()
. 我知道我可以在实例化
QProcess()
之前将“ C:\\ Users \\ Kristof \\ programs \\ openocd_0.10.0 \\ bin”添加到Windows PATH
环境变量中。 But that's not the point. 但这不是重点。 I want to know how to add it to the
PATH
environment variable for that one specific QProcess()
-instance. 我想知道如何将其添加到该特定
QProcess()
instance的PATH
环境变量中。 If possible, it should not affect any other QProcess()
-instances around in my software, nor should it affect any future QProcess()
-instances I create later on. 如果可能,它不应影响软件中的任何其他
QProcess()
实例,也不应影响我以后创建的任何将来的QProcess()
实例。
I use the PyQt5 framework in Python 3.7 on Windows 10. 我在Windows 10的Python 3.7中使用PyQt5框架。
NOTE:
注意:
I've just tried to improve the QProcess()
setup in the following way: 我刚刚尝试通过以下方式改进
QProcess()
设置:
# -------------------------------- #
# QProcess() setup #
# -------------------------------- #
self.__myProcess = QProcess()
self.__myProcess.setProcessChannelMode(QProcess.MergedChannels)
self.__myProcess.readyRead.connect(self.__on_output)
self.__myProcess.errorOccurred.connect(self.__on_error)
self.__myProcess.finished.connect(self.__on_exit)
# NEW: initialize the environment variables for self.__myProcess:
env = QProcessEnvironment.systemEnvironment()
self.__myProcess.setProcessEnvironment(env)
I was hopefull ... but it still won't work :-( 我满怀希望...但是它仍然行不通:-(
Based on the comment of Mr. @JonBrave, I have written the following workaround: 基于@JonBrave先生的评论,我编写了以下解决方法:
import sys
import os
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class CustomMainWindow(QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# -------------------------------- #
# QProcess() setup #
# -------------------------------- #
self.__myProcess = QProcess()
self.__myProcess.setProcessChannelMode(QProcess.MergedChannels)
self.__myProcess.readyRead.connect(self.__on_output)
self.__myProcess.errorOccurred.connect(self.__on_error)
self.__myProcess.finished.connect(self.__on_exit)
# -------------------------------- #
# Window setup #
# -------------------------------- #
self.setGeometry(100, 100, 800, 200)
self.setWindowTitle("QProcess test")
self.__frm = QFrame(self)
self.__frm.setStyleSheet("QWidget { background-color: #ffffff }")
self.__lyt = QVBoxLayout()
self.__lyt.setAlignment(Qt.AlignTop)
self.__frm.setLayout(self.__lyt)
self.setCentralWidget(self.__frm)
self.__myBtn = QPushButton("START QPROCESS()")
self.__myBtn.clicked.connect(self.__btn_clicked)
self.__myBtn.setFixedHeight(70)
self.__myBtn.setFixedWidth(200)
self.__lyt.addWidget(self.__myBtn)
self.show()
def __add_openocd_to_env(self):
self.__oldEnv = os.environ["PATH"]
os.environ["PATH"] = "C:\\Users\\Kristof\\Dropbox (Personal)\\EMBEDOFFICE\\embedoffice\\resources\\programs\\openocd_0.10.0_dev00459\\bin;" + self.__oldEnv
def __remove_openocd_from_env(self):
os.environ["PATH"] = self.__oldEnv
def __btn_clicked(self):
self.__add_openocd_to_env()
command = "openocd.exe" + '\r\n'
self.__myProcess.start(command)
self.__myProcess.waitForStarted(-1)
self.__remove_openocd_from_env()
def __on_output(self):
data = bytes(self.__myProcess.readAll()).decode().replace('\r\n', '\n')
print(data)
def __on_error(self, error):
print("")
print("Process error: {0}".format(str(error)))
print("")
def __on_exit(self, exitCode, exitStatus):
print("")
print("ExitCode = {0}".format(str(exitCode)))
print("ExitStatus = {0}".format(str(exitStatus)))
print("")
if __name__ == '__main__':
app = QApplication(sys.argv)
QApplication.setStyle(QStyleFactory.create('Fusion'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
Basically I'm doing the following: just before ordering the QProcess()
-instance to start a command, I add the executable path to the PATH
environment variable that belongs to the whole Python session. 基本上,我正在执行以下操作:在命令
QProcess()
instance开始命令之前,我将可执行路径添加到属于整个Python会话的PATH
环境变量中。 Once the command has started, I can remove it again so it won't have an effect on other QProcess()
-instances created in the future. 命令启动后,我可以再次将其删除,以免对以后创建的其他
QProcess()
实例产生影响。
It works, but it will certainly require a lot of "bookkeeping" if I'm going to apply that approach in my software (many QProcess()
-instances live in my software). 它可以工作,但是如果我要在软件中应用这种方法(许多
QProcess()
instances都存在于我的软件中),肯定会需要很多“簿记”操作。 If you find a better approach, please don't hesitate to share! 如果您找到更好的方法,请随时分享!
There is a solution using python subprocess.run() instead of QProcess. 有一个使用python subprocess.run()而不是QProcess的解决方案。
In subprocess.run(), you can specify a set of environment variables (actually a dictionary) using the env
parameter. 在subprocess.run()中,可以使用
env
参数指定一组环境变量(实际上是字典)。 The idea is to take a copy of your original environment, modify the PATH variable, and pass the modified environment to subprocess.run, as follows: 这个想法是复制原始环境,修改PATH变量,然后将修改后的环境传递给subprocess.run,如下所示:
env = os.environ.copy()
env['PATH'] = "C:\\Users\\Kristof\\programs\\openocd_0.10.0\\bin" \
+ os.pathsep + env['PATH']
subprocess.run("openocd", env=env)
This still doesn't work: the remaining problem is that the environment (including the modified PATH variable) will be available in the subprocess but is not used to search for the openocd command. 这仍然不起作用:剩下的问题是该环境(包括修改后的PATH变量)将在子进程中可用,但不能用于搜索openocd命令。 But that is easy to fix: subprocess.run also has a boolean
shell
parameter (default False) that tells it to run the command in a shell. 但这很容易解决:subprocess.run也有一个布尔
shell
参数(默认为False),告诉它在shell中运行命令。 Since the shell will run in the subprocess, it will use the modified PATH to search for openocd. 由于外壳程序将在子进程中运行,因此它将使用修改后的PATH搜索openocd。 So working code is:
因此,工作代码为:
env = os.environ.copy()
env['PATH'] = "C:\\Users\\Kristof\\programs\\openocd_0.10.0\\bin" \
+ os.pathsep + env['PATH']
subprocess.run("openocd", env=env, shell=True)
An alternative for shell=True is to use shutil.which (available in Python >= 3.3) to resolve the command. shell = True的替代方法是使用shutil.which(在Python> = 3.3中可用)来解析命令。 This will also work reliably when the command is given as a list of strings instead of a single string.
当命令以字符串列表而不是单个字符串给出时,这也将可靠地工作。
env = os.environ.copy()
env['PATH'] = "C:\\Users\\Kristof\\programs\\openocd_0.10.0\\bin" \
+ os.pathsep + env['PATH']
command = shutil.which("openocd", path = self.env.get('PATH', None))
subprocess.run([ command ], env=env)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.