Python: use kits page in python wizards

Change-Id: I1f7aaf145443481546abb868c8c167186600b848
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2023-11-01 15:05:03 +01:00
parent 12428bf1d6
commit 09e94ae4ac
33 changed files with 870 additions and 680 deletions

View File

@@ -13,7 +13,8 @@
"options":
[
{ "key": "SrcFileName", "value": "main.py" },
{ "key": "PyProjectFile", "value": "%{JS: Util.fileName('%{ProjectName}', 'pyproject')}" }
{ "key": "PyProjectFile", "value": "%{JS: Util.fileName('%{ProjectName}', 'pyproject')}" },
{ "key": "ProjectFilePath", "value": "%{ProjectDirectory}/%{PyProjectFile}" }
],
"pages":
@@ -49,6 +50,15 @@
]
}
},
{
"trDisplayName": "Kit Selection",
"trShortTitle": "Kits",
"typeId": "Kits",
"data": {
"projectFilePath": "%{ProjectFilePath}",
"requiredFeatures": [ "Python.Interpreter" ]
}
},
{
"trDisplayName": "Project Management",
"trShortTitle": "Summary",

View File

@@ -13,7 +13,8 @@
"options":
[
{ "key": "MainPyFileName", "value": "%{ProjectDirectory}/%{SrcFileName}" },
{ "key": "PyProjectFile", "value": "%{ProjectDirectory}/%{ProjectFileName}" }
{ "key": "PyProjectFile", "value": "%{ProjectFileName}" },
{ "key": "ProjectFilePath", "value": "%{ProjectDirectory}/%{PyProjectFile}" }
],
"pages":
@@ -93,6 +94,15 @@
]
}
},
{
"trDisplayName": "Kit Selection",
"trShortTitle": "Kits",
"typeId": "Kits",
"data": {
"projectFilePath": "%{ProjectFilePath}",
"requiredFeatures": [ "Python.Interpreter" ]
}
},
{
"trDisplayName": "Project Management",
"trShortTitle": "Summary",

View File

@@ -17,7 +17,8 @@
{ "key": "PyProjectFile", "value": "%{JS: Util.fileName('%{ProjectName}', 'pyproject')}" },
{ "key": "QtQuickVersion", "value": "%{JS: value('QtVersion').QtQuickVersion}" },
{ "key": "QtQuickWindowVersion", "value": "%{JS: value('QtVersion').QtQuickWindowVersion}" },
{ "key": "PySideVersion", "value": "%{JS: value('QtVersion').PySideVersion}" }
{ "key": "PySideVersion", "value": "%{JS: value('QtVersion').PySideVersion}" },
{ "key": "ProjectFilePath", "value": "%{ProjectDirectory}/%{PyProjectFile}" }
],
"pages":
@@ -84,6 +85,15 @@
]
}
},
{
"trDisplayName": "Kit Selection",
"trShortTitle": "Kits",
"typeId": "Kits",
"data": {
"projectFilePath": "%{ProjectFilePath}",
"requiredFeatures": [ "Python.Interpreter" ]
}
},
{
"trDisplayName": "Project Management",
"trShortTitle": "Summary",

View File

@@ -13,7 +13,8 @@
"options":
[
{ "key": "SrcFileName", "value": "%{MainFileName}" },
{ "key": "PyProjectFile", "value": "%{ProjectFileName}" }
{ "key": "PyProjectFile", "value": "%{ProjectFileName}" },
{ "key": "ProjectFilePath", "value": "%{ProjectDirectory}/%{PyProjectFile}" }
],
"pages":
@@ -93,6 +94,15 @@
]
}
},
{
"trDisplayName": "Kit Selection",
"trShortTitle": "Kits",
"typeId": "Kits",
"data": {
"projectFilePath": "%{ProjectFilePath}",
"requiredFeatures": [ "Python.Interpreter" ]
}
},
{
"trDisplayName": "Project Management",
"trShortTitle": "Summary",

View File

@@ -908,10 +908,10 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
m_runParameters.nativeMixedEnabled = bool(nativeMixedOverride);
if (auto interpreterAspect = runControl->aspect<InterpreterAspect>()) {
if (auto interpreterAspect = runControl->aspect<FilePathAspect>()) {
if (auto mainScriptAspect = runControl->aspect<MainScriptAspect>()) {
const FilePath mainScript = mainScriptAspect->filePath;
const FilePath interpreter = interpreterAspect->interpreter.command;
const FilePath interpreter = interpreterAspect->filePath;
if (!interpreter.isEmpty() && mainScript.endsWith(".py")) {
m_runParameters.mainScript = mainScript;
m_runParameters.interpreter = interpreter;

View File

@@ -4,7 +4,7 @@ add_qtc_plugin(Python
SOURCES
pipsupport.cpp pipsupport.h
pyside.cpp pyside.h
pysidebuildconfiguration.cpp pysidebuildconfiguration.h
pythonbuildconfiguration.cpp pythonbuildconfiguration.h
pysideuicextracompiler.cpp pysideuicextracompiler.h
python.qrc
pythonbuildsystem.cpp pythonbuildsystem.h

View File

@@ -46,6 +46,10 @@ void PySideInstaller::checkPySideInstallation(const FilePath &python,
TextEditor::TextDocument *document)
{
document->infoBar()->removeInfo(installPySideInfoBarId);
if (QPointer<QFutureWatcher<bool>> watcher = instance()->m_futureWatchers.value(document))
watcher->cancel();
if (!python.exists())
return;
const QString pySide = importedPySide(document->plainText());
if (pySide == "PySide2" || pySide == "PySide6")
instance()->runPySideChecker(python, pySide, document);
@@ -186,10 +190,8 @@ void PySideInstaller::runPySideChecker(const FilePath &python,
// cancel and delete watcher after a 10 second timeout
QTimer::singleShot(10000, this, [watcher]() {
if (watcher) {
if (watcher)
watcher->cancel();
watcher->deleteLater();
}
});
connect(watcher,
&CheckPySideWatcher::resultReadyAt,
@@ -197,9 +199,13 @@ void PySideInstaller::runPySideChecker(const FilePath &python,
[=, document = QPointer<TextEditor::TextDocument>(document)]() {
if (watcher->result())
handlePySideMissing(python, pySide, document);
watcher->deleteLater();
});
connect(watcher, &CheckPySideWatcher::finished, watcher, &CheckPySideWatcher::deleteLater);
connect(watcher, &CheckPySideWatcher::finished, this, [this, document]{
m_futureWatchers.remove(document);
});
watcher->setFuture(Utils::asyncRun(&missingPySideInstallation, python, pySide));
m_futureWatchers[document] = watcher;
}
} // Python::Internal

View File

@@ -6,6 +6,8 @@
#include <utils/filepath.h>
#include <QCoreApplication>
#include <QFutureWatcher>
#include <QPointer>
#include <QTextDocument>
namespace TextEditor { class TextDocument; }
@@ -13,6 +15,13 @@ namespace ProjectExplorer { class RunConfiguration; }
namespace Python::Internal {
class PySideTools
{
public:
Utils::FilePath pySideProjectPath;
Utils::FilePath pySideUicPath;
};
class PySideInstaller : public QObject
{
Q_OBJECT
@@ -41,6 +50,7 @@ private:
static QString importedPySide(const QString &text);
QHash<Utils::FilePath, QList<TextEditor::TextDocument *>> m_infoBarEntries;
QHash<TextEditor::TextDocument *, QPointer<QFutureWatcher<bool>>> m_futureWatchers;
};
} // Python::Internal

View File

@@ -1,108 +0,0 @@
// 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 "pysidebuildconfiguration.h"
#include "pythonconstants.h"
#include "pythonproject.h"
#include "pythontr.h"
#include <projectexplorer/buildinfo.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/environmentaspect.h>
#include <projectexplorer/processparameters.h>
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/target.h>
#include <utils/commandline.h>
#include <utils/process.h>
using namespace ProjectExplorer;
using namespace Utils;
namespace Python::Internal {
const char pySideBuildStep[] = "Python.PysideBuildStep";
PySideBuildStepFactory::PySideBuildStepFactory()
{
registerStep<PySideBuildStep>(pySideBuildStep);
setSupportedProjectType(PythonProjectId);
setDisplayName(Tr::tr("Run PySide6 project tool"));
setFlags(BuildStep::UniqueStep);
}
PySideBuildStep::PySideBuildStep(BuildStepList *bsl, Id id)
: AbstractProcessStep(bsl, id)
{
m_pysideProject.setSettingsKey("Python.PySideProjectTool");
m_pysideProject.setLabelText(Tr::tr("PySide project tool:"));
m_pysideProject.setToolTip(Tr::tr("Enter location of PySide project tool."));
m_pysideProject.setExpectedKind(PathChooser::Command);
m_pysideProject.setHistoryCompleter("Python.PySideProjectTool.History");
const FilePath pySideProjectPath = FilePath("pyside6-project").searchInPath();
if (pySideProjectPath.isExecutableFile())
m_pysideProject.setValue(pySideProjectPath);
setCommandLineProvider([this] { return CommandLine(m_pysideProject(), {"build"}); });
setWorkingDirectoryProvider([this] {
return m_pysideProject().withNewMappedPath(project()->projectDirectory()); // FIXME: new path needed?
});
setEnvironmentModifier([this](Environment &env) {
env.prependOrSetPath(m_pysideProject().parentDir());
});
}
void PySideBuildStep::updatePySideProjectPath(const FilePath &pySideProjectPath)
{
m_pysideProject.setValue(pySideProjectPath);
}
Tasking::GroupItem PySideBuildStep::runRecipe()
{
using namespace Tasking;
const auto onSetup = [this] {
if (!processParameters()->effectiveCommand().isExecutableFile())
return SetupResult::StopWithSuccess;
return SetupResult::Continue;
};
return Group { onGroupSetup(onSetup), defaultProcessTask() };
}
// PySideBuildConfiguration
class PySideBuildConfiguration : public BuildConfiguration
{
public:
PySideBuildConfiguration(Target *target, Id id)
: BuildConfiguration(target, id)
{
setConfigWidgetDisplayName(Tr::tr("General"));
setInitializer([this](const BuildInfo &) {
buildSteps()->appendStep(pySideBuildStep);
updateCacheAndEmitEnvironmentChanged();
});
updateCacheAndEmitEnvironmentChanged();
}
};
PySideBuildConfigurationFactory::PySideBuildConfigurationFactory()
{
registerBuildConfiguration<PySideBuildConfiguration>("Python.PySideBuildConfiguration");
setSupportedProjectType(PythonProjectId);
setSupportedProjectMimeTypeName(Constants::C_PY_MIMETYPE);
setBuildGenerator([](const Kit *, const FilePath &projectPath, bool) {
BuildInfo info;
info.displayName = "build";
info.typeName = "build";
info.buildDirectory = projectPath.parentDir();
return QList<BuildInfo>{info};
});
}
} // Python::Internal

View File

@@ -1,37 +0,0 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <projectexplorer/abstractprocessstep.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildstep.h>
namespace Python::Internal {
class PySideBuildStep : public ProjectExplorer::AbstractProcessStep
{
Q_OBJECT
public:
PySideBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
void updatePySideProjectPath(const Utils::FilePath &pySideProjectPath);
private:
Tasking::GroupItem runRecipe() final;
Utils::FilePathAspect m_pysideProject{this};
};
class PySideBuildStepFactory : public ProjectExplorer::BuildStepFactory
{
public:
PySideBuildStepFactory();
};
class PySideBuildConfigurationFactory : public ProjectExplorer::BuildConfigurationFactory
{
public:
PySideBuildConfigurationFactory();
};
} // Python::Internal

View File

@@ -22,8 +22,8 @@ QtcPlugin {
"pipsupport.h",
"pyside.cpp",
"pyside.h",
"pysidebuildconfiguration.cpp",
"pysidebuildconfiguration.h",
"pythonbuildconfiguration.cpp",
"pythonbuildconfiguration.h",
"pysideuicextracompiler.cpp",
"pysideuicextracompiler.h",
"python.qrc",

View File

@@ -0,0 +1,415 @@
// 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 "pythonbuildconfiguration.h"
#include "pipsupport.h"
#include "pyside.h"
#include "pysideuicextracompiler.h"
#include "pythonconstants.h"
#include "pythoneditor.h"
#include "pythonkitaspect.h"
#include "pythonlanguageclient.h"
#include "pythonproject.h"
#include "pythonsettings.h"
#include "pythontr.h"
#include "pythonutils.h"
#include <coreplugin/editormanager/documentmodel.h>
#include <languageclient/languageclientmanager.h>
#include <projectexplorer/buildinfo.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/environmentaspect.h>
#include <projectexplorer/namedwidget.h>
#include <projectexplorer/processparameters.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/target.h>
#include <extensionsystem/pluginmanager.h>
#include <utils/algorithm.h>
#include <utils/commandline.h>
#include <utils/detailswidget.h>
#include <utils/futuresynchronizer.h>
#include <utils/layoutbuilder.h>
#include <utils/process.h>
using namespace ProjectExplorer;
using namespace Utils;
namespace Python::Internal {
PySideBuildStepFactory::PySideBuildStepFactory()
{
registerStep<PySideBuildStep>(PySideBuildStep::id());
setSupportedProjectType(PythonProjectId);
setDisplayName(Tr::tr("Run PySide6 project tool"));
setFlags(BuildStep::UniqueStep);
}
PySideBuildStep::PySideBuildStep(BuildStepList *bsl, Id id)
: AbstractProcessStep(bsl, id)
{
m_pysideProject.setSettingsKey("Python.PySideProjectTool");
m_pysideProject.setLabelText(Tr::tr("PySide project tool:"));
m_pysideProject.setToolTip(Tr::tr("Enter location of PySide project tool."));
m_pysideProject.setExpectedKind(PathChooser::Command);
m_pysideProject.setHistoryCompleter("Python.PySideProjectTool.History");
m_pysideProject.setReadOnly(true);
m_pysideUic.setSettingsKey("Python.PySideUic");
m_pysideUic.setLabelText(Tr::tr("PySide uic tool:"));
m_pysideUic.setToolTip(Tr::tr("Enter location of PySide uic tool."));
m_pysideUic.setExpectedKind(PathChooser::Command);
m_pysideUic.setHistoryCompleter("Python.PySideUic.History");
m_pysideUic.setReadOnly(true);
setCommandLineProvider([this] { return CommandLine(m_pysideProject(), {"build"}); });
setWorkingDirectoryProvider([this] {
return m_pysideProject().withNewMappedPath(project()->projectDirectory()); // FIXME: new path needed?
});
setEnvironmentModifier([this](Environment &env) {
env.prependOrSetPath(m_pysideProject().parentDir());
});
connect(target(), &Target::buildSystemUpdated, this, &PySideBuildStep::updateExtraCompilers);
connect(&m_pysideUic, &BaseAspect::changed, this, &PySideBuildStep::updateExtraCompilers);
}
PySideBuildStep::~PySideBuildStep()
{
qDeleteAll(m_extraCompilers);
}
void PySideBuildStep::checkForPySide(const FilePath &python)
{
PySideTools tools;
if (python.isEmpty() || !python.isExecutableFile()) {
m_pysideProject.setValue(FilePath());
m_pysideUic.setValue(FilePath());
return;
}
const FilePath dir = python.parentDir();
tools.pySideProjectPath = dir.pathAppended("pyside6-project").withExecutableSuffix();
tools.pySideUicPath = dir.pathAppended("pyside6-uic").withExecutableSuffix();
if (tools.pySideProjectPath.isExecutableFile() && tools.pySideUicPath.isExecutableFile()) {
m_pysideProject.setValue(tools.pySideProjectPath.toUserOutput());
m_pysideUic.setValue(tools.pySideUicPath.toUserOutput());
} else {
checkForPySide(python, "PySide6-Essentials");
}
}
void PySideBuildStep::checkForPySide(const FilePath &python, const QString &pySidePackageName)
{
const PipPackage package(pySidePackageName);
QObject::disconnect(m_watcherConnection);
m_watcher.reset(new QFutureWatcher<PipPackageInfo>());
m_watcherConnection = QObject::connect(m_watcher.get(), &QFutureWatcherBase::finished, this, [=] {
handlePySidePackageInfo(m_watcher->result(), python, pySidePackageName);
});
const auto future = Pip::instance(python)->info(package);
m_watcher->setFuture(future);
ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(future);
}
void PySideBuildStep::handlePySidePackageInfo(const PipPackageInfo &pySideInfo,
const FilePath &python,
const QString &requestedPackageName)
{
const auto findPythonTools = [](const FilePaths &files,
const FilePath &location,
const FilePath &python) -> PySideTools {
PySideTools result;
const QString pySide6ProjectName
= OsSpecificAspects::withExecutableSuffix(python.osType(), "pyside6-project");
const QString pySide6UicName
= OsSpecificAspects::withExecutableSuffix(python.osType(), "pyside6-uic");
for (const FilePath &file : files) {
if (file.fileName() == pySide6ProjectName) {
result.pySideProjectPath = python.withNewMappedPath(location.resolvePath(file));
result.pySideProjectPath = result.pySideProjectPath.cleanPath();
if (!result.pySideUicPath.isEmpty())
return result;
} else if (file.fileName() == pySide6UicName) {
result.pySideUicPath = python.withNewMappedPath(location.resolvePath(file));
result.pySideUicPath = result.pySideUicPath.cleanPath();
if (!result.pySideProjectPath.isEmpty())
return result;
}
}
return {};
};
PySideTools tools = findPythonTools(pySideInfo.files, pySideInfo.location, python);
if (!tools.pySideProjectPath.isExecutableFile() && requestedPackageName != "PySide6") {
checkForPySide(python, "PySide6");
return;
}
m_pysideProject.setValue(tools.pySideProjectPath.toUserOutput());
m_pysideUic.setValue(tools.pySideUicPath.toUserOutput());
}
Tasking::GroupItem PySideBuildStep::runRecipe()
{
using namespace Tasking;
const auto onSetup = [this] {
if (!processParameters()->effectiveCommand().isExecutableFile())
return SetupResult::StopWithSuccess;
return SetupResult::Continue;
};
return Group { onGroupSetup(onSetup), defaultProcessTask() };
}
void PySideBuildStep::updateExtraCompilers()
{
QList<PySideUicExtraCompiler *> oldCompilers = m_extraCompilers;
m_extraCompilers.clear();
if (m_pysideUic().isExecutableFile()) {
auto uiMatcher = [](const Node *node) {
if (const FileNode *fileNode = node->asFileNode())
return fileNode->fileType() == FileType::Form;
return false;
};
const FilePaths uiFiles = project()->files(uiMatcher);
for (const FilePath &uiFile : uiFiles) {
FilePath generated = uiFile.parentDir();
generated = generated.pathAppended("/ui_" + uiFile.baseName() + ".py");
int index = Utils::indexOf(oldCompilers, [&](PySideUicExtraCompiler *oldCompiler) {
return oldCompiler->pySideUicPath() == m_pysideUic()
&& oldCompiler->project() == project() && oldCompiler->source() == uiFile
&& oldCompiler->targets() == FilePaths{generated};
});
if (index < 0) {
m_extraCompilers << new PySideUicExtraCompiler(m_pysideUic(),
project(),
uiFile,
{generated},
this);
} else {
m_extraCompilers << oldCompilers.takeAt(index);
}
}
}
for (LanguageClient::Client *client : LanguageClient::LanguageClientManager::clients()) {
if (auto pylsClient = qobject_cast<PyLSClient *>(client))
pylsClient->updateExtraCompilers(project(), m_extraCompilers);
}
qDeleteAll(oldCompilers);
}
QList<PySideUicExtraCompiler *> PySideBuildStep::extraCompilers() const
{
return m_extraCompilers;
}
Id PySideBuildStep::id()
{
return Id("Python.PysideBuildStep");
}
class PythonBuildSettingsWidget : public NamedWidget
{
public:
PythonBuildSettingsWidget(PythonBuildConfiguration *bc)
: NamedWidget(Tr::tr("Python"))
{
using namespace Layouting;
m_configureDetailsWidget = new DetailsWidget;
m_configureDetailsWidget->setSummaryText(bc->python().toUserOutput());
if (const std::optional<FilePath> venv = bc->venv()) {
auto details = new QWidget();
Form{Tr::tr("Effective venv:"), venv->toUserOutput(), br}.attachTo(details);
m_configureDetailsWidget->setWidget(details);
} else {
m_configureDetailsWidget->setState(DetailsWidget::OnlySummary);
}
Column{
m_configureDetailsWidget,
noMargin
}.attachTo(this);
}
private:
DetailsWidget *m_configureDetailsWidget;
};
PythonBuildConfiguration::PythonBuildConfiguration(Target *target, const Id &id)
: BuildConfiguration(target, id)
, m_buildSystem(std::make_unique<PythonBuildSystem>(this))
{
setInitializer([this](const BuildInfo &info) { initialize(info); });
updateCacheAndEmitEnvironmentChanged();
connect(PySideInstaller::instance(),
&PySideInstaller::pySideInstalled,
this,
&PythonBuildConfiguration::handlePythonUpdated);
auto update = [this]() {
if (isActive()) {
m_buildSystem->emitBuildSystemUpdated();
const FilePaths files = project()->files(Project::AllFiles);
for (const FilePath &file : files) {
if (auto doc = qobject_cast<PythonDocument *>(
Core::DocumentModel::documentForFilePath(file))) {
doc->updatePython(m_python);
}
}
}
};
connect(target, &Target::activeBuildConfigurationChanged, this, update);
connect(project(), &Project::activeTargetChanged, this, update);
connect(ProjectExplorerPlugin::instance(),
&ProjectExplorerPlugin::fileListChanged,
this,
update);
connect(PythonSettings::instance(),
&PythonSettings::virtualEnvironmentCreated,
this,
&PythonBuildConfiguration::handlePythonUpdated);
}
NamedWidget *PythonBuildConfiguration::createConfigWidget()
{
return new PythonBuildSettingsWidget(this);
}
static QString venvTypeName()
{
static QString name = Tr::tr("New Virtual Environment");
return name;
}
void PythonBuildConfiguration::initialize(const BuildInfo &info)
{
buildSteps()->appendStep(PySideBuildStep::id());
if (info.typeName == venvTypeName()) {
m_venv = info.buildDirectory;
const FilePath venvInterpreterPath = info.buildDirectory.resolvePath(
HostOsInfo::isWindowsHost() ? FilePath::fromUserInput("Scripts/python.exe")
: FilePath::fromUserInput("bin/python"));
updatePython(venvInterpreterPath);
if (info.extraInfo.toMap().value("createVenv", false).toBool()
&& !info.buildDirectory.exists()) {
if (std::optional<Interpreter> python = PythonKitAspect::python(target()->kit()))
PythonSettings::createVirtualEnvironment(python->command, info.buildDirectory);
}
} else {
updateInterpreter(PythonKitAspect::python(target()->kit()));
}
updateCacheAndEmitEnvironmentChanged();
}
void PythonBuildConfiguration::updateInterpreter(const std::optional<Interpreter> &python)
{
updatePython(python ? python->command : FilePath());
}
void PythonBuildConfiguration::updatePython(const FilePath &python)
{
m_python = python;
if (auto buildStep = buildSteps()->firstOfType<PySideBuildStep>())
buildStep->checkForPySide(python);
if (isActive()) {
const FilePaths files = project()->files(Project::AllFiles);
for (const FilePath &file : files) {
if (auto doc = qobject_cast<PythonDocument *>(
Core::DocumentModel::documentForFilePath(file))) {
doc->updatePython(m_python);
}
}
}
m_buildSystem->requestParse();
}
void PythonBuildConfiguration::handlePythonUpdated(const FilePath &python)
{
if (!m_python.isEmpty() && python == m_python)
updatePython(python); // retrigger pyside check
}
static const char pythonKey[] = "python";
static const char venvKey[] = "venv";
void PythonBuildConfiguration::fromMap(const Store &map)
{
BuildConfiguration::fromMap(map);
if (map.contains(venvKey))
m_venv = FilePath::fromSettings(map[venvKey]);
updatePython(FilePath::fromSettings(map[pythonKey]));
}
void PythonBuildConfiguration::toMap(Store &map) const
{
BuildConfiguration::toMap(map);
map[pythonKey] = m_python.toSettings();
if (m_venv)
map[venvKey] = m_venv->toSettings();
}
BuildSystem *PythonBuildConfiguration::buildSystem() const
{
return m_buildSystem.get();
}
FilePath PythonBuildConfiguration::python() const
{
return m_python;
}
std::optional<FilePath> PythonBuildConfiguration::venv() const
{
return m_venv;
}
PythonBuildConfigurationFactory::PythonBuildConfigurationFactory()
{
registerBuildConfiguration<PythonBuildConfiguration>("Python.PySideBuildConfiguration");
setSupportedProjectType(PythonProjectId);
setSupportedProjectMimeTypeName(Constants::C_PY_PROJECT_MIME_TYPE);
setBuildGenerator([](const Kit *k, const FilePath &projectPath, bool forSetup) {
if (std::optional<Interpreter> python = PythonKitAspect::python(k)) {
BuildInfo base;
base.buildDirectory = projectPath.parentDir();
base.displayName = python->name;
base.typeName = Tr::tr("Global Python");
base.showBuildDirConfigWidget = false;
if (isVenvPython(python->command))
return QList<BuildInfo>{base};
base.enabledByDefault = false;
BuildInfo venv;
const FilePath venvBase = projectPath.parentDir() / ".qtcreator"
/ FileUtils::fileSystemFriendlyName(python->name + "venv");
venv.buildDirectory = venvBase;
int i = 2;
while (venv.buildDirectory.exists())
venv.buildDirectory = venvBase.stringAppended('_' + QString::number(i++));
venv.displayName = python->name + Tr::tr(" Virtual Environment");
venv.typeName = venvTypeName();
venv.extraInfo = QVariantMap{{"createVenv", forSetup}};
return QList<BuildInfo>{base, venv};
}
return QList<BuildInfo>{};
});
}
} // Python::Internal

View File

@@ -0,0 +1,86 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "pythonbuildsystem.h"
#include <projectexplorer/abstractprocessstep.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildstep.h>
namespace ProjectExplorer { class Interpreter; }
namespace Python::Internal {
class PipPackageInfo;
class PySideUicExtraCompiler;
class PySideBuildStep : public ProjectExplorer::AbstractProcessStep
{
Q_OBJECT
public:
PySideBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
~PySideBuildStep();
void checkForPySide(const Utils::FilePath &python);
QList<PySideUicExtraCompiler *> extraCompilers() const;
static Utils::Id id();
private:
void checkForPySide(const Utils::FilePath &python, const QString &pySidePackageName);
void handlePySidePackageInfo(const PipPackageInfo &pySideInfo,
const Utils::FilePath &python,
const QString &requestedPackageName);
Tasking::GroupItem runRecipe() final;
void updateExtraCompilers();
std::unique_ptr<QFutureWatcher<PipPackageInfo>> m_watcher;
QMetaObject::Connection m_watcherConnection;
Utils::FilePathAspect m_pysideProject{this};
Utils::FilePathAspect m_pysideUic{this};
QList<PySideUicExtraCompiler *> m_extraCompilers;
};
class PySideBuildStepFactory : public ProjectExplorer::BuildStepFactory
{
public:
PySideBuildStepFactory();
};
class PythonBuildConfiguration : public ProjectExplorer::BuildConfiguration
{
Q_OBJECT
public:
PythonBuildConfiguration(ProjectExplorer::Target *target, const Utils::Id &id);
ProjectExplorer::NamedWidget *createConfigWidget() override;
void fromMap(const Utils::Store &map) override;
void toMap(Utils::Store &map) const override;
ProjectExplorer::BuildSystem *buildSystem() const override;
Utils::FilePath python() const;
std::optional<Utils::FilePath> venv() const;
private:
void initialize(const ProjectExplorer::BuildInfo &info);
void updateInterpreter(const std::optional<ProjectExplorer::Interpreter> &python);
void updatePython(const Utils::FilePath &python);
void handlePythonUpdated(const Utils::FilePath &python);
Utils::FilePath m_python;
std::optional<Utils::FilePath> m_venv;
std::unique_ptr<PythonBuildSystem> m_buildSystem;
};
class PythonBuildConfigurationFactory : public ProjectExplorer::BuildConfigurationFactory
{
public:
PythonBuildConfigurationFactory();
};
} // namespace Python::Internal

View File

@@ -3,7 +3,9 @@
#include "pythonbuildsystem.h"
#include "pythonbuildconfiguration.h"
#include "pythonconstants.h"
#include "pythonkitaspect.h"
#include "pythonproject.h"
#include "pythontr.h"
@@ -116,11 +118,15 @@ static QStringList readImportPathsJson(const FilePath &projectFile, QString *err
return importPaths;
}
PythonBuildSystem::PythonBuildSystem(Target *target)
: BuildSystem(target)
PythonBuildSystem::PythonBuildSystem(PythonBuildConfiguration *buildConfig)
: BuildSystem(buildConfig)
{
connect(target->project(), &Project::projectFileIsDirty, this, [this] { triggerParsing(); });
triggerParsing();
connect(project(),
&Project::projectFileIsDirty,
this,
&PythonBuildSystem::requestDelayedParse);
m_buildConfig = buildConfig;
requestParse();
}
bool PythonBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const
@@ -159,6 +165,12 @@ void PythonBuildSystem::triggerParsing()
auto newRoot = std::make_unique<PythonProjectNode>(projectDirectory());
FilePath python;
if (m_buildConfig)
python = m_buildConfig->python();
else if (auto kitPython = PythonKitAspect::python(kit()))
python = kitPython->command;
const FilePath projectFile = projectFilePath();
const QString displayName = projectFile.relativePathFrom(projectDirectory()).toUserOutput();
newRoot->addNestedNode(
@@ -178,6 +190,7 @@ void PythonBuildSystem::triggerParsing()
bti.targetFilePath = entry.filePath;
bti.projectFilePath = projectFile;
bti.isQtcRunnable = entry.filePath.fileName() == "main.py";
bti.additionalData = QVariantMap{{"python", python.toSettings()}};
appTargets.append(bti);
}
}

View File

@@ -7,10 +7,12 @@
namespace Python::Internal {
class PythonBuildConfiguration;
class PythonBuildSystem : public ProjectExplorer::BuildSystem
{
public:
explicit PythonBuildSystem(ProjectExplorer::Target *target);
explicit PythonBuildSystem(PythonBuildConfiguration *buildConfig);
bool supportsAction(ProjectExplorer::Node *context,
ProjectExplorer::ProjectAction action,
@@ -44,6 +46,7 @@ private:
QList<FileEntry> m_files;
QList<FileEntry> m_qmlImportPaths;
PythonBuildConfiguration *m_buildConfig = nullptr;
};

View File

@@ -32,6 +32,9 @@ const char C_PY_MIMETYPE[] = "text/x-python";
const char C_PY_GUI_MIMETYPE[] = "text/x-python-gui";
const char C_PY3_MIMETYPE[] = "text/x-python3";
const char C_PY_MIME_ICON[] = "text-x-python";
const char C_PY_PROJECT_MIME_TYPE[] = "text/x-python-project";
const char C_PY_PROJECT_MIME_TYPE_LEGACY[] = "text/x-pyqt-project";
} // namespace Constants
} // namespace Python

View File

@@ -4,10 +4,13 @@
#include "pythoneditor.h"
#include "pyside.h"
#include "pythonbuildconfiguration.h"
#include "pythonconstants.h"
#include "pythonhighlighter.h"
#include "pythonindenter.h"
#include "pythonkitaspect.h"
#include "pythonlanguageclient.h"
#include "pythonplugin.h"
#include "pythonsettings.h"
#include "pythontr.h"
#include "pythonutils.h"
@@ -17,12 +20,14 @@
#include <coreplugin/coreplugintr.h>
#include <coreplugin/icore.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildinfo.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditoractionhandler.h>
#include <utils/stylehelper.h>
@@ -73,37 +78,6 @@ static void registerReplAction(QObject *parent)
Constants::PYTHON_OPEN_REPL_IMPORT_TOPLEVEL);
}
class PythonDocument : public TextDocument
{
Q_OBJECT
public:
PythonDocument() : TextDocument(Constants::C_PYTHONEDITOR_ID)
{
connect(PythonSettings::instance(),
&PythonSettings::pylsEnabledChanged,
this,
[this](const bool enabled) {
if (!enabled)
return;
const FilePath &python = detectPython(filePath());
if (python.exists())
PyLSConfigureAssistant::openDocumentWithPython(python, this);
});
connect(this, &PythonDocument::openFinishedSuccessfully,
this, &PythonDocument::checkForPyls);
}
void checkForPyls()
{
const FilePath &python = detectPython(filePath());
if (!python.exists())
return;
PyLSConfigureAssistant::openDocumentWithPython(python, this);
PySideInstaller::checkPySideInstallation(python, this);
}
};
class PythonEditorWidget : public TextEditorWidget
{
public:
@@ -111,12 +85,10 @@ public:
protected:
void finalizeInitialization() override;
void setUserDefinedPython(const Interpreter &interpreter);
void updateInterpretersSelector();
private:
QToolButton *m_interpreters = nullptr;
QList<QMetaObject::Connection> m_projectConnections;
};
PythonEditorWidget::PythonEditorWidget(QWidget *parent) : TextEditorWidget(parent)
@@ -142,31 +114,15 @@ void PythonEditorWidget::finalizeInitialization()
{
connect(textDocument(), &TextDocument::filePathChanged,
this, &PythonEditorWidget::updateInterpretersSelector);
connect(PythonSettings::instance(), &PythonSettings::interpretersChanged,
this, &PythonEditorWidget::updateInterpretersSelector);
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::fileListChanged,
this, &PythonEditorWidget::updateInterpretersSelector);
}
void PythonEditorWidget::setUserDefinedPython(const Interpreter &interpreter)
{
const auto pythonDocument = qobject_cast<PythonDocument *>(textDocument());
QTC_ASSERT(pythonDocument, return);
FilePath documentPath = pythonDocument->filePath();
QTC_ASSERT(!documentPath.isEmpty(), return);
if (Project *project = ProjectManager::projectForFile(documentPath)) {
if (Target *target = project->activeTarget()) {
if (RunConfiguration *rc = target->activeRunConfiguration()) {
if (auto interpretersAspect= rc->aspect<InterpreterAspect>()) {
interpretersAspect->setCurrentInterpreter(interpreter);
return;
}
}
}
connect(KitManager::instance(), &KitManager::kitsChanged,
this, &PythonEditorWidget::updateInterpretersSelector);
auto pythonDocument = qobject_cast<PythonDocument *>(textDocument());
if (QTC_GUARD(pythonDocument)) {
connect(pythonDocument, &PythonDocument::pythonUpdated,
this, &PythonEditorWidget::updateInterpretersSelector);
}
definePythonForDocument(textDocument()->filePath(), interpreter.command);
updateInterpretersSelector();
pythonDocument->checkForPyls();
}
void PythonEditorWidget::updateInterpretersSelector()
@@ -183,30 +139,6 @@ void PythonEditorWidget::updateInterpretersSelector()
QMenu *menu = m_interpreters->menu();
QTC_ASSERT(menu, return);
menu->clear();
for (const QMetaObject::Connection &connection : m_projectConnections)
disconnect(connection);
m_projectConnections.clear();
const FilePath documentPath = textDocument()->filePath();
if (Project *project = ProjectManager::projectForFile(documentPath)) {
m_projectConnections << connect(project,
&Project::activeTargetChanged,
this,
&PythonEditorWidget::updateInterpretersSelector);
if (Target *target = project->activeTarget()) {
m_projectConnections << connect(target,
&Target::activeRunConfigurationChanged,
this,
&PythonEditorWidget::updateInterpretersSelector);
if (RunConfiguration *rc = target->activeRunConfiguration()) {
if (auto interpreterAspect = rc->aspect<InterpreterAspect>()) {
m_projectConnections << connect(interpreterAspect,
&InterpreterAspect::changed,
this,
&PythonEditorWidget::updateInterpretersSelector);
}
}
}
}
auto setButtonText = [this](QString text) {
constexpr int maxTextLength = 25;
@@ -215,50 +147,130 @@ void PythonEditorWidget::updateInterpretersSelector()
m_interpreters->setText(text);
};
const FilePath currentInterpreterPath = detectPython(textDocument()->filePath());
const QList<Interpreter> configuredInterpreters = PythonSettings::interpreters();
auto interpretersGroup = new QActionGroup(menu);
interpretersGroup->setExclusive(true);
std::optional<Interpreter> currentInterpreter;
for (const Interpreter &interpreter : configuredInterpreters) {
QAction *action = interpretersGroup->addAction(interpreter.name);
connect(action, &QAction::triggered, this, [this, interpreter]() {
setUserDefinedPython(interpreter);
});
action->setCheckable(true);
if (!currentInterpreter && interpreter.command == currentInterpreterPath) {
currentInterpreter = interpreter;
action->setChecked(true);
setButtonText(interpreter.name);
m_interpreters->setToolTip(interpreter.command.toUserOutput());
const FilePath documentPath = textDocument()->filePath();
Project *project = Utils::findOrDefault(ProjectManager::projects(),
[documentPath](Project *project) {
return project->mimeType()
== Constants::C_PY_PROJECT_MIME_TYPE
&& project->isKnownFile(documentPath);
});
if (project) {
auto interpretersGroup = new QActionGroup(menu);
interpretersGroup->setExclusive(true);
for (Target *target : project->targets()) {
QTC_ASSERT(target, continue);
for (auto buildConfiguration : target->buildConfigurations()) {
QTC_ASSERT(buildConfiguration, continue);
const QString name = buildConfiguration->displayName();
QAction *action = interpretersGroup->addAction(buildConfiguration->displayName());
action->setCheckable(true);
if (target == project->activeTarget()
&& target->activeBuildConfiguration() == buildConfiguration) {
action->setChecked(true);
setButtonText(name);
if (auto pbc = qobject_cast<PythonBuildConfiguration *>(buildConfiguration))
m_interpreters->setToolTip(pbc->python().toUserOutput());
}
connect(action,
&QAction::triggered,
project,
[project, target, buildConfiguration]() {
target->setActiveBuildConfiguration(buildConfiguration,
SetActive::NoCascade);
if (target != project->activeTarget())
project->setActiveTarget(target, SetActive::NoCascade);
});
}
}
}
menu->addActions(interpretersGroup->actions());
if (!currentInterpreter) {
if (currentInterpreterPath.exists())
setButtonText(currentInterpreterPath.toUserOutput());
else
setButtonText(Tr::tr("No Python Selected"));
}
if (!interpretersGroup->actions().isEmpty()) {
menu->addActions(interpretersGroup->actions());
QMenu *addMenu = menu->addMenu("Add new Interpreter");
for (auto kit : KitManager::kits()) {
if (std::optional<Interpreter> python = PythonKitAspect::python(kit)) {
if (auto buildConficurationFactory
= ProjectExplorer::BuildConfigurationFactory::find(kit,
project->projectFilePath())) {
const QString name = kit->displayName();
QMenu *interpreterAddMenu = addMenu->addMenu(name);
const QList<BuildInfo> buildInfos
= buildConficurationFactory->allAvailableSetups(kit,
project->projectFilePath());
for (const BuildInfo &buildInfo : buildInfos) {
QAction *action = interpreterAddMenu->addAction(buildInfo.displayName);
connect(action, &QAction::triggered, project, [project, buildInfo]() {
if (BuildConfiguration *buildConfig = project->setup(buildInfo)) {
buildConfig->target()
->setActiveBuildConfiguration(buildConfig, SetActive::NoCascade);
project->setActiveTarget(buildConfig->target(),
SetActive::NoCascade);
}
});
}
}
}
}
menu->addSeparator();
auto venvAction = menu->addAction(Tr::tr("Create Virtual Environment"));
connect(venvAction,
&QAction::triggered,
this,
[self = QPointer<PythonEditorWidget>(this), currentInterpreter]() {
if (!currentInterpreter)
return;
auto callback = [self](const std::optional<Interpreter> &venvInterpreter) {
if (self && venvInterpreter)
self->setUserDefinedPython(*venvInterpreter);
};
PythonSettings::createVirtualEnvironmentInteractive(self->textDocument()
->filePath()
.parentDir(),
*currentInterpreter,
callback);
});
} else {
auto setUserDefinedPython = [this](const FilePath &interpreter){
const auto pythonDocument = qobject_cast<PythonDocument *>(textDocument());
QTC_ASSERT(pythonDocument, return);
const FilePath documentPath = pythonDocument->filePath();
QTC_ASSERT(!documentPath.isEmpty(), return);
definePythonForDocument(documentPath, interpreter);
updateInterpretersSelector();
pythonDocument->updateCurrentPython();
};
const FilePath currentInterpreterPath = detectPython(documentPath);
const QList<Interpreter> configuredInterpreters = PythonSettings::interpreters();
auto interpretersGroup = new QActionGroup(menu);
interpretersGroup->setExclusive(true);
std::optional<Interpreter> currentInterpreter;
for (const Interpreter &interpreter : configuredInterpreters) {
QAction *action = interpretersGroup->addAction(interpreter.name);
connect(action, &QAction::triggered, this, [interpreter, setUserDefinedPython]() {
setUserDefinedPython(interpreter.command);
});
action->setCheckable(true);
if (!currentInterpreter && interpreter.command == currentInterpreterPath) {
currentInterpreter = interpreter;
action->setChecked(true);
setButtonText(interpreter.name);
m_interpreters->setToolTip(interpreter.command.toUserOutput());
}
}
menu->addActions(interpretersGroup->actions());
if (!currentInterpreter) {
if (currentInterpreterPath.exists())
setButtonText(currentInterpreterPath.toUserOutput());
else
setButtonText(Tr::tr("No Python Selected"));
}
if (!interpretersGroup->actions().isEmpty()) {
menu->addSeparator();
auto venvAction = menu->addAction(Tr::tr("Create Virtual Environment"));
connect(venvAction,
&QAction::triggered,
this,
[self = QPointer<PythonEditorWidget>(this),
currentInterpreter,
setUserDefinedPython]() {
if (!currentInterpreter)
return;
auto callback = [self, setUserDefinedPython](
const std::optional<FilePath> &venvInterpreter) {
if (self && venvInterpreter)
setUserDefinedPython(*venvInterpreter);
};
PythonSettings::createVirtualEnvironmentInteractive(self->textDocument()
->filePath()
.parentDir(),
*currentInterpreter,
callback);
});
}
}
auto settingsAction = menu->addAction(Tr::tr("Manage Python Interpreters"));
connect(settingsAction, &QAction::triggered, this, []() {
@@ -288,6 +300,35 @@ PythonEditorFactory::PythonEditorFactory()
setCodeFoldingSupported(true);
}
} // Python::Internal
PythonDocument::PythonDocument()
: TextDocument(Constants::C_PYTHONEDITOR_ID)
{
connect(PythonSettings::instance(),
&PythonSettings::pylsEnabledChanged,
this,
[this](const bool enabled) {
if (!enabled)
return;
const FilePath &python = detectPython(filePath());
if (python.exists())
PyLSConfigureAssistant::openDocumentWithPython(python, this);
});
connect(this,
&PythonDocument::openFinishedSuccessfully,
this,
&PythonDocument::updateCurrentPython);
}
#include "pythoneditor.moc"
void PythonDocument::updateCurrentPython()
{
updatePython(detectPython(filePath()));
}
void PythonDocument::updatePython(const FilePath &python)
{
PyLSConfigureAssistant::openDocumentWithPython(python, this);
PySideInstaller::checkPySideInstallation(python, this);
emit pythonUpdated(python);
}
} // Python::Internal

View File

@@ -3,6 +3,7 @@
#pragma once
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
namespace Python::Internal {
@@ -15,4 +16,17 @@ private:
QObject m_guard;
};
class PythonDocument : public TextEditor::TextDocument
{
Q_OBJECT
public:
PythonDocument();
void updateCurrentPython();
void updatePython(const Utils::FilePath &python);
signals:
void pythonUpdated(const Utils::FilePath &python);
};
} // Python::Internal

View File

@@ -119,6 +119,13 @@ public:
}
KitAspect *createKitAspect(Kit *k) const override { return new PythonKitAspectImpl(k, this); }
QSet<Id> availableFeatures(const Kit *k) const override
{
if (k->isAspectRelevant(PythonKitAspect::id()) && PythonKitAspect::python(k))
return {PythonKitAspect::id()};
return {};
}
};
std::optional<Interpreter> PythonKitAspect::python(const Kit *kit)

View File

@@ -4,6 +4,7 @@
#include "pythonlanguageclient.h"
#include "pipsupport.h"
#include "pythonbuildconfiguration.h"
#include "pysideuicextracompiler.h"
#include "pythonconstants.h"
#include "pythonplugin.h"
@@ -22,6 +23,8 @@
#include <languageserverprotocol/textsynchronization.h>
#include <languageserverprotocol/workspace.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/extracompiler.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
@@ -207,12 +210,13 @@ void PyLSClient::openDocument(TextEditor::TextDocument *document)
const FilePath documentPath = document->filePath();
if (PythonProject *project = pythonProjectForFile(documentPath)) {
if (Target *target = project->activeTarget()) {
if (RunConfiguration *rc = target->activeRunConfiguration())
if (auto aspect = rc->aspect<InterpreterAspect>()) {
updateExtraCompilers(project,
static_cast<PythonInterpreterAspect *>(aspect)
->extraCompilers());
if (BuildConfiguration *buildConfig = target->activeBuildConfiguration()) {
if (BuildStepList *buildSteps = buildConfig->buildSteps()) {
BuildStep *buildStep = buildSteps->firstStepWithId(PySideBuildStep::id());
if (auto *pythonBuildStep = qobject_cast<PySideBuildStep *>(buildStep))
updateExtraCompilers(project, pythonBuildStep->extraCompilers());
}
}
}
} else if (isSupportedDocument(document)) {
const FilePath workspacePath = documentPath.parentDir();
@@ -321,7 +325,7 @@ void PyLSConfigureAssistant::openDocumentWithPython(const FilePath &python,
TextEditor::TextDocument *document)
{
instance()->resetEditorInfoBar(document);
if (!PythonSettings::pylsEnabled())
if (!PythonSettings::pylsEnabled() || !python.exists())
return;
if (auto client = pythonClients().value(python)) {
@@ -347,9 +351,13 @@ void PyLSConfigureAssistant::openDocumentWithPython(const FilePath &python,
if (!document || !watcher)
return;
instance()->handlePyLSState(python, watcher->result(), document);
watcher->deleteLater();
});
connect(watcher, &CheckPylsWatcher::finished, watcher, &CheckPylsWatcher::deleteLater);
connect(watcher, &CheckPylsWatcher::finished, instance(), [document](){
instance()->m_runningChecks.remove(document);
});
watcher->setFuture(Utils::asyncRun(&checkPythonLanguageServer, python));
instance()->m_runningChecks[document] = watcher;
}
void PyLSConfigureAssistant::handlePyLSState(const FilePath &python,
@@ -383,6 +391,8 @@ void PyLSConfigureAssistant::resetEditorInfoBar(TextEditor::TextDocument *docume
for (QList<TextEditor::TextDocument *> &documents : m_infoBarEntries)
documents.removeAll(document);
document->infoBar()->removeInfo(installPylsInfoBarId);
if (auto watcher = m_runningChecks.value(document))
watcher->cancel();
}
PyLSConfigureAssistant::PyLSConfigureAssistant(QObject *parent)

View File

@@ -67,6 +67,8 @@ private:
QPointer<TextEditor::TextDocument> document);
QHash<Utils::FilePath, QList<TextEditor::TextDocument *>> m_infoBarEntries;
QHash<TextEditor::TextDocument *, QPointer<QFutureWatcher<PythonLanguageServerState>>>
m_runningChecks;
};
} // Python::Internal

View File

@@ -3,7 +3,8 @@
#include "pythonplugin.h"
#include "pysidebuildconfiguration.h"
#include "pythonbuildconfiguration.h"
#include "pythonconstants.h"
#include "pythoneditor.h"
#include "pythonkitaspect.h"
#include "pythonproject.h"
@@ -36,7 +37,7 @@ public:
PythonOutputFormatterFactory outputFormatterFactory;
PythonRunConfigurationFactory runConfigFactory;
PySideBuildStepFactory buildStepFactory;
PySideBuildConfigurationFactory buildConfigFactory;
PythonBuildConfigurationFactory buildConfigFactory;
SimpleTargetRunnerFactory runWorkerFactory{{runConfigFactory.runConfigurationId()}};
PythonSettings settings;
PythonWizardPageFactory pythonWizardPageFactory;
@@ -65,8 +66,8 @@ void PythonPlugin::initialize()
KitManager::setIrrelevantAspects(KitManager::irrelevantAspects()
+ QSet<Id>{PythonKitAspect::id()});
ProjectManager::registerProjectType<PythonProject>(PythonMimeType);
ProjectManager::registerProjectType<PythonProject>(PythonMimeTypeLegacy);
ProjectManager::registerProjectType<PythonProject>(Constants::C_PY_PROJECT_MIME_TYPE);
ProjectManager::registerProjectType<PythonProject>(Constants::C_PY_PROJECT_MIME_TYPE_LEGACY);
}
void PythonPlugin::extensionsInitialized()

View File

@@ -7,6 +7,8 @@
namespace Python::Internal {
class PythonBuildConfigurationFactory;
class PythonPlugin final : public ExtensionSystem::IPlugin
{
Q_OBJECT

View File

@@ -9,6 +9,7 @@
#include <coreplugin/icontext.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
using namespace Core;
using namespace ProjectExplorer;
@@ -17,16 +18,13 @@ using namespace Utils;
namespace Python::Internal {
PythonProject::PythonProject(const FilePath &fileName)
: Project(Constants::C_PY_MIMETYPE, fileName)
: Project(Constants::C_PY_PROJECT_MIME_TYPE, fileName)
{
setId(PythonProjectId);
setProjectLanguages(Context(ProjectExplorer::Constants::PYTHON_LANGUAGE_ID));
setDisplayName(fileName.completeBaseName());
setBuildSystemCreator([](Target *t) { return new PythonBuildSystem(t); });
}
Project::RestoreResult PythonProject::fromMap(const Store &map, QString *errorMessage)
{
Project::RestoreResult res = Project::fromMap(map, errorMessage);

View File

@@ -10,8 +10,6 @@ namespace Utils { class FilePath; }
namespace Python::Internal {
const char PythonMimeType[] = "text/x-python-project";
const char PythonMimeTypeLegacy[] = "text/x-pyqt-project";
const char PythonProjectId[] = "PythonProject";
const char PythonErrorTaskCategory[] = "Task.Category.Python";

View File

@@ -3,24 +3,21 @@
#include "pythonrunconfiguration.h"
#include "pipsupport.h"
#include "pyside.h"
#include "pysidebuildconfiguration.h"
#include "pysideuicextracompiler.h"
#include "pythonbuildconfiguration.h"
#include "pythonconstants.h"
#include "pythoneditor.h"
#include "pythonkitaspect.h"
#include "pythonlanguageclient.h"
#include "pythonproject.h"
#include "pythonsettings.h"
#include "pythontr.h"
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <extensionsystem/pluginmanager.h>
#include <languageclient/languageclientmanager.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/devicesupport/idevice.h>
@@ -36,6 +33,7 @@
#include <utils/futuresynchronizer.h>
#include <utils/layoutbuilder.h>
#include <utils/outputformatter.h>
#include <utils/qtcassert.h>
#include <utils/theme/theme.h>
#include <QComboBox>
@@ -118,229 +116,6 @@ private:
bool m_inTraceBack;
};
////////////////////////////////////////////////////////////////
class PythonInterpreterAspectPrivate : public QObject
{
public:
PythonInterpreterAspectPrivate(PythonInterpreterAspect *parent, RunConfiguration *rc)
: q(parent), rc(rc)
{
connect(q, &InterpreterAspect::changed,
this, &PythonInterpreterAspectPrivate::currentInterpreterChanged);
connect(PySideInstaller::instance(), &PySideInstaller::pySideInstalled, this,
[this](const FilePath &python) {
if (python == q->currentInterpreter().command)
checkForPySide(python);
}
);
connect(rc->target(), &Target::buildSystemUpdated,
this, &PythonInterpreterAspectPrivate::updateExtraCompilers);
}
~PythonInterpreterAspectPrivate() { qDeleteAll(m_extraCompilers); }
void checkForPySide(const FilePath &python);
void checkForPySide(const FilePath &python, const QString &pySidePackageName);
void handlePySidePackageInfo(const PipPackageInfo &pySideInfo,
const FilePath &python,
const QString &requestedPackageName);
void updateExtraCompilers();
void currentInterpreterChanged();
struct PySideTools
{
FilePath pySideProjectPath;
FilePath pySideUicPath;
};
void updateTools(const PySideTools &tools);
FilePath m_pySideUicPath;
PythonInterpreterAspect *q;
RunConfiguration *rc;
QList<PySideUicExtraCompiler *> m_extraCompilers;
QFutureWatcher<PipPackageInfo> *m_watcher = nullptr;
QMetaObject::Connection m_watcherConnection;
};
PythonInterpreterAspect::PythonInterpreterAspect(AspectContainer *container, RunConfiguration *rc)
: InterpreterAspect(container), d(new PythonInterpreterAspectPrivate(this, rc))
{
setSettingsKey("PythonEditor.RunConfiguation.Interpreter");
setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID);
updateInterpreters(PythonSettings::interpreters());
const QList<Interpreter> interpreters = PythonSettings::detectPythonVenvs(
rc->project()->projectDirectory());
Interpreter defaultInterpreter = interpreters.isEmpty() ? PythonSettings::defaultInterpreter()
: interpreters.first();
if (!defaultInterpreter.command.isExecutableFile())
defaultInterpreter = PythonSettings::interpreters().value(0);
if (defaultInterpreter.command.isExecutableFile()) {
const IDeviceConstPtr device = DeviceKitAspect::device(rc->kit());
if (device && !device->handlesFile(defaultInterpreter.command)) {
defaultInterpreter = Utils::findOr(PythonSettings::interpreters(),
defaultInterpreter,
[device](const Interpreter &interpreter) {
return device->handlesFile(interpreter.command);
});
}
}
setDefaultInterpreter(defaultInterpreter);
connect(PythonSettings::instance(), &PythonSettings::interpretersChanged,
this, &InterpreterAspect::updateInterpreters);
}
PythonInterpreterAspect::~PythonInterpreterAspect()
{
delete d;
}
void PythonInterpreterAspectPrivate::checkForPySide(const FilePath &python)
{
PySideTools tools;
const FilePath dir = python.parentDir();
tools.pySideProjectPath = dir.pathAppended("pyside6-project").withExecutableSuffix();
tools.pySideUicPath = dir.pathAppended("pyside6-uic").withExecutableSuffix();
if (tools.pySideProjectPath.isExecutableFile() && tools.pySideUicPath.isExecutableFile())
updateTools(tools);
else
checkForPySide(python, "PySide6-Essentials");
}
void PythonInterpreterAspectPrivate::checkForPySide(const FilePath &python,
const QString &pySidePackageName)
{
const PipPackage package(pySidePackageName);
QObject::disconnect(m_watcherConnection);
delete m_watcher;
m_watcher = new QFutureWatcher<PipPackageInfo>(this);
m_watcherConnection = QObject::connect(m_watcher, &QFutureWatcherBase::finished, q, [=] {
handlePySidePackageInfo(m_watcher->result(), python, pySidePackageName);
});
const auto future = Pip::instance(python)->info(package);
m_watcher->setFuture(future);
ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(future);
}
void PythonInterpreterAspectPrivate::handlePySidePackageInfo(const PipPackageInfo &pySideInfo,
const FilePath &python,
const QString &requestedPackageName)
{
const auto findPythonTools = [](const FilePaths &files,
const FilePath &location,
const FilePath &python) -> PySideTools {
PySideTools result;
const QString pySide6ProjectName
= OsSpecificAspects::withExecutableSuffix(python.osType(), "pyside6-project");
const QString pySide6UicName
= OsSpecificAspects::withExecutableSuffix(python.osType(), "pyside6-uic");
for (const FilePath &file : files) {
if (file.fileName() == pySide6ProjectName) {
result.pySideProjectPath = python.withNewMappedPath(location.resolvePath(file));
result.pySideProjectPath = result.pySideProjectPath.cleanPath();
if (!result.pySideUicPath.isEmpty())
return result;
} else if (file.fileName() == pySide6UicName) {
result.pySideUicPath = python.withNewMappedPath(location.resolvePath(file));
result.pySideUicPath = result.pySideUicPath.cleanPath();
if (!result.pySideProjectPath.isEmpty())
return result;
}
}
return {};
};
PySideTools tools = findPythonTools(pySideInfo.files, pySideInfo.location, python);
if (!tools.pySideProjectPath.isExecutableFile() && requestedPackageName != "PySide6") {
checkForPySide(python, "PySide6");
return;
}
updateTools(tools);
}
void PythonInterpreterAspectPrivate::currentInterpreterChanged()
{
const FilePath python = q->currentInterpreter().command;
checkForPySide(python);
for (FilePath &file : rc->project()->files(Project::AllFiles)) {
if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) {
if (document->mimeType() == Constants::C_PY_MIMETYPE
|| document->mimeType() == Constants::C_PY3_MIMETYPE) {
PyLSConfigureAssistant::openDocumentWithPython(python, document);
PySideInstaller::checkPySideInstallation(python, document);
}
}
}
}
void PythonInterpreterAspectPrivate::updateTools(const PySideTools &tools)
{
m_pySideUicPath = tools.pySideUicPath;
updateExtraCompilers();
if (Target *target = rc->target()) {
if (BuildConfiguration *buildConfiguration = target->activeBuildConfiguration()) {
if (BuildStepList *buildSteps = buildConfiguration->buildSteps()) {
if (auto buildStep = buildSteps->firstOfType<PySideBuildStep>())
buildStep->updatePySideProjectPath(tools.pySideProjectPath);
}
}
}
}
QList<PySideUicExtraCompiler *> PythonInterpreterAspect::extraCompilers() const
{
return d->m_extraCompilers;
}
void PythonInterpreterAspectPrivate::updateExtraCompilers()
{
QList<PySideUicExtraCompiler *> oldCompilers = m_extraCompilers;
m_extraCompilers.clear();
if (m_pySideUicPath.isExecutableFile()) {
auto uiMatcher = [](const Node *node) {
if (const FileNode *fileNode = node->asFileNode())
return fileNode->fileType() == FileType::Form;
return false;
};
const FilePaths uiFiles = rc->project()->files(uiMatcher);
for (const FilePath &uiFile : uiFiles) {
FilePath generated = uiFile.parentDir();
generated = generated.pathAppended("/ui_" + uiFile.baseName() + ".py");
int index = Utils::indexOf(oldCompilers, [&](PySideUicExtraCompiler *oldCompiler) {
return oldCompiler->pySideUicPath() == m_pySideUicPath
&& oldCompiler->project() == rc->project() && oldCompiler->source() == uiFile
&& oldCompiler->targets() == FilePaths{generated};
});
if (index < 0) {
m_extraCompilers << new PySideUicExtraCompiler(m_pySideUicPath,
rc->project(),
uiFile,
{generated},
this);
} else {
m_extraCompilers << oldCompilers.takeAt(index);
}
}
}
for (LanguageClient::Client *client : LanguageClient::LanguageClientManager::clients()) {
if (auto pylsClient = qobject_cast<PyLSClient *>(client))
pylsClient->updateExtraCompilers(rc->project(), m_extraCompilers);
}
qDeleteAll(oldCompilers);
}
// RunConfiguration
class PythonRunConfiguration : public RunConfiguration
@@ -368,11 +143,14 @@ public:
x11Forwarding.setMacroExpander(macroExpander());
x11Forwarding.setVisible(HostOsInfo::isAnyUnixHost());
if (const std::optional<Interpreter> kitPython = PythonKitAspect::python(target->kit()))
interpreter.setCurrentInterpreter(*kitPython);
interpreter.setLabelText(Tr::tr("Python:"));
interpreter.setReadOnly(true);
setCommandLineGetter([this] {
CommandLine cmd{interpreter.currentInterpreter().command};
CommandLine cmd;
cmd.setExecutable(interpreter());
if (interpreter().isEmpty())
return cmd;
if (!buffered())
cmd.addArg("-u");
cmd.addArg(mainScript().fileName());
@@ -382,6 +160,9 @@ public:
setUpdater([this] {
const BuildTargetInfo bti = buildTargetInfo();
const auto python = FilePath::fromSettings(bti.additionalData.toMap().value("python"));
interpreter.setValue(python);
setDefaultDisplayName(Tr::tr("Run %1").arg(bti.targetFilePath.toUserOutput()));
mainScript.setValue(bti.targetFilePath);
workingDir.setDefaultWorkingDirectory(bti.targetFilePath.parentDir());
@@ -390,7 +171,7 @@ public:
connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
}
PythonInterpreterAspect interpreter{this, this};
FilePathAspect interpreter{this};
BoolAspect buffered{this};
MainScriptAspect mainScript{this};
EnvironmentAspect environment{this};
@@ -411,7 +192,7 @@ PythonRunConfigurationFactory::PythonRunConfigurationFactory()
PythonOutputFormatterFactory::PythonOutputFormatterFactory()
{
setFormatterCreator([](Target *t) -> QList<OutputLineParser *> {
if (t && t->project()->mimeType() == Constants::C_PY_MIMETYPE)
if (t && t->project()->mimeType() == Constants::C_PY_PROJECT_MIME_TYPE)
return {new PythonOutputLineParser};
return {};
});

View File

@@ -10,7 +10,6 @@
namespace Python::Internal {
class PySideUicExtraCompiler;
class PythonRunConfiguration;
class PythonInterpreterAspect final : public ProjectExplorer::InterpreterAspect
{
@@ -18,8 +17,6 @@ public:
PythonInterpreterAspect(Utils::AspectContainer *container, ProjectExplorer::RunConfiguration *rc);
~PythonInterpreterAspect() final;
QList<PySideUicExtraCompiler *> extraCompilers() const;
private:
friend class PythonRunConfiguration;
class PythonInterpreterAspectPrivate *d = nullptr;

View File

@@ -11,6 +11,7 @@
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/processprogress.h>
#include <projectexplorer/kitaspects.h>
#include <projectexplorer/kitmanager.h>
@@ -813,7 +814,7 @@ void PythonSettings::addKitsForInterpreter(const Interpreter &interpreter)
const Id kitId = Id::fromString(interpreter.id);
if (Kit *k = KitManager::kit(kitId)) {
setRelevantAspectsToKit(k);
} else {
} else if (!isVenvPython(interpreter.command)) {
KitManager::registerKit(
[interpreter](Kit *k) {
k->setAutoDetected(true);
@@ -821,6 +822,7 @@ void PythonSettings::addKitsForInterpreter(const Interpreter &interpreter)
k->setUnexpandedDisplayName(interpreter.name);
setRelevantAspectsToKit(k);
PythonKitAspect::setPython(k, interpreter.id);
k->setSticky(PythonKitAspect::id(), true);
},
kitId);
}
@@ -914,7 +916,7 @@ PythonSettings *PythonSettings::instance()
void PythonSettings::createVirtualEnvironmentInteractive(
const FilePath &startDirectory,
const Interpreter &defaultInterpreter,
const std::function<void(std::optional<Interpreter>)> &callback)
const std::function<void(const FilePath &)> &callback)
{
QDialog dialog;
dialog.setModal(true);
@@ -954,26 +956,37 @@ void PythonSettings::createVirtualEnvironmentInteractive(
interpreters->currentData().toString());
auto venvDir = pathChooser->filePath();
createVirtualEnvironment(venvDir, interpreter, callback);
createVirtualEnvironment(interpreter.command, venvDir, callback);
}
void PythonSettings::createVirtualEnvironment(
const FilePath &python,
const FilePath &directory,
const Interpreter &interpreter,
const std::function<void(std::optional<Interpreter>)> &callback,
const QString &nameSuffix)
const std::function<void(const FilePath &)> &callback)
{
createVenv(interpreter.command, directory, [directory, callback, nameSuffix](bool success) {
std::optional<Interpreter> result;
if (success) {
QTC_ASSERT(python.isExecutableFile(), return);
QTC_ASSERT(!directory.exists() || directory.isDir(), return);
const CommandLine command(python, QStringList{"-m", "venv", directory.toUserOutput()});
auto process = new Process;
auto progress = new Core::ProcessProgress(process);
progress->setDisplayName(Tr::tr("Create Python venv"));
QObject::connect(process, &Process::done, [directory, process, callback](){
if (process->result() == ProcessResult::FinishedWithSuccess) {
FilePath venvPython = directory.osType() == Utils::OsTypeWindows ? directory / "Scripts"
: directory / "bin";
venvPython = venvPython.pathAppended("python").withExecutableSuffix();
if (venvPython.exists())
result = PythonSettings::addInterpreter(venvPython, false, nameSuffix);
if (venvPython.exists()) {
if (callback)
callback(venvPython);
emit instance()->virtualEnvironmentCreated(venvPython);
}
}
callback(result);
process->deleteLater();
});
process->setCommand(command);
process->start();
}
QList<Interpreter> PythonSettings::detectPythonVenvs(const FilePath &path)

View File

@@ -35,12 +35,11 @@ public:
static void createVirtualEnvironmentInteractive(
const Utils::FilePath &startDirectory,
const Interpreter &defaultInterpreter,
const std::function<void(std::optional<Interpreter>)> &callback);
const std::function<void(const Utils::FilePath &)> &callback);
static void createVirtualEnvironment(
const Utils::FilePath &interpreter,
const Utils::FilePath &directory,
const Interpreter &interpreter,
const std::function<void(std::optional<Interpreter>)> &callback,
const QString &nameSuffix = {});
const std::function<void(const Utils::FilePath &)> &callback = {});
static QList<Interpreter> detectPythonVenvs(const Utils::FilePath &path);
static void addKitsForInterpreter(const Interpreter &interpreter);
static void removeKitsForInterpreter(const Interpreter &interpreter);
@@ -49,6 +48,7 @@ signals:
void interpretersChanged(const QList<Interpreter> &interpreters, const QString &defaultId);
void pylsConfigurationChanged(const QString &configuration);
void pylsEnabledChanged(const bool enabled);
void virtualEnvironmentCreated(const Utils::FilePath &venvPython);
public slots:
void detectPythonOnDevice(const Utils::FilePaths &searchPaths,

View File

@@ -3,6 +3,9 @@
#include "pythonutils.h"
#include "pythonbuildconfiguration.h"
#include "pythonconstants.h"
#include "pythonkitaspect.h"
#include "pythonproject.h"
#include "pythonsettings.h"
#include "pythontr.h"
@@ -38,14 +41,12 @@ FilePath detectPython(const FilePath &documentPath)
FilePaths dirs = Environment::systemEnvironment().path();
if (project) {
if (auto target = project->activeTarget()) {
if (auto runConfig = target->activeRunConfiguration()) {
if (auto interpreter = runConfig->aspect<InterpreterAspect>())
return interpreter->currentInterpreter().command;
if (auto environmentAspect = runConfig->aspect<EnvironmentAspect>())
dirs = environmentAspect->environment().path();
}
if (project && project->mimeType() == Constants::C_PY_PROJECT_MIME_TYPE) {
if (const Target *target = project->activeTarget()) {
if (auto bc = qobject_cast<PythonBuildConfiguration *>(target->activeBuildConfiguration()))
return bc->python();
if (const std::optional<Interpreter> python = PythonKitAspect::python(target->kit()))
return python->command;
}
}
@@ -186,4 +187,9 @@ void createVenv(const Utils::FilePath &python,
process->start();
}
bool isVenvPython(const Utils::FilePath &python)
{
return python.parentDir().parentDir().contains("pyvenv.cfg");
}
} // Python::Internal

View File

@@ -20,4 +20,6 @@ void createVenv(const Utils::FilePath &python,
const Utils::FilePath &venvPath,
const std::function<void(bool)> &callback);
bool isVenvPython(const Utils::FilePath &python);
} // Python::Internal

View File

@@ -4,6 +4,7 @@
#include "pythonwizardpage.h"
#include "pythonconstants.h"
#include "pythonkitaspect.h"
#include "pythonsettings.h"
#include "pythontr.h"
@@ -14,6 +15,7 @@
#include <utils/mimeutils.h>
#include <utils/qtcassert.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
@@ -86,11 +88,6 @@ PythonWizardPage::PythonWizardPage(const QList<QPair<QString, QVariant>> &pySide
const int defaultPyside)
{
using namespace Layouting;
m_interpreter.setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID);
connect(PythonSettings::instance(),
&PythonSettings::interpretersChanged,
this,
&PythonWizardPage::updateInterpreters);
m_pySideVersion.setLabelText(Tr::tr("PySide version:"));
m_pySideVersion.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox);
@@ -99,49 +96,13 @@ PythonWizardPage::PythonWizardPage(const QList<QPair<QString, QVariant>> &pySide
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.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, &FilePathAspect::validChanged, this, &PythonWizardPage::updateStateLabel);
connect(&m_createVenv, &BaseAspect::changed, this, &PythonWizardPage::updateStateLabel);
Form {
m_pySideVersion, st, br,
m_interpreter, st, br,
m_createVenv, st, br,
m_venvPath, br,
m_stateLabel, br
}.attachTo(this);
}
void PythonWizardPage::initializePage()
{
auto wiz = qobject_cast<JsonWizard *>(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().isEmpty())
m_venvPath.setValue(projectDir.isEmpty() ? FilePath{} : projectDir / "venv");
updateInterpreters();
updateStateLabel();
}
bool PythonWizardPage::validatePage()
{
if (m_createVenv() && !m_venvPath.pathChooser()->isValid())
return false;
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)
@@ -149,70 +110,5 @@ bool PythonWizardPage::validatePage()
return true;
}
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()) {
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(),
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<InterpreterAspect>()) {
interpreters->setCurrentInterpreter(interpreter);
project->saveSettings();
}
}
}
delete project;
}
}
}
}
void PythonWizardPage::updateInterpreters()
{
m_interpreter.setDefaultInterpreter(PythonSettings::defaultInterpreter());
m_interpreter.updateInterpreters(PythonSettings::interpreters());
}
void PythonWizardPage::updateStateLabel()
{
QTC_ASSERT(m_stateLabel, return);
if (m_createVenv()) {
if (PathChooser *pathChooser = m_venvPath.pathChooser()) {
if (!pathChooser->isValid()) {
m_stateLabel->show();
m_stateLabel->setText(pathChooser->errorMessage());
return;
}
}
}
m_stateLabel->hide();
}
} // namespace Python::Internal

View File

@@ -27,19 +27,10 @@ class PythonWizardPage : public Utils::WizardPage
{
public:
PythonWizardPage(const QList<QPair<QString, QVariant>> &pySideAndData, const int defaultPyside);
void initializePage() override;
bool validatePage() override;
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::FilePathAspect m_venvPath;
Utils::InfoLabel *m_stateLabel = nullptr;
};
} // namespace Python::Internal