Как я могу добавить путь к переменной среды QProcess PATH?(PyQt5 на Python 3.7) - PullRequest
0 голосов
/ 23 ноября 2018

1.Объясненная проблема

Я создаю QProcess() -объект непосредственно перед тем, как приложение показывает свое главное окно.QProcess() -экземпляр сохраняется в переменной self.__myProcess и остается активным, пока вы видите главное окно.

Главное окно выглядит так:

enter image description here

Когда вы нажимаете на кнопку, выполняется следующий код:

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.Мой вопрос

Я знаю, что мог бы просто добавить "C: \ Users \ Kristof \ Programs \ openocd_0.10.0 \ bin" в мою переменную среды Windows PATH, прежде чем создавать экземпляр QProcess().Но дело не в этом.Я хочу знать, как добавить его в переменную окружения PATH для этого конкретного экземпляра QProcess().Если возможно, это не должно влиять на другие QProcess() -экземпляры в моем программном обеспечении, а также не должно влиять на будущие QProcess() -экземпляры, которые я создаю позже.

3.Системные настройки

Я использую инфраструктуру PyQt5 в Python 3.7 в Windows 10.


ПРИМЕЧАНИЕ:
Я только что попытался улучшить 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)

Я надеялся ... но он все равно не будет работать :-(

Ответы [ 2 ]

0 голосов
/ 03 января 2019

Существует решение, использующее 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), который указывает ему запускать команду в оболочке.Поскольку оболочка будет работать в подпроцессе, она будет использовать измененный 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)
0 голосов
/ 23 ноября 2018

Основываясь на комментарии г-на @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() -экземпляр для запуска командыЯ добавляю путь к исполняемому файлу в переменную окружения PATH, которая принадлежит всему сеансу Python.После запуска команды я могу удалить ее снова, чтобы она не влияла на другие QProcess() -инстанции, созданные в будущем.

Она работает, но, безусловно, потребует много "бухгалтерии"«если я собираюсь применить этот подход в своем программном обеспечении (многие QProcess() -приложения живут в моем программном обеспечении).Если вы найдете лучший подход, пожалуйста, не стесняйтесь поделиться!

...