Python: add create venv option to the wizard

and optimize layouting

Fixes: PYSIDE-2152
Change-Id: If3ecb76c4bac885840f54fd382471ac22a06dee3
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
David Schulz
2023-03-10 10:26:56 +01:00
parent 3afe94777c
commit eb8c996f49
5 changed files with 123 additions and 45 deletions

View File

@@ -251,7 +251,7 @@ void PythonEditorWidget::updateInterpretersSelector()
if (self && venvInterpreter) if (self && venvInterpreter)
self->setUserDefinedPython(*venvInterpreter); self->setUserDefinedPython(*venvInterpreter);
}; };
PythonSettings::createVirtualEnvironment(self->textDocument() PythonSettings::createVirtualEnvironmentInteractive(self->textDocument()
->filePath() ->filePath()
.parentDir(), .parentDir(),
*currentInterpreter, *currentInterpreter,

View File

@@ -773,9 +773,11 @@ void PythonSettings::addInterpreter(const Interpreter &interpreter, bool isDefau
saveSettings(); 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); addInterpreter(interpreter, isDefault);
return interpreter; return interpreter;
} }
@@ -786,7 +788,7 @@ PythonSettings *PythonSettings::instance()
return settingsInstance; return settingsInstance;
} }
void PythonSettings::createVirtualEnvironment( void PythonSettings::createVirtualEnvironmentInteractive(
const FilePath &startDirectory, const FilePath &startDirectory,
const Interpreter &defaultInterpreter, const Interpreter &defaultInterpreter,
const std::function<void(std::optional<Interpreter>)> &callback) const std::function<void(std::optional<Interpreter>)> &callback)
@@ -829,14 +831,23 @@ void PythonSettings::createVirtualEnvironment(
interpreters->currentData().toString()); interpreters->currentData().toString());
auto venvDir = pathChooser->filePath(); 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<void(std::optional<Interpreter>)> &callback,
const QString &nameSuffix)
{
createVenv(interpreter.command, directory, [directory, callback, nameSuffix](bool success) {
std::optional<Interpreter> result; std::optional<Interpreter> result;
if (success) { if (success) {
FilePath venvPython = venvDir.osType() == Utils::OsTypeWindows ? venvDir / "Scripts" FilePath venvPython = directory.osType() == Utils::OsTypeWindows ? directory / "Scripts"
: venvDir / "bin"; : directory / "bin";
venvPython = venvPython.pathAppended("python").withExecutableSuffix(); venvPython = venvPython.pathAppended("python").withExecutableSuffix();
if (venvPython.exists()) if (venvPython.exists())
result = PythonSettings::addInterpreter(venvPython); result = PythonSettings::addInterpreter(venvPython, false, nameSuffix);
} }
callback(result); callback(result);
}); });

View File

@@ -25,13 +25,22 @@ public:
static void setInterpreter(const QList<Interpreter> &interpreters, const QString &defaultId); static void setInterpreter(const QList<Interpreter> &interpreters, const QString &defaultId);
static void addInterpreter(const Interpreter &interpreter, bool isDefault = false); static void addInterpreter(const Interpreter &interpreter, bool isDefault = false);
static Interpreter addInterpreter(const Utils::FilePath &interpreterPath, static Interpreter addInterpreter(const Utils::FilePath &interpreterPath,
bool isDefault = false); bool isDefault = false,
const QString &nameSuffix = {});
static void setPyLSConfiguration(const QString &configuration); static void setPyLSConfiguration(const QString &configuration);
static bool pylsEnabled(); static bool pylsEnabled();
static void setPylsEnabled(const bool &enabled); static void setPylsEnabled(const bool &enabled);
static QString pylsConfiguration(); static QString pylsConfiguration();
static PythonSettings *instance(); static PythonSettings *instance();
static void createVirtualEnvironment(const Utils::FilePath &startDirectory, const Interpreter &defaultInterpreter, const std::function<void (std::optional<Interpreter>)> &callback); static void createVirtualEnvironmentInteractive(
const Utils::FilePath &startDirectory,
const Interpreter &defaultInterpreter,
const std::function<void(std::optional<Interpreter>)> &callback);
static void createVirtualEnvironment(
const Utils::FilePath &directory,
const Interpreter &interpreter,
const std::function<void(std::optional<Interpreter>)> &callback,
const QString &nameSuffix = {});
static QList<Interpreter> detectPythonVenvs(const Utils::FilePath &path); static QList<Interpreter> detectPythonVenvs(const Utils::FilePath &path);
signals: signals:

View File

@@ -34,18 +34,18 @@ WizardPage *PythonWizardPageFactory::create(JsonWizard *wizard, Id typeId, const
QTC_ASSERT(canCreate(typeId), return nullptr); QTC_ASSERT(canCreate(typeId), return nullptr);
auto page = new PythonWizardPage; QList<QPair<QString, QVariant>> pySideAndData;
for (const QVariant &item : data.toMap().value("items").toList()) { for (const QVariant &item : data.toMap().value("items").toList()) {
const QMap<QString, QVariant> map = item.toMap(); const QMap<QString, QVariant> map = item.toMap();
const QVariant name = map.value("trKey"); const QVariant name = map.value("trKey");
if (name.isValid()) if (name.isValid())
page->addPySideVersions(name.toString(), map.value("value")); pySideAndData.emplaceBack(QPair<QString, QVariant>{name.toString(), map.value("value")});
} }
bool validIndex = false; bool validIndex = false;
const int index = data.toMap().value("index").toInt(&validIndex); int defaultPySide = data.toMap().value("index").toInt(&validIndex);
if (validIndex) if (!validIndex)
page->setDefaultPySideVersions(index); defaultPySide = -1;
return page; return new PythonWizardPage(pySideAndData, defaultPySide);
} }
static bool validItem(const QVariant &item) static bool validItem(const QVariant &item)
@@ -82,8 +82,10 @@ bool PythonWizardPageFactory::validateData(Id typeId, const QVariant &data, QStr
return true; return true;
} }
PythonWizardPage::PythonWizardPage() PythonWizardPage::PythonWizardPage(const QList<QPair<QString, QVariant>> &pySideAndData,
const int defaultPyside)
{ {
using namespace Utils::Layouting;
m_interpreter.setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID); m_interpreter.setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID);
connect(PythonSettings::instance(), connect(PythonSettings::instance(),
&PythonSettings::interpretersChanged, &PythonSettings::interpretersChanged,
@@ -92,27 +94,55 @@ PythonWizardPage::PythonWizardPage()
m_pySideVersion.setLabelText(Tr::tr("PySide version")); m_pySideVersion.setLabelText(Tr::tr("PySide version"));
m_pySideVersion.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); 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() void PythonWizardPage::initializePage()
{ {
using namespace Utils::Layouting;
auto wiz = qobject_cast<JsonWizard *>(wizard()); auto wiz = qobject_cast<JsonWizard *>(wizard());
QTC_ASSERT(wiz, return); 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(); updateInterpreters();
updateStateLabel();
connect(wiz, &JsonWizard::filesPolished, this, &PythonWizardPage::setupProject);
Grid {
m_pySideVersion, br,
m_interpreter, br
}.attachTo(this, WithoutMargins);
} }
bool PythonWizardPage::validatePage() bool PythonWizardPage::validatePage()
{ {
if (m_createVenv.value() && !m_venvPath.pathChooser()->isValid())
return false;
auto wiz = qobject_cast<JsonWizard *>(wizard()); auto wiz = qobject_cast<JsonWizard *>(wizard());
const QMap<QString, QVariant> data = m_pySideVersion.itemValue().toMap(); const QMap<QString, QVariant> data = m_pySideVersion.itemValue().toMap();
for (auto it = data.begin(), end = data.end(); it != end; ++it) for (auto it = data.begin(), end = data.end(); it != end; ++it)
@@ -120,28 +150,40 @@ bool PythonWizardPage::validatePage()
return true; 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) void PythonWizardPage::setupProject(const JsonWizard::GeneratorFiles &files)
{ {
for (const JsonWizard::GeneratorFile &f : files) { for (const JsonWizard::GeneratorFile &f : files) {
if (f.file.attributes() & Core::GeneratedFile::OpenProjectAttribute) { if (f.file.attributes() & Core::GeneratedFile::OpenProjectAttribute) {
Interpreter interpreter = m_interpreter.currentInterpreter();
Project *project = ProjectManager::openProject(Utils::mimeTypeForFile(f.file.filePath()), Project *project = ProjectManager::openProject(Utils::mimeTypeForFile(f.file.filePath()),
f.file.filePath().absoluteFilePath()); f.file.filePath().absoluteFilePath());
if (m_createVenv.value()) {
auto openProjectWithInterpreter = [f](const std::optional<Interpreter> &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<InterpreterAspect>())
interpreters->setCurrentInterpreter(*interpreter);
}
}
};
PythonSettings::createVirtualEnvironment(m_venvPath.filePath(),
interpreter,
openProjectWithInterpreter,
project ? project->displayName()
: QString{});
}
if (project) { if (project) {
project->addTargetForDefaultKit(); project->addTargetForDefaultKit();
if (Target *target = project->activeTarget()) { if (Target *target = project->activeTarget()) {
if (RunConfiguration *rc = target->activeRunConfiguration()) { if (RunConfiguration *rc = target->activeRunConfiguration()) {
if (auto interpreters = rc->aspect<InterpreterAspect>()) { if (auto interpreters = rc->aspect<InterpreterAspect>()) {
interpreters->setCurrentInterpreter(m_interpreter.currentInterpreter()); interpreters->setCurrentInterpreter(interpreter);
project->saveSettings(); project->saveSettings();
} }
} }
@@ -158,5 +200,20 @@ void PythonWizardPage::updateInterpreters()
m_interpreter.updateInterpreters(PythonSettings::interpreters()); 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 } // namespace Python::Internal

View File

@@ -24,19 +24,20 @@ public:
class PythonWizardPage : public Utils::WizardPage class PythonWizardPage : public Utils::WizardPage
{ {
public: public:
PythonWizardPage(); PythonWizardPage(const QList<QPair<QString, QVariant>> &pySideAndData, const int defaultPyside);
void initializePage() override; void initializePage() override;
bool validatePage() override; bool validatePage() override;
void addPySideVersions(const QString &name, const QVariant &data);
void setDefaultPySideVersions(int index);
private: private:
void setupProject(const ProjectExplorer::JsonWizard::GeneratorFiles &files); void setupProject(const ProjectExplorer::JsonWizard::GeneratorFiles &files);
void updateInterpreters(); void updateInterpreters();
void updateStateLabel();
ProjectExplorer::InterpreterAspect m_interpreter; ProjectExplorer::InterpreterAspect m_interpreter;
Utils::SelectionAspect m_pySideVersion; Utils::SelectionAspect m_pySideVersion;
Utils::BoolAspect m_createVenv;
Utils::StringAspect m_venvPath;
Utils::InfoLabel *m_stateLabel = nullptr;
}; };
} // namespace Python::Internal } // namespace Python::Internal