【发布时间】:2019-12-01 17:47:17
【问题描述】:
我正在使用 PyQt5 向导来创建 Python 虚拟环境。创建环境有效。参数(例如,带或不带pip,...)从QCheckBox() 传递到Python 附带的venv 模块的create() 函数。
问题是传递给venv.create()的参数对虚拟环境的创建没有影响。它始终使用默认设置创建(例如,默认安装pip)。
但是如果我直接通过with_pip=False,pip就不会被安装。这意味着,出于某种原因,从args 传递的参数对创建没有影响。我尝试将这些值转换为bool(),但这不起作用。
我不清楚为什么,因为print(args) 输出True 或False(作为字符串),这取决于对应QCheckBox() 的isChecked() 状态。
此外,我正在使用location 和name(在args 中)构建将安装虚拟环境的路径,这很好用。
有人可以解释为什么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_())
【问题讨论】:
标签: python pyqt5 python-venv qwizard qwizardpage