2023-03-07 13:00:44 +01:00
|
|
|
// Copyright (C) 2022 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
|
|
|
|
|
|
#include "pythonwizardpage.h"
|
|
|
|
|
|
|
|
|
|
#include "pythonconstants.h"
|
|
|
|
|
#include "pythonsettings.h"
|
|
|
|
|
#include "pythontr.h"
|
|
|
|
|
|
|
|
|
|
#include <coreplugin/generatedfile.h>
|
|
|
|
|
|
|
|
|
|
#include <utils/algorithm.h>
|
|
|
|
|
#include <utils/layoutbuilder.h>
|
|
|
|
|
#include <utils/mimeutils.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
|
|
|
|
#include <projectexplorer/project.h>
|
|
|
|
|
#include <projectexplorer/projectmanager.h>
|
|
|
|
|
#include <projectexplorer/target.h>
|
|
|
|
|
|
|
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
using namespace Utils;
|
|
|
|
|
|
|
|
|
|
namespace Python::Internal {
|
|
|
|
|
|
|
|
|
|
PythonWizardPageFactory::PythonWizardPageFactory()
|
|
|
|
|
{
|
|
|
|
|
setTypeIdsSuffix("PythonConfiguration");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WizardPage *PythonWizardPageFactory::create(JsonWizard *wizard, Id typeId, const QVariant &data)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(wizard)
|
|
|
|
|
|
|
|
|
|
QTC_ASSERT(canCreate(typeId), return nullptr);
|
|
|
|
|
|
2023-03-10 10:26:56 +01:00
|
|
|
QList<QPair<QString, QVariant>> pySideAndData;
|
2023-03-07 13:00:44 +01:00
|
|
|
for (const QVariant &item : data.toMap().value("items").toList()) {
|
|
|
|
|
const QMap<QString, QVariant> map = item.toMap();
|
|
|
|
|
const QVariant name = map.value("trKey");
|
|
|
|
|
if (name.isValid())
|
2023-03-10 10:26:56 +01:00
|
|
|
pySideAndData.emplaceBack(QPair<QString, QVariant>{name.toString(), map.value("value")});
|
2023-03-07 13:00:44 +01:00
|
|
|
}
|
|
|
|
|
bool validIndex = false;
|
2023-03-10 10:26:56 +01:00
|
|
|
int defaultPySide = data.toMap().value("index").toInt(&validIndex);
|
|
|
|
|
if (!validIndex)
|
|
|
|
|
defaultPySide = -1;
|
|
|
|
|
return new PythonWizardPage(pySideAndData, defaultPySide);
|
2023-03-07 13:00:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool validItem(const QVariant &item)
|
|
|
|
|
{
|
|
|
|
|
QMap<QString, QVariant> map = item.toMap();
|
|
|
|
|
if (!map.value("trKey").canConvert<QString>())
|
|
|
|
|
return false;
|
|
|
|
|
map = map.value("value").toMap();
|
|
|
|
|
return map.value("PySideVersion").canConvert<QString>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PythonWizardPageFactory::validateData(Id typeId, const QVariant &data, QString *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(canCreate(typeId), return false);
|
|
|
|
|
const QList<QVariant> items = data.toMap().value("items").toList();
|
|
|
|
|
|
|
|
|
|
if (items.isEmpty()) {
|
|
|
|
|
if (errorMessage) {
|
2023-06-19 11:37:18 +02:00
|
|
|
*errorMessage = Tr::tr("\"data\" of a Python wizard page expects a map with \"items\" "
|
2023-06-13 15:25:38 +02:00
|
|
|
"containing a list of objects.");
|
2023-03-07 13:00:44 +01:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!Utils::allOf(items, &validItem)) {
|
|
|
|
|
if (errorMessage) {
|
|
|
|
|
*errorMessage = Tr::tr(
|
2023-06-19 11:37:18 +02:00
|
|
|
"An item of Python wizard page data expects a \"trKey\" field containing the UI "
|
|
|
|
|
"visible string for that Python version and a \"value\" field containing an object "
|
|
|
|
|
"with a \"PySideVersion\" field used for import statements in the Python files.");
|
2023-03-07 13:00:44 +01:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-10 10:26:56 +01:00
|
|
|
PythonWizardPage::PythonWizardPage(const QList<QPair<QString, QVariant>> &pySideAndData,
|
|
|
|
|
const int defaultPyside)
|
2023-03-07 13:00:44 +01:00
|
|
|
{
|
2023-04-25 10:15:07 +02:00
|
|
|
using namespace Layouting;
|
2023-03-07 13:00:44 +01:00
|
|
|
m_interpreter.setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID);
|
|
|
|
|
connect(PythonSettings::instance(),
|
|
|
|
|
&PythonSettings::interpretersChanged,
|
|
|
|
|
this,
|
|
|
|
|
&PythonWizardPage::updateInterpreters);
|
|
|
|
|
|
2023-06-13 15:25:38 +02:00
|
|
|
m_pySideVersion.setLabelText(Tr::tr("PySide version:"));
|
2023-03-07 13:00:44 +01:00
|
|
|
m_pySideVersion.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
|
2023-03-10 10:26:56 +01:00
|
|
|
for (auto [name, data] : pySideAndData)
|
|
|
|
|
m_pySideVersion.addOption(SelectionAspect::Option(name, {}, data));
|
|
|
|
|
if (defaultPyside >= 0)
|
|
|
|
|
m_pySideVersion.setDefaultValue(defaultPyside);
|
|
|
|
|
|
2023-06-13 15:25:38 +02:00
|
|
|
m_createVenv.setLabelText(Tr::tr("Create new virtual environment"));
|
2023-03-10 10:26:56 +01:00
|
|
|
|
2023-06-13 15:25:38 +02:00
|
|
|
m_venvPath.setLabelText(Tr::tr("Path to virtual environment:"));
|
2023-03-10 10:26:56 +01:00
|
|
|
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);
|
2023-07-04 15:14:04 +02:00
|
|
|
connect(&m_venvPath, &StringAspect::validChanged, this, &PythonWizardPage::updateStateLabel);
|
2023-06-26 11:02:42 +02:00
|
|
|
connect(&m_createVenv, &BaseAspect::changed, this, &PythonWizardPage::updateStateLabel);
|
2023-03-10 10:26:56 +01:00
|
|
|
|
2023-07-04 14:31:15 +02:00
|
|
|
Form {
|
|
|
|
|
m_pySideVersion, st, br,
|
|
|
|
|
m_interpreter, st, br,
|
|
|
|
|
m_createVenv, st, br,
|
2023-03-10 10:26:56 +01:00
|
|
|
m_venvPath, br,
|
2023-07-04 14:31:15 +02:00
|
|
|
m_stateLabel, br
|
2023-05-02 12:51:03 +02:00
|
|
|
}.attachTo(this);
|
2023-03-07 13:00:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PythonWizardPage::initializePage()
|
|
|
|
|
{
|
|
|
|
|
auto wiz = qobject_cast<JsonWizard *>(wizard());
|
|
|
|
|
QTC_ASSERT(wiz, return);
|
2023-03-10 10:26:56 +01:00
|
|
|
connect(wiz, &JsonWizard::filesPolished,
|
|
|
|
|
this, &PythonWizardPage::setupProject,
|
|
|
|
|
Qt::UniqueConnection);
|
2023-03-07 13:00:44 +01:00
|
|
|
|
2023-03-10 10:26:56 +01:00
|
|
|
const FilePath projectDir = FilePath::fromString(wiz->property("ProjectDirectory").toString());
|
|
|
|
|
m_createVenv.setValue(!projectDir.isEmpty());
|
2023-06-29 17:21:18 +02:00
|
|
|
if (m_venvPath().isEmpty())
|
2023-06-29 17:18:26 +02:00
|
|
|
m_venvPath.setValue(projectDir.isEmpty() ? FilePath{} : projectDir / "venv");
|
2023-03-07 13:00:44 +01:00
|
|
|
|
2023-03-10 10:26:56 +01:00
|
|
|
updateInterpreters();
|
|
|
|
|
updateStateLabel();
|
2023-03-07 13:00:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PythonWizardPage::validatePage()
|
|
|
|
|
{
|
2023-03-10 10:26:56 +01:00
|
|
|
if (m_createVenv.value() && !m_venvPath.pathChooser()->isValid())
|
|
|
|
|
return false;
|
2023-03-07 13:00:44 +01:00
|
|
|
auto wiz = qobject_cast<JsonWizard *>(wizard());
|
|
|
|
|
const QMap<QString, QVariant> data = m_pySideVersion.itemValue().toMap();
|
|
|
|
|
for (auto it = data.begin(), end = data.end(); it != end; ++it)
|
|
|
|
|
wiz->setValue(it.key(), it.value());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PythonWizardPage::setupProject(const JsonWizard::GeneratorFiles &files)
|
|
|
|
|
{
|
|
|
|
|
for (const JsonWizard::GeneratorFile &f : files) {
|
|
|
|
|
if (f.file.attributes() & Core::GeneratedFile::OpenProjectAttribute) {
|
2023-03-10 10:26:56 +01:00
|
|
|
Interpreter interpreter = m_interpreter.currentInterpreter();
|
2023-03-07 13:00:44 +01:00
|
|
|
Project *project = ProjectManager::openProject(Utils::mimeTypeForFile(f.file.filePath()),
|
|
|
|
|
f.file.filePath().absoluteFilePath());
|
2023-03-10 10:26:56 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-06-29 17:21:18 +02:00
|
|
|
PythonSettings::createVirtualEnvironment(m_venvPath(),
|
2023-03-10 10:26:56 +01:00
|
|
|
interpreter,
|
|
|
|
|
openProjectWithInterpreter,
|
|
|
|
|
project ? project->displayName()
|
|
|
|
|
: QString{});
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-07 13:00:44 +01:00
|
|
|
if (project) {
|
|
|
|
|
project->addTargetForDefaultKit();
|
|
|
|
|
if (Target *target = project->activeTarget()) {
|
|
|
|
|
if (RunConfiguration *rc = target->activeRunConfiguration()) {
|
|
|
|
|
if (auto interpreters = rc->aspect<InterpreterAspect>()) {
|
2023-03-10 10:26:56 +01:00
|
|
|
interpreters->setCurrentInterpreter(interpreter);
|
2023-03-07 13:00:44 +01:00
|
|
|
project->saveSettings();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete project;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PythonWizardPage::updateInterpreters()
|
|
|
|
|
{
|
|
|
|
|
m_interpreter.setDefaultInterpreter(PythonSettings::defaultInterpreter());
|
|
|
|
|
m_interpreter.updateInterpreters(PythonSettings::interpreters());
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-10 10:26:56 +01:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-07 13:00:44 +01:00
|
|
|
} // namespace Python::Internal
|
|
|
|
|
|