diff --git a/src/plugins/python/pythoneditor.cpp b/src/plugins/python/pythoneditor.cpp index 42771fc91a2..ca6b606616a 100644 --- a/src/plugins/python/pythoneditor.cpp +++ b/src/plugins/python/pythoneditor.cpp @@ -251,11 +251,11 @@ void PythonEditorWidget::updateInterpretersSelector() if (self && venvInterpreter) self->setUserDefinedPython(*venvInterpreter); }; - PythonSettings::createVirtualEnvironment(self->textDocument() - ->filePath() - .parentDir(), - *currentInterpreter, - callback); + PythonSettings::createVirtualEnvironmentInteractive(self->textDocument() + ->filePath() + .parentDir(), + *currentInterpreter, + callback); }); } auto settingsAction = menu->addAction(Tr::tr("Manage Python Interpreters")); diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp index f22f0c3c8ce..ff3a18ef149 100644 --- a/src/plugins/python/pythonsettings.cpp +++ b/src/plugins/python/pythonsettings.cpp @@ -773,9 +773,11 @@ void PythonSettings::addInterpreter(const Interpreter &interpreter, bool isDefau saveSettings(); } -Interpreter PythonSettings::addInterpreter(const FilePath &interpreterPath, bool isDefault) +Interpreter PythonSettings::addInterpreter(const FilePath &interpreterPath, + bool isDefault, + const QString &nameSuffix) { - const Interpreter interpreter = createInterpreter(interpreterPath, {}); + const Interpreter interpreter = createInterpreter(interpreterPath, {}, nameSuffix); addInterpreter(interpreter, isDefault); return interpreter; } @@ -786,7 +788,7 @@ PythonSettings *PythonSettings::instance() return settingsInstance; } -void PythonSettings::createVirtualEnvironment( +void PythonSettings::createVirtualEnvironmentInteractive( const FilePath &startDirectory, const Interpreter &defaultInterpreter, const std::function)> &callback) @@ -829,14 +831,23 @@ void PythonSettings::createVirtualEnvironment( interpreters->currentData().toString()); auto venvDir = pathChooser->filePath(); - createVenv(interpreter.command, venvDir, [venvDir, callback](bool success){ + createVirtualEnvironment(venvDir, interpreter, callback); +} + +void PythonSettings::createVirtualEnvironment( + const FilePath &directory, + const Interpreter &interpreter, + const std::function)> &callback, + const QString &nameSuffix) +{ + createVenv(interpreter.command, directory, [directory, callback, nameSuffix](bool success) { std::optional result; if (success) { - FilePath venvPython = venvDir.osType() == Utils::OsTypeWindows ? venvDir / "Scripts" - : venvDir / "bin"; + FilePath venvPython = directory.osType() == Utils::OsTypeWindows ? directory / "Scripts" + : directory / "bin"; venvPython = venvPython.pathAppended("python").withExecutableSuffix(); if (venvPython.exists()) - result = PythonSettings::addInterpreter(venvPython); + result = PythonSettings::addInterpreter(venvPython, false, nameSuffix); } callback(result); }); diff --git a/src/plugins/python/pythonsettings.h b/src/plugins/python/pythonsettings.h index 2e27e266162..35939c1ecd1 100644 --- a/src/plugins/python/pythonsettings.h +++ b/src/plugins/python/pythonsettings.h @@ -25,13 +25,22 @@ public: static void setInterpreter(const QList &interpreters, const QString &defaultId); static void addInterpreter(const Interpreter &interpreter, bool isDefault = false); static Interpreter addInterpreter(const Utils::FilePath &interpreterPath, - bool isDefault = false); + bool isDefault = false, + const QString &nameSuffix = {}); static void setPyLSConfiguration(const QString &configuration); static bool pylsEnabled(); static void setPylsEnabled(const bool &enabled); static QString pylsConfiguration(); static PythonSettings *instance(); - static void createVirtualEnvironment(const Utils::FilePath &startDirectory, const Interpreter &defaultInterpreter, const std::function)> &callback); + static void createVirtualEnvironmentInteractive( + const Utils::FilePath &startDirectory, + const Interpreter &defaultInterpreter, + const std::function)> &callback); + static void createVirtualEnvironment( + const Utils::FilePath &directory, + const Interpreter &interpreter, + const std::function)> &callback, + const QString &nameSuffix = {}); static QList detectPythonVenvs(const Utils::FilePath &path); signals: diff --git a/src/plugins/python/pythonwizardpage.cpp b/src/plugins/python/pythonwizardpage.cpp index 1b5798f1733..b13cf3c346d 100644 --- a/src/plugins/python/pythonwizardpage.cpp +++ b/src/plugins/python/pythonwizardpage.cpp @@ -34,18 +34,18 @@ WizardPage *PythonWizardPageFactory::create(JsonWizard *wizard, Id typeId, const QTC_ASSERT(canCreate(typeId), return nullptr); - auto page = new PythonWizardPage; + QList> pySideAndData; for (const QVariant &item : data.toMap().value("items").toList()) { const QMap map = item.toMap(); const QVariant name = map.value("trKey"); if (name.isValid()) - page->addPySideVersions(name.toString(), map.value("value")); + pySideAndData.emplaceBack(QPair{name.toString(), map.value("value")}); } bool validIndex = false; - const int index = data.toMap().value("index").toInt(&validIndex); - if (validIndex) - page->setDefaultPySideVersions(index); - return page; + int defaultPySide = data.toMap().value("index").toInt(&validIndex); + if (!validIndex) + defaultPySide = -1; + return new PythonWizardPage(pySideAndData, defaultPySide); } static bool validItem(const QVariant &item) @@ -82,8 +82,10 @@ bool PythonWizardPageFactory::validateData(Id typeId, const QVariant &data, QStr return true; } -PythonWizardPage::PythonWizardPage() +PythonWizardPage::PythonWizardPage(const QList> &pySideAndData, + const int defaultPyside) { + using namespace Utils::Layouting; m_interpreter.setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID); connect(PythonSettings::instance(), &PythonSettings::interpretersChanged, @@ -92,27 +94,55 @@ PythonWizardPage::PythonWizardPage() m_pySideVersion.setLabelText(Tr::tr("PySide version")); m_pySideVersion.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + for (auto [name, data] : pySideAndData) + m_pySideVersion.addOption(SelectionAspect::Option(name, {}, data)); + if (defaultPyside >= 0) + m_pySideVersion.setDefaultValue(defaultPyside); + + m_createVenv.setLabelText(Tr::tr("Create new Virtual Environment")); + + m_venvPath.setLabelText(Tr::tr("Path to virtual environment")); + m_venvPath.setDisplayStyle(StringAspect::PathChooserDisplay); + m_venvPath.setEnabler(&m_createVenv); + m_venvPath.setExpectedKind(PathChooser::Directory); + + m_stateLabel = new InfoLabel(); + m_stateLabel->setWordWrap(true); + m_stateLabel->setFilled(true); + m_stateLabel->setType(InfoLabel::Error); + connect(&m_venvPath, &StringAspect::valueChanged, this, &PythonWizardPage::updateStateLabel); + connect(&m_createVenv, &BoolAspect::valueChanged, this, &PythonWizardPage::updateStateLabel); + + Grid { + m_pySideVersion, br, + m_interpreter, br, + m_createVenv, br, + m_venvPath, br, + m_stateLabel, br + }.attachTo(this, WithoutMargins); } void PythonWizardPage::initializePage() { - using namespace Utils::Layouting; - auto wiz = qobject_cast(wizard()); QTC_ASSERT(wiz, return); + connect(wiz, &JsonWizard::filesPolished, + this, &PythonWizardPage::setupProject, + Qt::UniqueConnection); + + const FilePath projectDir = FilePath::fromString(wiz->property("ProjectDirectory").toString()); + m_createVenv.setValue(!projectDir.isEmpty()); + if (m_venvPath.filePath().isEmpty()) + m_venvPath.setFilePath(projectDir.isEmpty() ? FilePath{} : projectDir / "venv"); updateInterpreters(); - - connect(wiz, &JsonWizard::filesPolished, this, &PythonWizardPage::setupProject); - - Grid { - m_pySideVersion, br, - m_interpreter, br - }.attachTo(this, WithoutMargins); + updateStateLabel(); } bool PythonWizardPage::validatePage() { + if (m_createVenv.value() && !m_venvPath.pathChooser()->isValid()) + return false; auto wiz = qobject_cast(wizard()); const QMap data = m_pySideVersion.itemValue().toMap(); for (auto it = data.begin(), end = data.end(); it != end; ++it) @@ -120,28 +150,40 @@ bool PythonWizardPage::validatePage() return true; } -void PythonWizardPage::addPySideVersions(const QString &name, const QVariant &data) -{ - m_pySideVersion.addOption(SelectionAspect::Option(name, {}, data)); -} - -void PythonWizardPage::setDefaultPySideVersions(int index) -{ - m_pySideVersion.setDefaultValue(index); -} - void PythonWizardPage::setupProject(const JsonWizard::GeneratorFiles &files) { for (const JsonWizard::GeneratorFile &f : files) { if (f.file.attributes() & Core::GeneratedFile::OpenProjectAttribute) { + Interpreter interpreter = m_interpreter.currentInterpreter(); Project *project = ProjectManager::openProject(Utils::mimeTypeForFile(f.file.filePath()), f.file.filePath().absoluteFilePath()); + if (m_createVenv.value()) { + auto openProjectWithInterpreter = [f](const std::optional &interpreter) { + if (!interpreter) + return; + Project *project = ProjectManager::projectWithProjectFilePath(f.file.filePath()); + if (!project) + return; + if (Target *target = project->activeTarget()) { + if (RunConfiguration *rc = target->activeRunConfiguration()) { + if (auto interpreters = rc->aspect()) + interpreters->setCurrentInterpreter(*interpreter); + } + } + }; + PythonSettings::createVirtualEnvironment(m_venvPath.filePath(), + interpreter, + openProjectWithInterpreter, + project ? project->displayName() + : QString{}); + } + if (project) { project->addTargetForDefaultKit(); if (Target *target = project->activeTarget()) { if (RunConfiguration *rc = target->activeRunConfiguration()) { if (auto interpreters = rc->aspect()) { - interpreters->setCurrentInterpreter(m_interpreter.currentInterpreter()); + interpreters->setCurrentInterpreter(interpreter); project->saveSettings(); } } @@ -158,5 +200,20 @@ void PythonWizardPage::updateInterpreters() m_interpreter.updateInterpreters(PythonSettings::interpreters()); } +void PythonWizardPage::updateStateLabel() +{ + QTC_ASSERT(m_stateLabel, return); + if (m_createVenv.value()) { + if (PathChooser *pathChooser = m_venvPath.pathChooser()) { + if (!pathChooser->isValid()) { + m_stateLabel->show(); + m_stateLabel->setText(pathChooser->errorMessage()); + return; + } + } + } + m_stateLabel->hide(); +} + } // namespace Python::Internal diff --git a/src/plugins/python/pythonwizardpage.h b/src/plugins/python/pythonwizardpage.h index e35e3ca55cb..1d605b24176 100644 --- a/src/plugins/python/pythonwizardpage.h +++ b/src/plugins/python/pythonwizardpage.h @@ -24,19 +24,20 @@ public: class PythonWizardPage : public Utils::WizardPage { public: - PythonWizardPage(); + PythonWizardPage(const QList> &pySideAndData, const int defaultPyside); void initializePage() override; bool validatePage() override; - void addPySideVersions(const QString &name, const QVariant &data); - void setDefaultPySideVersions(int index); - private: void setupProject(const ProjectExplorer::JsonWizard::GeneratorFiles &files); void updateInterpreters(); + void updateStateLabel(); ProjectExplorer::InterpreterAspect m_interpreter; Utils::SelectionAspect m_pySideVersion; + Utils::BoolAspect m_createVenv; + Utils::StringAspect m_venvPath; + Utils::InfoLabel *m_stateLabel = nullptr; }; } // namespace Python::Internal