Аргументы, передаваемые в venv.create(), не влияют на создание виртуальной среды.

Я работаю над мастером PyQt5 для создания виртуальных сред Python. Создание среды работает. Аргументы (например, с или без pip...) передаются из QCheckBox() к create() функция venv Модуль, который поставляется с Python.

Проблема в том, что аргументы переданы venv.create() не влияют на создание виртуальной среды. Он всегда создается с настройками по умолчанию (например, pip устанавливается по умолчанию).

Но если я пройду with_pip=False непосредственно, pip не будет установлен. Это означает, что по какой-то причине аргументы переданы args не имеют никакого влияния на создание. Я пытался преобразовать значения в bool(), но это не сработало.

Мне не понятно почему, потому что print(args) выходы True или же False (в виде строк), в зависимости от isChecked() статус соответствующего QCheckBox(),

Далее я пользуюсь location а также nameargs) построить путь к месту установки виртуальной среды, и это прекрасно работает.

Может кто-нибудь объяснить, почему venv.create() не принимает аргументы от isChecked() из QCheckBox()? Или я что-то упустил?


Код для воспроизведения:

from subprocess import Popen, PIPE, CalledProcessError
from functools import partial
import venv
import os

from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt, QObject, QTimer, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import (QApplication, QFileDialog, QGridLayout, QLabel,
                             QVBoxLayout, QWizard, QWizardPage, QProgressBar,
                             QCheckBox, QLineEdit, QGroupBox, QToolButton,
                             QComboBox, QDialog, QHBoxLayout)



#]===========================================================================[#
#] FIND INSTALLED INTERPRETERS [#============================================[#
#]===========================================================================[#

# look for installed Python 3 versions
versions = ['3.9', '3.8', '3.7', '3.6', '3.5', '3.4', '3.3', '3']

notFound = []
versFound = []
pathFound = []

for i, v in enumerate(versions):
    try:
        # get installed python3 versions
        getVers = Popen(["python" + v, "-V"],
                            stdout=PIPE, universal_newlines=True)
        version = getVers.communicate()[0].strip()

        # get paths of the python executables
        getPath = Popen(["which", "python" + v],
                            stdout=PIPE, universal_newlines=True)
        path = getPath.communicate()[0].strip()

        versFound.append(version)
        pathFound.append(path)

    except (CalledProcessError, FileNotFoundError):
        notFound.append(i)


#]===========================================================================[#
#] PROGRESS BAR [#===========================================================[#
#]===========================================================================[#

class ProgBarWidget(QDialog):
    """
    The dialog that shows a progress bar during the create process.
    """
    def __init__(self):
        super().__init__()
        self.initMe()

    def initMe(self):
        self.setGeometry(690, 365, 325, 80)
        self.setFixedSize(325, 80)
        self.setWindowTitle("Creating")
        self.setWindowFlag(Qt.WindowCloseButtonHint, False)
        self.setWindowFlag(Qt.WindowMinimizeButtonHint, False)

        horizontalLayout = QHBoxLayout(self)
        verticalLayout = QVBoxLayout()

        statusLabel = QLabel(self)
        statusLabel.setText("Creating virtual environment...")

        self.progressBar = QProgressBar(self)
        self.progressBar.setFixedSize(300, 23)
        self.progressBar.setRange(0, 0)

        verticalLayout.addWidget(statusLabel)
        verticalLayout.addWidget(self.progressBar)

        horizontalLayout.addLayout(verticalLayout)
        self.setLayout(horizontalLayout)



#]===========================================================================[#
#] VENV WIZARD [#============================================================[#
#]===========================================================================[#

class VenvWizard(QWizard):
    """
    Wizard for creating and setting up virtual environments.
    """
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Venv Wizard")
        self.resize(535, 430)
        self.move(578, 183)

        self.setStyleSheet(
            """
            QToolTip {
                background-color: rgb(47, 52, 63);
                border: rgb(47, 52, 63);
                color: rgb(210, 210, 210);
                padding: 2px;
                opacity: 325
            }
            """
        )

        self.addPage(BasicSettings())
        self.addPage(InstallPackages())
        self.addPage(Summary())


class BasicSettings(QWizardPage):
    """
    Basic settings of the virtual environment being created.
    """
    def __init__(self):
        super().__init__()

        folder_icon = QIcon.fromTheme("folder")

        self.setTitle("Basic Settings")
        self.setSubTitle("This wizard will help you to create and set up "
                         "a virtual environment for Python 3. ")


        #]===================================================================[#
        #] PAGE CONTENT [#===================================================[#
        #]===================================================================[#

        interpreterLabel = QLabel("&Interpreter:")
        self.interprComboBox = QComboBox()
        interpreterLabel.setBuddy(self.interprComboBox)

        # add items from versFound to combobox
        self.interprComboBox.addItem("---")
        for i in range(len(versFound)):
            self.interprComboBox.addItem(versFound[i], pathFound[i])

        venvNameLabel = QLabel("Venv &name:")
        self.venvNameLineEdit = QLineEdit()
        venvNameLabel.setBuddy(self.venvNameLineEdit)

        venvLocationLabel = QLabel("&Location:")
        self.venvLocationLineEdit = QLineEdit()
        venvLocationLabel.setBuddy(self.venvLocationLineEdit)

        selectFolderToolButton = QToolButton()
        selectFolderToolButton.setFixedSize(26, 27)
        selectFolderToolButton.setIcon(folder_icon)
        selectFolderToolButton.setToolTip("Browse")

        placeHolder = QLabel()

        # the 'options' groupbox
        groupBox = QGroupBox("Options")

        self.withPipCBox = QCheckBox("Install and update &Pip")
        self.sitePackagesCBox = QCheckBox(
            "&Make system (global) site-packages dir available to venv")
        self.launchVenvCBox = QCheckBox(
            "Launch a terminal with activated &venv after installation")
        self.symlinksCBox = QCheckBox(
            "Attempt to &symlink rather than copy files into venv")

        # events
        self.withPipCBox.toggled.connect(self.collectData)
        self.sitePackagesCBox.toggled.connect(self.collectData)
        self.launchVenvCBox.toggled.connect(self.collectData)
        self.venvNameLineEdit.textChanged.connect(self.collectData)
        self.venvLocationLineEdit.textChanged.connect(self.collectData)
        self.interprComboBox.currentIndexChanged.connect(self.collectData)
        self.symlinksCBox.toggled.connect(self.collectData)
        selectFolderToolButton.clicked.connect(self.selectDir)

        # store the collected data in line edits
        self.interprVers = QLineEdit()
        self.interprPath = QLineEdit()
        self.venvName = QLineEdit()
        self.venvLocation = QLineEdit()
        self.withPip = QLineEdit()
        self.sitePackages = QLineEdit()
        self.launchVenv = QLineEdit()
        self.symlinks = QLineEdit()

        # register fields
        self.registerField("interprComboBox*", self.interprComboBox)
        self.registerField("venvNameLineEdit*", self.venvNameLineEdit)
        self.registerField("venvLocationLineEdit*", self.venvLocationLineEdit)
        self.registerField("interprVers", self.interprVers)
        self.registerField("interprPath", self.interprPath)
        self.registerField("venvName", self.venvName)
        self.registerField("venvLocation", self.venvLocation)
        self.registerField("withPip", self.withPip)
        self.registerField("sitePackages", self.sitePackages)
        self.registerField("launchVenv", self.launchVenv)
        self.registerField("symlinks", self.symlinks)

        # grid layout
        gridLayout = QGridLayout()
        gridLayout.addWidget(interpreterLabel, 0, 0, 1, 1)
        gridLayout.addWidget(self.interprComboBox, 0, 1, 1, 2)
        gridLayout.addWidget(venvNameLabel, 1, 0, 1, 1)
        gridLayout.addWidget(self.venvNameLineEdit, 1, 1, 1, 2)
        gridLayout.addWidget(venvLocationLabel, 2, 0, 1, 1)
        gridLayout.addWidget(self.venvLocationLineEdit, 2, 1, 1, 1)
        gridLayout.addWidget(selectFolderToolButton, 2, 2, 1, 1)
        gridLayout.addWidget(placeHolder, 3, 0, 1, 2)
        gridLayout.addWidget(groupBox, 4, 0, 1, 3)
        self.setLayout(gridLayout)

        # 'options' groupbox
        groupBoxLayout = QVBoxLayout()
        groupBoxLayout.addWidget(self.withPipCBox)
        groupBoxLayout.addWidget(self.sitePackagesCBox)
        groupBoxLayout.addWidget(self.launchVenvCBox)
        groupBoxLayout.addWidget(self.symlinksCBox)
        groupBox.setLayout(groupBoxLayout)


    #]=======================================================================[#
    #] SELECTIONS [#=========================================================[#
    #]=======================================================================[#

    def selectDir(self):
        """
        Specify path where to create venv.
        """
        folderName = QFileDialog.getExistingDirectory()
        self.venvLocationLineEdit.setText(folderName)

    def collectData(self, i):
        """
        Collect all input data and create the virtual environment.
        """
        self.interprVers.setText(self.interprComboBox.currentText())
        self.interprPath.setText(self.interprComboBox.currentData())
        self.venvName.setText(self.venvNameLineEdit.text())
        self.venvLocation.setText(self.venvLocationLineEdit.text())

        # the 'options'
        self.withPip.setText(str(self.withPipCBox.isChecked()))
        self.sitePackages.setText(str(self.sitePackagesCBox.isChecked()))
        self.launchVenv.setText(str(self.launchVenvCBox.isChecked()))
        self.symlinks.setText(str(self.symlinksCBox.isChecked()))


#]===========================================================================[#
#] WORKER  [#================================================================[#
#]===========================================================================[#

class InstallWorker(QObject):
    """
    Worker informing about start and finish of the create process.
    """
    started = pyqtSignal()
    finished = pyqtSignal()

    @pyqtSlot(tuple)
    def install(self, args):
        self.started.emit()

        name, location, with_pip, site_packages, symlinks = args
        # outputs as excpected
        #print(args)
        #print("pip:", args[2], "\nsite-pkgs:", args[3], "\nsymlinks:", args[4])

        venv.create(
            os.path.join(location, name),  # 'location' and 'name' works
            with_pip=with_pip,  # installs pip always (the default)
            system_site_packages=site_packages,  # not tested yet
            symlinks=symlinks,  # never symlinking
        )

        self.finished.emit()


class InstallPackages(QWizardPage):
    """
    Install packages via `pip` into the created virtual environment.
    """
    def __init__(self):
        super().__init__()

        self.setTitle("Install Packages")
        self.setSubTitle("Specify the packages which you want Pip to "
                         "install into the virtual environment.")

        self.progressBar = ProgBarWidget()


        #]===================================================================[#
        #] THREAD  [#========================================================[#
        #]===================================================================[#

        thread = QThread(self)
        thread.start()

        self.m_install_worker = InstallWorker()
        self.m_install_worker.moveToThread(thread)

        self.m_install_worker.started.connect(self.progressBar.exec_)
        self.m_install_worker.finished.connect(self.progressBar.close)
        self.m_install_worker.finished.connect(self.reEnablePage)


        #]===================================================================[#
        #] PAGE CONTENT [#===================================================[#
        #]===================================================================[#

        # just some test content (page is still in development)
        TestLabel = QLabel("This is a test label:", self)
        TestLineEdit = QLineEdit(self)
        TestLabel.setBuddy(TestLineEdit)

        TestLabel2 = QLabel("This is a test label:", self)
        TestLineEdit2 = QLineEdit(self)
        TestLabel2.setBuddy(TestLineEdit2)

        v_layout = QVBoxLayout(self)
        v_layout.addWidget(TestLabel)
        v_layout.addWidget(TestLineEdit)
        v_layout.addWidget(TestLabel2)
        v_layout.addWidget(TestLineEdit2)
        self.setLayout(v_layout)


    def initializePage(self):
        #interprVers = self.field("interprVers")
        self.interprPath = self.field("interprPath")
        self.venvName = self.field("venvName")
        self.venvLocation = self.field("venvLocation")
        self.withPip = self.field("withPip")
        self.sitePackages = self.field("sitePackages")
        #launchVenv = self.field("launchVenv")
        self.symlinks = self.field("symlinks")

        # set the selected interpreter
        sys.executable = self.interprPath

        # run the create process
        self.createProcess()

        # disable the page as long as the progress bar is up
        self.setEnabled(False)


    def reEnablePage(self):
        """
        Re-enable page after the create process has finished.
        """
        self.setEnabled(True)


    def createProcess(self):
        """
        Create the virtual environment.
        """
        args = (
            self.venvName,
            self.venvLocation,
            self.withPip,
            self.sitePackages,
            self.symlinks,
        )

        wrapper = partial(self.m_install_worker.install, args)
        QTimer.singleShot(0, wrapper)



class Summary(QWizardPage):
    def __init__(self):
        super().__init__()

        self.setTitle("Summary")
        self.setSubTitle("...............")



if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)

    wizard = VenvWizard()
    wizard.show()

    sys.exit(app.exec_())

1 ответ

Решение

Вы создаете QLineEdit просто для сохранения ненужных данных, поскольку вы можете зарегистрировать свойства виджета в качестве статуса флажка. Вы также преобразуете логическое значение True или False в строку "True" или "False" соответственно, которую затем передаете в функцию venv.create(), которая преобразует его в логическое значение, но любая не пустая строка является истинной.

Решение состоит в том, чтобы зарегистрировать QCheckBox напрямую, в дополнение к другим виджетам.

class BasicSettings(QWizardPage):
    """
    Basic settings of the virtual environment being created.
    """

    def __init__(self):
        super().__init__()

        # ...
        groupBox.setLayout(groupBoxLayout)

        selectFolderToolButton.clicked.connect(self.selectDir)

        self.registerField("interprVers", self.interprComboBox, "currentText")
        self.registerField("interprPath", self.interprComboBox, "currentData")
        self.registerField("venvName", self.venvNameLineEdit)
        self.registerField("venvLocation", self.venvLocationLineEdit)

        self.registerField("withPip", self.withPipCBox)
        self.registerField("sitePackages", self.sitePackagesCBox)
        self.registerField("launchVenv", self.launchVenvCBox)
        self.registerField("symlinks", self.symlinksCBox)

    # ...
Другие вопросы по тегам