summaryrefslogtreecommitdiff
path: root/src/plugins/python
diff options
context:
space:
mode:
authorDavid Schulz <david.schulz@qt.io>2023-01-23 13:43:58 +0100
committerDavid Schulz <david.schulz@qt.io>2023-03-21 05:39:48 +0000
commit7d4f123842ea937de4390b2ba783c7c13f9ea55a (patch)
tree2a430480e14f39435c80d1b203bcdc61e6f297fc /src/plugins/python
parent5256f08b6dcd6fe5abdf80427626b56194ca209a (diff)
downloadqt-creator-7d4f123842ea937de4390b2ba783c7c13f9ea55a.tar.gz
Python: add create venv action
The action can be triggered from the interpreter chooser of the editor toolbar. Change-Id: Ie23b68a3790525ea02883ef359b357a0d317b2f5 Reviewed-by: Christian Stenger <christian.stenger@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Diffstat (limited to 'src/plugins/python')
-rw-r--r--src/plugins/python/pythoneditor.cpp37
-rw-r--r--src/plugins/python/pythonsettings.cpp81
-rw-r--r--src/plugins/python/pythonsettings.h4
-rw-r--r--src/plugins/python/pythonutils.cpp21
-rw-r--r--src/plugins/python/pythonutils.h4
5 files changed, 130 insertions, 17 deletions
diff --git a/src/plugins/python/pythoneditor.cpp b/src/plugins/python/pythoneditor.cpp
index f758ae3d9b..42771fc91a 100644
--- a/src/plugins/python/pythoneditor.cpp
+++ b/src/plugins/python/pythoneditor.cpp
@@ -163,6 +163,7 @@ void PythonEditorWidget::setUserDefinedPython(const Interpreter &interpreter)
}
}
definePythonForDocument(textDocument()->filePath(), interpreter.command);
+ updateInterpretersSelector();
pythonDocument->checkForPyls();
}
@@ -212,33 +213,51 @@ void PythonEditorWidget::updateInterpretersSelector()
m_interpreters->setText(text);
};
- const FilePath currentInterpreter = detectPython(textDocument()->filePath());
+ const FilePath currentInterpreterPath = detectPython(textDocument()->filePath());
const QList<Interpreter> configuredInterpreters = PythonSettings::interpreters();
- bool foundCurrentInterpreter = false;
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 (!foundCurrentInterpreter && interpreter.command == currentInterpreter) {
- foundCurrentInterpreter = 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 (!foundCurrentInterpreter) {
- if (currentInterpreter.exists())
- setButtonText(currentInterpreter.toUserOutput());
+ if (!currentInterpreter) {
+ if (currentInterpreterPath.exists())
+ setButtonText(currentInterpreterPath.toUserOutput());
else
setButtonText(Tr::tr("No Python Selected"));
}
- if (!interpretersGroup->actions().isEmpty())
- menu->addSeparator();
+ 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]() {
+ if (!currentInterpreter)
+ return;
+ auto callback = [self](const std::optional<Interpreter> &venvInterpreter) {
+ if (self && venvInterpreter)
+ self->setUserDefinedPython(*venvInterpreter);
+ };
+ PythonSettings::createVirtualEnvironment(self->textDocument()
+ ->filePath()
+ .parentDir(),
+ *currentInterpreter,
+ callback);
+ });
+ }
auto settingsAction = menu->addAction(Tr::tr("Manage Python Interpreters"));
connect(settingsAction, &QAction::triggered, this, []() {
Core::ICore::showOptionsDialog(Constants::C_PYTHONOPTIONS_PAGE_ID);
diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp
index 9306fe3b6f..f22f0c3c8c 100644
--- a/src/plugins/python/pythonsettings.cpp
+++ b/src/plugins/python/pythonsettings.cpp
@@ -6,6 +6,7 @@
#include "pythonconstants.h"
#include "pythonplugin.h"
#include "pythontr.h"
+#include "pythonutils.h"
#include <coreplugin/dialogs/ioptionspage.h>
#include <coreplugin/icore.h>
@@ -30,19 +31,22 @@
#include <utils/treemodel.h>
#include <utils/utilsicons.h>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDialogButtonBox>
#include <QDir>
+#include <QFormLayout>
+#include <QGroupBox>
+#include <QJsonDocument>
+#include <QJsonObject>
#include <QLabel>
-#include <QPushButton>
#include <QPointer>
+#include <QPushButton>
#include <QSettings>
#include <QStackedWidget>
#include <QTreeView>
-#include <QWidget>
#include <QVBoxLayout>
-#include <QGroupBox>
-#include <QCheckBox>
-#include <QJsonDocument>
-#include <QJsonObject>
+#include <QWidget>
using namespace ProjectExplorer;
using namespace Utils;
@@ -69,7 +73,7 @@ static Interpreter createInterpreter(const FilePath &python,
result.name = defaultName;
QDir pythonDir(python.parentDir().toString());
if (pythonDir.exists() && pythonDir.exists("activate") && pythonDir.cdUp())
- result.name += QString(" (%1 Virtual Environment)").arg(pythonDir.dirName());
+ result.name += QString(" (%1)").arg(pythonDir.dirName());
if (!suffix.isEmpty())
result.name += ' ' + suffix;
@@ -769,12 +773,75 @@ void PythonSettings::addInterpreter(const Interpreter &interpreter, bool isDefau
saveSettings();
}
+Interpreter PythonSettings::addInterpreter(const FilePath &interpreterPath, bool isDefault)
+{
+ const Interpreter interpreter = createInterpreter(interpreterPath, {});
+ addInterpreter(interpreter, isDefault);
+ return interpreter;
+}
+
PythonSettings *PythonSettings::instance()
{
QTC_CHECK(settingsInstance);
return settingsInstance;
}
+void PythonSettings::createVirtualEnvironment(
+ const FilePath &startDirectory,
+ const Interpreter &defaultInterpreter,
+ const std::function<void(std::optional<Interpreter>)> &callback)
+{
+ QDialog dialog;
+ dialog.setModal(true);
+ auto layout = new QFormLayout(&dialog);
+ auto interpreters = new QComboBox;
+ const QString preselectedId = defaultInterpreter.id.isEmpty()
+ ? PythonSettings::defaultInterpreter().id
+ : defaultInterpreter.id;
+ for (const Interpreter &interpreter : PythonSettings::interpreters()) {
+ interpreters->addItem(interpreter.name, interpreter.id);
+ if (!preselectedId.isEmpty() && interpreter.id == preselectedId)
+ interpreters->setCurrentIndex(interpreters->count() - 1);
+ }
+ layout->addRow(Tr::tr("Python Interpreter"), interpreters);
+ auto pathChooser = new PathChooser();
+ pathChooser->setInitialBrowsePathBackup(startDirectory);
+ pathChooser->setExpectedKind(PathChooser::Directory);
+ pathChooser->setPromptDialogTitle(Tr::tr("New Python Virtual Environment Directory"));
+ layout->addRow(Tr::tr("Virtual Environment Directory"), pathChooser);
+ auto buttons = new QDialogButtonBox(QDialogButtonBox::Cancel);
+ auto createButton = buttons->addButton(Tr::tr("Create"), QDialogButtonBox::AcceptRole);
+ createButton->setEnabled(false);
+ connect(pathChooser,
+ &PathChooser::validChanged,
+ createButton,
+ [createButton](bool valid) { createButton->setEnabled(valid); });
+ connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
+ connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
+ layout->addRow(buttons);
+ dialog.setLayout(layout);
+ if (dialog.exec() == QDialog::Rejected) {
+ callback({});
+ return;
+ }
+
+ const Interpreter interpreter = PythonSettings::interpreter(
+ interpreters->currentData().toString());
+
+ auto venvDir = pathChooser->filePath();
+ createVenv(interpreter.command, venvDir, [venvDir, callback](bool success){
+ std::optional<Interpreter> result;
+ if (success) {
+ FilePath venvPython = venvDir.osType() == Utils::OsTypeWindows ? venvDir / "Scripts"
+ : venvDir / "bin";
+ venvPython = venvPython.pathAppended("python").withExecutableSuffix();
+ if (venvPython.exists())
+ result = PythonSettings::addInterpreter(venvPython);
+ }
+ callback(result);
+ });
+}
+
QList<Interpreter> PythonSettings::detectPythonVenvs(const FilePath &path)
{
QList<Interpreter> result;
diff --git a/src/plugins/python/pythonsettings.h b/src/plugins/python/pythonsettings.h
index 693c732208..2e27e26616 100644
--- a/src/plugins/python/pythonsettings.h
+++ b/src/plugins/python/pythonsettings.h
@@ -24,12 +24,14 @@ public:
static Interpreter interpreter(const QString &interpreterId);
static void setInterpreter(const QList<Interpreter> &interpreters, const QString &defaultId);
static void addInterpreter(const Interpreter &interpreter, bool isDefault = false);
+ static Interpreter addInterpreter(const Utils::FilePath &interpreterPath,
+ bool isDefault = false);
static void setPyLSConfiguration(const QString &configuration);
static bool pylsEnabled();
static void setPylsEnabled(const bool &enabled);
static QString pylsConfiguration();
static PythonSettings *instance();
-
+ static void createVirtualEnvironment(const Utils::FilePath &startDirectory, const Interpreter &defaultInterpreter, const std::function<void (std::optional<Interpreter>)> &callback);
static QList<Interpreter> detectPythonVenvs(const Utils::FilePath &path);
signals:
diff --git a/src/plugins/python/pythonutils.cpp b/src/plugins/python/pythonutils.cpp
index 8635605092..1f89eef5b5 100644
--- a/src/plugins/python/pythonutils.cpp
+++ b/src/plugins/python/pythonutils.cpp
@@ -8,6 +8,7 @@
#include "pythontr.h"
#include <coreplugin/messagemanager.h>
+#include <coreplugin/progressmanager/processprogress.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectmanager.h>
@@ -164,4 +165,24 @@ PythonProject *pythonProjectForFile(const FilePath &pythonFile)
return nullptr;
}
+void createVenv(const Utils::FilePath &python,
+ const Utils::FilePath &venvPath,
+ const std::function<void(bool)> &callback)
+{
+ QTC_ASSERT(python.isExecutableFile(), callback(false); return);
+ QTC_ASSERT(!venvPath.exists() || venvPath.isDir(), callback(false); return);
+
+ const CommandLine command(python, QStringList{"-m", "venv", venvPath.toUserOutput()});
+
+ auto process = new QtcProcess;
+ auto progress = new Core::ProcessProgress(process);
+ progress->setDisplayName(Tr::tr("Create Python venv"));
+ QObject::connect(process, &QtcProcess::done, [process, callback](){
+ callback(process->result() == ProcessResult::FinishedWithSuccess);
+ process->deleteLater();
+ });
+ process->setCommand(command);
+ process->start();
+}
+
} // Python::Internal
diff --git a/src/plugins/python/pythonutils.h b/src/plugins/python/pythonutils.h
index 8d5b06974e..f3e685b4ae 100644
--- a/src/plugins/python/pythonutils.h
+++ b/src/plugins/python/pythonutils.h
@@ -16,4 +16,8 @@ QString pythonName(const Utils::FilePath &pythonPath);
class PythonProject;
PythonProject *pythonProjectForFile(const Utils::FilePath &pythonFile);
+void createVenv(const Utils::FilePath &python,
+ const Utils::FilePath &venvPath,
+ const std::function<void(bool)> &callback);
+
} // Python::Internal