1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
|
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "pythonkitaspect.h"
#include "pythonconstants.h"
#include "pythonsettings.h"
#include "pythontr.h"
#include "pythonutils.h"
#include <projectexplorer/kit.h>
#include <projectexplorer/kitaspect.h>
#include <utils/guard.h>
#include <utils/layoutbuilder.h>
#include <utils/qtcprocess.h>
#include <QComboBox>
#include <QSortFilterProxyModel>
using namespace ProjectExplorer;
using namespace Utils;
namespace Python {
namespace Internal {
class PythonAspectModel : public QSortFilterProxyModel
{
public:
using QSortFilterProxyModel::QSortFilterProxyModel;
void reset()
{
if (QAbstractItemModel * const model = sourceModel()) {
setSourceModel(nullptr);
model->deleteLater();
}
ListModel<Interpreter> * const model = createInterpreterModel(this);
model->setAllData(model->allData() << Interpreter("none", {}, {}));
setSourceModel(model);
}
};
class PythonKitAspectImpl final : public KitAspect
{
public:
PythonKitAspectImpl(Kit *kit, const KitAspectFactory *kitInfo)
: KitAspect(kit, kitInfo)
{
setManagingPage(Constants::C_PYTHONOPTIONS_PAGE_ID);
const auto model = new PythonAspectModel(this);
auto getter = [](const Kit &k) -> QVariant {
if (const auto interpreter = PythonKitAspect::python(&k))
return interpreter->id;
return {};
};
auto setter = [](Kit &k, const QVariant &v) {
PythonKitAspect::setPython(&k, v.toString());
};
auto resetModel = [model] { model->reset(); };
addListAspectSpec({model, std::move(getter), std::move(setter), std::move(resetModel)});
connect(PythonSettings::instance(),
&PythonSettings::interpretersChanged,
this,
&PythonKitAspectImpl::refresh);
}
void refresh() override
{
KitAspect::refresh();
emit changed(); // we need to emit changed here to update changes in the macro expander
}
};
class PythonKitAspectFactory : public KitAspectFactory
{
public:
PythonKitAspectFactory()
{
setId(PythonKitAspect::id());
setDisplayName(Tr::tr("Python"));
setDescription(Tr::tr("The interpreter used for Python based projects."));
setPriority(10000);
}
Tasks validate(const Kit *k) const override
{
Tasks result;
const std::optional<Interpreter> python = PythonKitAspect::python(k);
if (!python)
return result;
const FilePath path = python->command;
if (!path.isLocal())
return result;
if (path.isEmpty()) {
result << BuildSystemTask(Task::Error, Tr::tr("No Python set up."));
} else if (!path.exists()) {
result << BuildSystemTask(Task::Error,
Tr::tr("Python \"%1\" not found.").arg(path.toUserOutput()));
} else if (!path.isExecutableFile()) {
result << BuildSystemTask(Task::Error,
Tr::tr("Python \"%1\" is not executable.")
.arg(path.toUserOutput()));
} else {
if (!pipIsUsable(path)) {
result << BuildSystemTask(
Task::Warning,
Tr::tr("Python \"%1\" does not contain a usable pip. pip is needed to install "
"Python "
"packages from the Python Package Index, like PySide and the Python "
"language server. To use any of that functionality "
"ensure that pip is installed for that Python.")
.arg(path.toUserOutput()));
}
if (!venvIsUsable(path)) {
result << BuildSystemTask(
Task::Warning,
Tr::tr(
"Python \"%1\" does not contain a usable venv. venv is the recommended way "
"to isolate a development environment for a project from the globally "
"installed Python.")
.arg(path.toUserOutput()));
}
}
return result;
}
ItemList toUserOutput(const Kit *k) const override
{
if (const std::optional<Interpreter> python = PythonKitAspect::python(k)) {
return {{displayName(),
QString("%1 (%2)").arg(python->name).arg(python->command.toUserOutput())}};
}
return {};
}
KitAspect *createKitAspect(Kit *k) const override { return new PythonKitAspectImpl(k, this); }
QSet<Id> availableFeatures(const Kit *k) const override
{
if (PythonKitAspect::python(k))
return {PythonKitAspect::id()};
return {};
}
void addToMacroExpander(Kit *kit, MacroExpander *expander) const override
{
QTC_ASSERT(kit, return);
expander->registerVariable("Python:Name",
Tr::tr("Name of Python Interpreter"),
[kit]() -> QString {
if (auto python = PythonKitAspect::python(kit))
return python->name;
return {};
});
expander->registerVariable("Python:Path",
Tr::tr("Path to Python Interpreter"),
[kit]() -> QString {
if (auto python = PythonKitAspect::python(kit))
return python->command.toUserOutput();
return {};
});
}
};
} // Internal
using namespace Internal;
std::optional<Interpreter> PythonKitAspect::python(const Kit *kit)
{
QTC_ASSERT(kit, return {});
const QString interpreterId = kit->value(id()).toString();
if (interpreterId.isEmpty())
return {};
if (const Interpreter interpreter = PythonSettings::interpreter(interpreterId); interpreter.id == interpreterId)
return interpreter;
return {};
}
void PythonKitAspect::setPython(Kit *k, const QString &interpreterId)
{
QTC_ASSERT(k, return);
k->setValue(PythonKitAspect::id(), interpreterId);
}
Id PythonKitAspect::id()
{
return Constants::PYTHON_TOOLKIT_ASPECT_ID;
}
const PythonKitAspectFactory thePythonToolKitAspectFactory;
} // namespace Python
|