【问题标题】:How can I add a path to the QProcess PATH environment variable? (PyQt5 on Python 3.7)如何添加 QProcess PATH 环境变量的路径? (Python 3.7 上的 PyQt5)
【发布时间】:2019-04-25 13:22:19
【问题描述】:

1。问题解释

我在应用程序显示其主窗口之前实例化了一个QProcess()-object。 QProcess()-instance 存储在self.__myProcess 变量中,只要您能看到主窗口,它就会一直存在。

主窗口如下所示:

当你点击按钮时,会执行以下代码:

def __btn_clicked(self):
    self.__add_openocd_to_env()
    command = "openocd.exe" + '\r\n'
    self.__myProcess.start(command)

最后两行很清楚:命令openocd.exe 被传递给self.__myProcess 并执行。 这个可执行文件的实际作用在这里并不重要。 事实上,我可以使用任何随机的可执行文件。关键是:如果可执行文件在我的 Windows PATH 环境变量中,它就会被找到并执行。

假设可执行文件不在PATH 环境变量中。那么函数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)

但是,我注意到它根本没有效果。我在这个函数中尝试了很多不同的东西,但它不会有任何效果。

 
你可以在这里找到完整的代码:
如果你安装了 Python 3 和 PyQt5,你可以简单地将代码复制粘贴到一个 .py 模块中并运行它。您应该看到带有按钮的小窗口。当然,您应该将路径“C:\Users\Kristof..”更改为计算机上有效的路径。您可以为此测试选择任何您喜欢的可执行文件。

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

 

2。我的问题

我知道在实例化 QProcess() 之前,我可以简单地将“C:\Users\Kristof\programs\openocd_0.10.0\bin”添加到我的 Windows PATH 环境变量中。但这不是重点。我想知道如何将它添加到特定QProcess()-instance 的PATH 环境变量中。如果可能,它不应该影响我软件中的任何其他QProcess()-instances,也不应该影响我以后创建的任何未来QProcess()-instances。

3。系统设置

我在 Windows 10 上使用 Python 3.7 中的 PyQt5 框架。


注意:
我刚刚尝试通过以下方式改进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)

我满怀希望......但它仍然无法正常工作:-(

【问题讨论】:

  • 我认为你误会了。您正试图影响传递给子进程的 PATH 环境。你想要做的只是改变parent中的PATH环境,它需要在子进程产生之前找到你的可执行文件。如果你愿意,那么在子进程生成之后将你父级的 PATH 改回
  • 嗨@JonBrave,你说得有道理。你能举个例子来说明一下吗?

标签: python python-3.x process environment-variables qprocess


【解决方案1】:

根据@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_())

基本上我正在执行以下操作:就在命令QProcess()-instance 启动命令之前,我将可执行路径添加到属于整个 Python 会话的PATH 环境变量。命令启动后,我可以再次删除它,这样它就不会影响将来创建的其他 QProcess()-instances。

它有效,但如果我要在我的软件中应用这种方法,它肯定需要大量的“簿记”(许多QProcess()-instances 存在于我的软件中)。如果您找到更好的方法,请不要犹豫分享!

【讨论】:

  • 看起来不错。抱歉,我没有时间输入答案!您可以可能start() 之后、waitForStarted() 之前直接调用__remove_openocd_from_env()(这样您就不需要了)以减少更改环境暴露时间(试试看?)。您可以从 QProcess 派生您自己的类,仅用于这种情况以避免更改其他类。最后,您可以从您当前目录中的PATH/ 中的.bat 文件执行此操作,该文件在调用openocd.exe 之前更改PATH,然后它在一个地方(并且您可以更改路径)并且您没有改变QProcess... :)
  • 刚刚注意到您的代码:command = "openocd.exe" + '\r\n'。把那些 CRLF 东西拿出来,它在发出操作系统命令时没有位置。
  • 最后,从您给出的示例中,如果您知道 exe 位于您要添加到PATH 的目录中,并且exe 本身不需要为自己的目的更改 PATH,只需自己制作 exe 的完整路径并以这种方式调用它,非常更简单。
  • 这个过程在 linux 上对我有用。有用,因为我打算制作自己的 IDE。感谢您的提问和回答。
  • 非常有趣的@SpencerWilliams,请在 LinkedIn 上加我。我的名字是:克里斯托夫穆里尔
【解决方案2】:

有一个解决方案是使用 python subprocess.run() 代替 QProcess。

在 subprocess.run() 中,您可以使用env 参数指定一组环境变量(实际上是字典)。思路是把你原来的环境复制一份,修改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)

这仍然不起作用:剩下的问题是环境(包括修改后的 PATH 变量)将在子进程中可用但不用于搜索 openocd 命令。但这很容易解决:subprocess.run 还有一个布尔值shell 参数(默认为 False),告诉它在 shell 中运行命令。由于 shell 将在子进程中运行,因此它将使用修改后的 PATH 来搜索 openocd。所以工作代码是:

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)

shell=True 的替代方法是使用 shutil.which(在 Python >= 3.3 中可用)来解析命令。当命令作为字符串列表而不是单个字符串给出时,这也将可靠地工作。

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)

【讨论】:

    猜你喜欢
    • 2019-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-19
    • 2020-07-30
    • 2017-02-21
    • 1970-01-01
    相关资源
    最近更新 更多