Promote previously python-specific InterpreterAspect

... and drop PythonRunConfiguration, which is a plain RunConfiguration now.

Change-Id: I540cb738180fc1424f730d6d1998886915ce527b
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
hjk
2022-04-13 12:26:54 +02:00
parent 9a6d8aebe7
commit 28cfdf388a
16 changed files with 327 additions and 339 deletions

View File

@@ -184,8 +184,8 @@ public:
Utils::FilePaths projectSourceFiles;
// Used by Script debugging
QString interpreter;
QString mainScript;
Utils::FilePath interpreter;
Utils::FilePath mainScript;
// Used by AttachCrashedExternal.
QString crashParameter;

View File

@@ -939,16 +939,17 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
m_runParameters.nativeMixedEnabled = bool(nativeMixedOverride);
RunConfiguration *runConfig = runControl->runConfiguration();
if (runConfig && runConfig->property("supportsDebugger").toBool()) {
const QString mainScript = runConfig->property("mainScript").toString();
const QString interpreter = runConfig->property("interpreter").toString();
if (!interpreter.isEmpty() && mainScript.endsWith(".py")) {
m_runParameters.mainScript = mainScript;
m_runParameters.interpreter = interpreter;
const QString args = runConfig->property("arguments").toString();
m_runParameters.inferior.command.addArgs(args, CommandLine::Raw);
m_engine = createPdbEngine();
if (auto interpreterAspect = runControl->aspect<InterpreterAspect>()) {
if (auto mainScriptAspect = runControl->aspect<MainScriptAspect>()) {
const FilePath mainScript = mainScriptAspect->filePath;
const FilePath interpreter = interpreterAspect->interpreter.command;
if (!interpreter.isEmpty() && mainScript.endsWith(".py")) {
m_runParameters.mainScript = mainScript;
m_runParameters.interpreter = interpreter;
if (auto args = runControl->aspect<ArgumentsAspect>())
m_runParameters.inferior.command.addArgs(args->arguments, CommandLine::Raw);
m_engine = createPdbEngine();
}
}
}

View File

@@ -125,28 +125,24 @@ void PdbEngine::setupEngine()
connect(&m_proc, &QtcProcess::readyReadStandardOutput, this, &PdbEngine::readPdbStandardOutput);
connect(&m_proc, &QtcProcess::readyReadStandardError, this, &PdbEngine::readPdbStandardError);
QFile scriptFile(runParameters().mainScript);
if (!scriptFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
const FilePath scriptFile = runParameters().mainScript;
if (!scriptFile.isReadableFile()) {
AsynchronousMessageBox::critical(tr("Python Error"),
QString("Cannot open script file %1:\n%2").
arg(scriptFile.fileName(), scriptFile.errorString()));
QString("Cannot open script file %1").arg(scriptFile.toUserOutput()));
notifyEngineSetupFailed();
}
QStringList args = {bridge, scriptFile.fileName()};
args.append(ProcessArgs::splitArgs(runParameters().inferior.workingDirectory.path()));
showMessage("STARTING " + m_interpreter + ' ' + args.join(' '));
CommandLine cmd{m_interpreter, {bridge, scriptFile.path()}};
cmd.addArg(runParameters().inferior.workingDirectory.path());
showMessage("STARTING " + cmd.toUserOutput());
m_proc.setEnvironment(runParameters().debugger.environment);
m_proc.setCommand({ FilePath::fromString(m_interpreter), args });
m_proc.setCommand(cmd);
m_proc.start();
if (!m_proc.waitForStarted()) {
const QString msg = tr("Unable to start pdb \"%1\": %2")
.arg(m_interpreter, m_proc.errorString());
notifyEngineSetupFailed();
showMessage("ADAPTER START FAILED");
if (!msg.isEmpty())
ICore::showWarningWithOptions(tr("Adapter start failed"), msg);
ICore::showWarningWithOptions(tr("Adapter start failed"), m_proc.exitMessage());
notifyEngineSetupFailed();
return;
}
@@ -416,7 +412,7 @@ QString PdbEngine::errorMessage(QProcess::ProcessError error) const
return tr("The Pdb process failed to start. Either the "
"invoked program \"%1\" is missing, or you may have insufficient "
"permissions to invoke the program.")
.arg(m_interpreter);
.arg(m_interpreter.toUserOutput());
case QProcess::Crashed:
return tr("The Pdb process crashed some time after starting "
"successfully.");

View File

@@ -112,7 +112,7 @@ private:
QString m_inbuffer;
Utils::QtcProcess m_proc;
QString m_interpreter;
Utils::FilePath m_interpreter;
};
} // namespace Internal

View File

@@ -32,6 +32,8 @@
#include "runconfiguration.h"
#include "target.h"
#include <coreplugin/icore.h>
#include <utils/detailsbutton.h>
#include <utils/fancylineedit.h>
#include <utils/layoutbuilder.h>
@@ -40,11 +42,13 @@
#include <utils/utilsicons.h>
#include <QCheckBox>
#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
#include <QFormLayout>
#include <QPlainTextEdit>
#include <QToolButton>
#include <QPushButton>
using namespace Utils;
@@ -764,4 +768,109 @@ RunAsRootAspect::RunAsRootAspect()
setLabel(tr("Run as root user"), LabelPlacement::AtCheckBox);
}
Interpreter::Interpreter()
: id(QUuid::createUuid().toString())
{}
Interpreter::Interpreter(const QString &_id,
const QString &_name,
const FilePath &_command,
bool _autoDetected)
: id(_id)
, name(_name)
, command(_command)
, autoDetected(_autoDetected)
{}
/*!
\class ProjectExplorer::InterpreterAspect
\inmodule QtCreator
\brief The InterpreterAspect class lets a user specify an interpreter
to use with files or projects using an interpreted language.
*/
InterpreterAspect::InterpreterAspect()
{
addDataExtractor(this, &InterpreterAspect::currentInterpreter, &Data::interpreter);
}
Interpreter InterpreterAspect::currentInterpreter() const
{
return Utils::findOrDefault(m_interpreters, Utils::equal(&Interpreter::id, m_currentId));
}
void InterpreterAspect::updateInterpreters(const QList<Interpreter> &interpreters)
{
m_interpreters = interpreters;
if (m_comboBox)
updateComboBox();
}
void InterpreterAspect::setCurrentInterpreter(const Interpreter &interpreter)
{
m_currentId = interpreter.id;
emit changed();
}
void InterpreterAspect::fromMap(const QVariantMap &map)
{
m_currentId = map.value(settingsKey(), m_defaultId).toString();
}
void InterpreterAspect::toMap(QVariantMap &map) const
{
saveToMap(map, m_currentId, QString(), settingsKey());
}
void InterpreterAspect::addToLayout(LayoutBuilder &builder)
{
if (QTC_GUARD(m_comboBox.isNull()))
m_comboBox = new QComboBox;
updateComboBox();
connect(m_comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &InterpreterAspect::updateCurrentInterpreter);
auto manageButton = new QPushButton(tr("Manage..."));
connect(manageButton, &QPushButton::clicked, [this] {
Core::ICore::showOptionsDialog(m_settingsDialogId);
});
builder.addItems({tr("Interpreter"), m_comboBox.data(), manageButton});
}
void InterpreterAspect::updateCurrentInterpreter()
{
const int index = m_comboBox->currentIndex();
if (index < 0)
return;
QTC_ASSERT(index < m_interpreters.size(), return);
m_currentId = m_interpreters[index].id;
m_comboBox->setToolTip(m_interpreters[index].command.toUserOutput());
emit changed();
}
void InterpreterAspect::updateComboBox()
{
int currentIndex = -1;
int defaultIndex = -1;
const QString currentId = m_currentId;
m_comboBox->clear();
for (const Interpreter &interpreter : qAsConst(m_interpreters)) {
int index = m_comboBox->count();
m_comboBox->addItem(interpreter.name);
m_comboBox->setItemData(index, interpreter.command.toUserOutput(), Qt::ToolTipRole);
if (interpreter.id == currentId)
currentIndex = index;
if (interpreter.id == m_defaultId)
defaultIndex = index;
}
if (currentIndex >= 0)
m_comboBox->setCurrentIndex(currentIndex);
else if (defaultIndex >= 0)
m_comboBox->setCurrentIndex(defaultIndex);
updateCurrentInterpreter();
}
} // namespace ProjectExplorer

View File

@@ -33,6 +33,7 @@
QT_BEGIN_NAMESPACE
class QCheckBox;
class QComboBox;
class QPlainTextEdit;
class QToolButton;
QT_END_NAMESPACE
@@ -214,4 +215,61 @@ public:
SymbolFileAspect() = default;
};
class PROJECTEXPLORER_EXPORT Interpreter
{
public:
Interpreter();
Interpreter(const QString &id,
const QString &name,
const Utils::FilePath &command,
bool autoDetected = true);
inline bool operator==(const Interpreter &other) const
{
return id == other.id && name == other.name && command == other.command;
}
QString id;
QString name;
Utils::FilePath command;
bool autoDetected = true;
};
class PROJECTEXPLORER_EXPORT InterpreterAspect : public Utils::BaseAspect
{
Q_OBJECT
public:
InterpreterAspect();
Interpreter currentInterpreter() const;
void updateInterpreters(const QList<Interpreter> &interpreters);
void setDefaultInterpreter(const Interpreter &interpreter) { m_defaultId = interpreter.id; }
void setCurrentInterpreter(const Interpreter &interpreter);
void setSettingsDialogId(Utils::Id id) { m_settingsDialogId = id; }
void fromMap(const QVariantMap &) override;
void toMap(QVariantMap &) const override;
void addToLayout(Utils::LayoutBuilder &builder) override;
struct Data : Utils::BaseAspect::Data { Interpreter interpreter; };
private:
void updateCurrentInterpreter();
void updateComboBox();
QList<Interpreter> m_interpreters;
QPointer<QComboBox> m_comboBox;
QString m_defaultId;
QString m_currentId;
Utils::Id m_settingsDialogId;
};
class PROJECTEXPLORER_EXPORT MainScriptAspect : public Utils::StringAspect
{
Q_OBJECT
public:
MainScriptAspect() = default;
};
} // namespace ProjectExplorer

View File

@@ -25,6 +25,7 @@
#include "pyside.h"
#include "pythonconstants.h"
#include "pythonplugin.h"
#include "pythonproject.h"
#include "pythonrunconfiguration.h"
@@ -32,8 +33,12 @@
#include "pythonutils.h"
#include <coreplugin/icore.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/target.h>
#include <texteditor/textdocument.h>
#include <utils/algorithm.h>
#include <utils/infobar.h>
#include <utils/runextensions.h>
@@ -42,6 +47,7 @@
#include <QTextCursor>
using namespace Utils;
using namespace ProjectExplorer;
namespace Python {
namespace Internal {
@@ -106,10 +112,12 @@ void PySideInstaller::installPyside(const Utils::FilePath &python,
}
void PySideInstaller::changeInterpreter(const QString &interpreterId,
PythonRunConfiguration *runConfig)
RunConfiguration *runConfig)
{
if (runConfig)
runConfig->setInterpreter(PythonSettings::interpreter(interpreterId));
if (runConfig) {
if (auto aspect = runConfig->aspect<InterpreterAspect>())
aspect->setCurrentInterpreter(PythonSettings::interpreter(interpreterId));
}
}
void PySideInstaller::handlePySideMissing(const FilePath &python,
@@ -128,21 +136,23 @@ void PySideInstaller::handlePySideMissing(const FilePath &python,
if (PythonProject *project = pythonProjectForFile(document->filePath())) {
if (ProjectExplorer::Target *target = project->activeTarget()) {
if (auto runConfiguration = qobject_cast<PythonRunConfiguration *>(
target->activeRunConfiguration())) {
auto runConfiguration = target->activeRunConfiguration();
if (runConfiguration->id() == Constants::C_PYTHONRUNCONFIGURATION_ID) {
const QList<InfoBarEntry::ComboInfo> interpreters = Utils::transform(
PythonSettings::interpreters(), [](const Interpreter &interpreter) {
return InfoBarEntry::ComboInfo{interpreter.name, interpreter.id};
});
auto interpreterChangeCallback =
[=, rc = QPointer<PythonRunConfiguration>(runConfiguration)](
[=, rc = QPointer<RunConfiguration>(runConfiguration)](
const InfoBarEntry::ComboInfo &info) {
changeInterpreter(info.data.toString(), rc);
};
const auto isCurrentInterpreter
= Utils::equal(&InfoBarEntry::ComboInfo::data,
QVariant(runConfiguration->interpreter().id));
auto interpreterAspect = runConfiguration->aspect<InterpreterAspect>();
QTC_ASSERT(interpreterAspect, return);
const QString id = interpreterAspect->currentInterpreter().id;
const auto isCurrentInterpreter = Utils::equal(&InfoBarEntry::ComboInfo::data,
QVariant(id));
const QString switchTooltip = tr("Switch the Python interpreter for %1")
.arg(runConfiguration->displayName());
info.setComboInfo(interpreters,

View File

@@ -32,11 +32,11 @@
#include <QTextDocument>
namespace TextEditor { class TextDocument; }
namespace ProjectExplorer { class RunConfiguration; }
namespace Python {
namespace Internal {
class PythonRunConfiguration;
class PySideInstaller : public QObject
{
Q_DECLARE_TR_FUNCTIONS(Python::Internal::PySideInstaller)
@@ -49,7 +49,7 @@ private:
void installPyside(const Utils::FilePath &python,
const QString &pySide, TextEditor::TextDocument *document);
void changeInterpreter(const QString &interpreterId, PythonRunConfiguration *runConfig);
void changeInterpreter(const QString &interpreterId, ProjectExplorer::RunConfiguration *runConfig);
void handlePySideMissing(const Utils::FilePath &python,
const QString &pySide,
TextEditor::TextDocument *document);

View File

@@ -31,6 +31,8 @@ namespace Python {
namespace Constants {
const char C_PYTHONEDITOR_ID[] = "PythonEditor.PythonEditor";
const char C_PYTHONRUNCONFIGURATION_ID[] = "PythonEditor.RunConfiguration.";
const char C_EDITOR_DISPLAY_NAME[] =
QT_TRANSLATE_NOOP("OpenWith::Editors", "Python Editor");

View File

@@ -56,6 +56,7 @@
#include <QTimer>
using namespace LanguageClient;
using namespace ProjectExplorer;
using namespace Utils;
namespace Python {

View File

@@ -37,7 +37,6 @@ namespace TextEditor { class TextDocument; }
namespace Python {
namespace Internal {
class Interpreter;
struct PythonLanguageServerState;
class PyLSSettings : public LanguageClient::StdIOSettings

View File

@@ -135,220 +135,80 @@ private:
////////////////////////////////////////////////////////////////
class InterpreterAspect : public BaseAspect
class PythonRunConfiguration : public RunConfiguration
{
Q_OBJECT
public:
InterpreterAspect() = default;
PythonRunConfiguration(Target *target, Id id)
: RunConfiguration(target, id)
{
auto interpreterAspect = addAspect<InterpreterAspect>();
interpreterAspect->setSettingsKey("PythonEditor.RunConfiguation.Interpreter");
interpreterAspect->setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID);
Interpreter currentInterpreter() const;
void updateInterpreters(const QList<Interpreter> &interpreters);
void setDefaultInterpreter(const Interpreter &interpreter) { m_defaultId = interpreter.id; }
void setCurrentInterpreter(const Interpreter &interpreter);
connect(interpreterAspect, &InterpreterAspect::changed, this, [this, interpreterAspect] {
using namespace LanguageClient;
const FilePath python = interpreterAspect->currentInterpreter().command;
void fromMap(const QVariantMap &) override;
void toMap(QVariantMap &) const override;
void addToLayout(LayoutBuilder &builder) override;
private:
void updateCurrentInterpreter();
void updateComboBox();
QList<Interpreter> m_interpreters;
QPointer<QComboBox> m_comboBox;
QString m_defaultId;
QString m_currentId;
};
Interpreter InterpreterAspect::currentInterpreter() const
{
return Utils::findOrDefault(m_interpreters, Utils::equal(&Interpreter::id, m_currentId));
}
void InterpreterAspect::updateInterpreters(const QList<Interpreter> &interpreters)
{
m_interpreters = interpreters;
if (m_comboBox)
updateComboBox();
}
void InterpreterAspect::setCurrentInterpreter(const Interpreter &interpreter)
{
m_currentId = interpreter.id;
emit changed();
}
void InterpreterAspect::fromMap(const QVariantMap &map)
{
m_currentId = map.value(settingsKey(), m_defaultId).toString();
}
void InterpreterAspect::toMap(QVariantMap &map) const
{
saveToMap(map, m_currentId, QString(), settingsKey());
}
void InterpreterAspect::addToLayout(LayoutBuilder &builder)
{
if (QTC_GUARD(m_comboBox.isNull()))
m_comboBox = new QComboBox;
updateComboBox();
connect(m_comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &InterpreterAspect::updateCurrentInterpreter);
auto manageButton = new QPushButton(tr("Manage..."));
connect(manageButton, &QPushButton::clicked, []() {
Core::ICore::showOptionsDialog(Constants::C_PYTHONOPTIONS_PAGE_ID);
});
builder.addItems({tr("Interpreter"), m_comboBox.data(), manageButton});
}
void InterpreterAspect::updateCurrentInterpreter()
{
const int index = m_comboBox->currentIndex();
if (index < 0)
return;
QTC_ASSERT(index < m_interpreters.size(), return);
m_currentId = m_interpreters[index].id;
m_comboBox->setToolTip(m_interpreters[index].command.toUserOutput());
emit changed();
}
void InterpreterAspect::updateComboBox()
{
int currentIndex = -1;
int defaultIndex = -1;
const QString currentId = m_currentId;
m_comboBox->clear();
for (const Interpreter &interpreter : qAsConst(m_interpreters)) {
int index = m_comboBox->count();
m_comboBox->addItem(interpreter.name);
m_comboBox->setItemData(index, interpreter.command.toUserOutput(), Qt::ToolTipRole);
if (interpreter.id == currentId)
currentIndex = index;
if (interpreter.id == m_defaultId)
defaultIndex = index;
}
if (currentIndex >= 0)
m_comboBox->setCurrentIndex(currentIndex);
else if (defaultIndex >= 0)
m_comboBox->setCurrentIndex(defaultIndex);
updateCurrentInterpreter();
}
class MainScriptAspect : public StringAspect
{
Q_OBJECT
public:
MainScriptAspect() = default;
};
PythonRunConfiguration::PythonRunConfiguration(Target *target, Utils::Id id)
: RunConfiguration(target, id)
{
auto interpreterAspect = addAspect<InterpreterAspect>();
interpreterAspect->setSettingsKey("PythonEditor.RunConfiguation.Interpreter");
connect(interpreterAspect, &InterpreterAspect::changed,
this, &PythonRunConfiguration::interpreterChanged);
connect(PythonSettings::instance(), &PythonSettings::interpretersChanged,
interpreterAspect, &InterpreterAspect::updateInterpreters);
QList<Interpreter> interpreters = PythonSettings::detectPythonVenvs(project()->projectDirectory());
aspect<InterpreterAspect>()->updateInterpreters(PythonSettings::interpreters());
aspect<InterpreterAspect>()->setDefaultInterpreter(
interpreters.isEmpty() ? PythonSettings::defaultInterpreter() : interpreters.first());
auto bufferedAspect = addAspect<BoolAspect>();
bufferedAspect->setSettingsKey("PythonEditor.RunConfiguation.Buffered");
bufferedAspect->setLabel(tr("Buffered output"), BoolAspect::LabelPlacement::AtCheckBox);
bufferedAspect->setToolTip(tr("Enabling improves output performance, "
"but results in delayed output."));
auto scriptAspect = addAspect<MainScriptAspect>();
scriptAspect->setSettingsKey("PythonEditor.RunConfiguation.Script");
scriptAspect->setLabelText(tr("Script:"));
scriptAspect->setDisplayStyle(StringAspect::LabelDisplay);
addAspect<LocalEnvironmentAspect>(target);
auto argumentsAspect = addAspect<ArgumentsAspect>();
addAspect<WorkingDirectoryAspect>(nullptr);
addAspect<TerminalAspect>();
setCommandLineGetter([this, bufferedAspect, interpreterAspect, argumentsAspect] {
CommandLine cmd{interpreterAspect->currentInterpreter().command};
if (!bufferedAspect->value())
cmd.addArg("-u");
cmd.addArg(mainScript());
cmd.addArgs(argumentsAspect->arguments(macroExpander()), CommandLine::Raw);
return cmd;
});
setUpdater([this, scriptAspect] {
const BuildTargetInfo bti = buildTargetInfo();
const QString script = bti.targetFilePath.toUserOutput();
setDefaultDisplayName(tr("Run %1").arg(script));
scriptAspect->setValue(script);
aspect<WorkingDirectoryAspect>()->setDefaultWorkingDirectory(bti.targetFilePath.parentDir());
});
connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
}
void PythonRunConfiguration::interpreterChanged()
{
using namespace LanguageClient;
const FilePath python = interpreter().command;
for (FilePath &file : project()->files(Project::AllFiles)) {
if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) {
if (document->mimeType() == Constants::C_PY_MIMETYPE) {
PyLSConfigureAssistant::instance()->openDocumentWithPython(python, document);
PySideInstaller::instance()->checkPySideInstallation(python, document);
for (FilePath &file : project()->files(Project::AllFiles)) {
if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) {
if (document->mimeType() == Constants::C_PY_MIMETYPE) {
PyLSConfigureAssistant::instance()->openDocumentWithPython(python, document);
PySideInstaller::instance()->checkPySideInstallation(python, document);
}
}
}
}
});
connect(PythonSettings::instance(), &PythonSettings::interpretersChanged,
interpreterAspect, &InterpreterAspect::updateInterpreters);
QList<Interpreter> interpreters = PythonSettings::detectPythonVenvs(project()->projectDirectory());
interpreterAspect->updateInterpreters(PythonSettings::interpreters());
interpreterAspect->setDefaultInterpreter(
interpreters.isEmpty() ? PythonSettings::defaultInterpreter() : interpreters.first());
auto bufferedAspect = addAspect<BoolAspect>();
bufferedAspect->setSettingsKey("PythonEditor.RunConfiguation.Buffered");
bufferedAspect->setLabel(tr("Buffered output"), BoolAspect::LabelPlacement::AtCheckBox);
bufferedAspect->setToolTip(tr("Enabling improves output performance, "
"but results in delayed output."));
auto scriptAspect = addAspect<MainScriptAspect>();
scriptAspect->setSettingsKey("PythonEditor.RunConfiguation.Script");
scriptAspect->setLabelText(tr("Script:"));
scriptAspect->setDisplayStyle(StringAspect::LabelDisplay);
addAspect<LocalEnvironmentAspect>(target);
auto argumentsAspect = addAspect<ArgumentsAspect>();
addAspect<WorkingDirectoryAspect>(nullptr);
addAspect<TerminalAspect>();
setCommandLineGetter([this, bufferedAspect, interpreterAspect, argumentsAspect, scriptAspect] {
CommandLine cmd{interpreterAspect->currentInterpreter().command};
if (!bufferedAspect->value())
cmd.addArg("-u");
cmd.addArg(scriptAspect->filePath().fileName());
cmd.addArgs(argumentsAspect->arguments(macroExpander()), CommandLine::Raw);
return cmd;
});
setUpdater([this, scriptAspect] {
const BuildTargetInfo bti = buildTargetInfo();
const QString script = bti.targetFilePath.toUserOutput();
setDefaultDisplayName(tr("Run %1").arg(script));
scriptAspect->setValue(script);
aspect<WorkingDirectoryAspect>()->setDefaultWorkingDirectory(bti.targetFilePath.parentDir());
});
connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
}
}
bool PythonRunConfiguration::supportsDebugger() const
{
return true;
}
QString PythonRunConfiguration::mainScript() const
{
return aspect<MainScriptAspect>()->value();
}
QString PythonRunConfiguration::arguments() const
{
return aspect<ArgumentsAspect>()->arguments(macroExpander());
}
Interpreter PythonRunConfiguration::interpreter() const
{
return aspect<InterpreterAspect>()->currentInterpreter();
}
QString PythonRunConfiguration::interpreterPath() const
{
return interpreter().command.toString();
}
void PythonRunConfiguration::setInterpreter(const Interpreter &interpreter)
{
aspect<InterpreterAspect>()->setCurrentInterpreter(interpreter);
}
};
PythonRunConfigurationFactory::PythonRunConfigurationFactory()
{
registerRunConfiguration<PythonRunConfiguration>("PythonEditor.RunConfiguration.");
registerRunConfiguration<PythonRunConfiguration>(Constants::C_PYTHONRUNCONFIGURATION_ID);
addSupportedProjectType(PythonProjectId);
}
@@ -363,5 +223,3 @@ PythonOutputFormatterFactory::PythonOutputFormatterFactory()
} // namespace Internal
} // namespace Python
#include "pythonrunconfiguration.moc"

View File

@@ -31,31 +31,6 @@
namespace Python {
namespace Internal {
class Interpreter;
class PythonRunConfiguration : public ProjectExplorer::RunConfiguration
{
Q_OBJECT
Q_PROPERTY(bool supportsDebugger READ supportsDebugger)
Q_PROPERTY(QString interpreter READ interpreterPath)
Q_PROPERTY(QString mainScript READ mainScript)
Q_PROPERTY(QString arguments READ arguments)
public:
PythonRunConfiguration(ProjectExplorer::Target *target, Utils::Id id);
Interpreter interpreter() const;
QString interpreterPath() const;
void setInterpreter(const Interpreter &interpreterId);
private:
void interpreterChanged();
bool supportsDebugger() const;
QString mainScript() const;
QString arguments() const;
};
class PythonRunConfigurationFactory : public ProjectExplorer::RunConfigurationFactory
{
public:

View File

@@ -50,11 +50,38 @@
#include <QTreeView>
#include <QWidget>
using namespace ProjectExplorer;
using namespace Utils;
using namespace Layouting;
namespace Python {
namespace Internal {
using namespace Utils;
using namespace Layouting;
static Interpreter createInterpreter(const FilePath &python,
const QString &defaultName,
bool windowedSuffix = false)
{
Interpreter result;
result.id = QUuid::createUuid().toString();
result.command = python;
QtcProcess pythonProcess;
pythonProcess.setProcessChannelMode(QProcess::MergedChannels);
pythonProcess.setTimeoutS(1);
pythonProcess.setCommand({python, {"--version"}});
pythonProcess.runBlocking();
if (pythonProcess.result() == ProcessResult::FinishedWithSuccess)
result.name = pythonProcess.stdOut().trimmed();
if (result.name.isEmpty())
result.name = defaultName;
if (windowedSuffix)
result.name += " (Windowed)";
QDir pythonDir(python.parentDir().toString());
if (pythonDir.exists() && pythonDir.exists("activate") && pythonDir.cdUp())
result.name += QString(" (%1 Virtual Environment)").arg(pythonDir.dirName());
return result;
}
class InterpreterDetailsWidget : public QWidget
{
@@ -331,36 +358,6 @@ static bool alreadyRegistered(const QList<Interpreter> &pythons, const FilePath
});
}
Interpreter::Interpreter(const FilePath &python, const QString &defaultName, bool windowedSuffix)
: id(QUuid::createUuid().toString())
, command(python)
{
QtcProcess pythonProcess;
pythonProcess.setProcessChannelMode(QProcess::MergedChannels);
pythonProcess.setTimeoutS(1);
pythonProcess.setCommand({python, {"--version"}});
pythonProcess.runBlocking();
if (pythonProcess.result() == ProcessResult::FinishedWithSuccess)
name = pythonProcess.stdOut().trimmed();
if (name.isEmpty())
name = defaultName;
if (windowedSuffix)
name += " (Windowed)";
QDir pythonDir(python.parentDir().toString());
if (pythonDir.exists() && pythonDir.exists("activate") && pythonDir.cdUp())
name += QString(" (%1 Virtual Environment)").arg(pythonDir.dirName());
}
Interpreter::Interpreter(const QString &_id,
const QString &_name,
const FilePath &_command,
bool _autoDetected)
: id(_id)
, name(_name)
, command(_command)
, autoDetected(_autoDetected)
{}
static InterpreterOptionsPage &interpreterOptionsPage()
{
static InterpreterOptionsPage page;
@@ -480,10 +477,10 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
const FilePath &path = FilePath::fromUserInput(regVal.toString());
const FilePath python = path.pathAppended("python").withExecutableSuffix();
if (python.exists() && !alreadyRegistered(pythons, python))
pythons << Interpreter(python, "Python " + versionGroup);
pythons << createInterpreter(python, "Python " + versionGroup);
const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix();
if (pythonw.exists() && !alreadyRegistered(pythons, pythonw))
pythons << Interpreter(pythonw, "Python " + versionGroup, true);
pythons << createInterpreter(pythonw, "Python " + versionGroup, true);
}
pythonRegistry.endGroup();
}
@@ -499,11 +496,11 @@ static void addPythonsFromPath(QList<Interpreter> &pythons)
if (executable.toFileInfo().size() == 0)
continue;
if (executable.exists() && !alreadyRegistered(pythons, executable))
pythons << Interpreter(executable, "Python from Path");
pythons << createInterpreter(executable, "Python from Path");
}
for (const FilePath &executable : env.findAllInPath("pythonw")) {
if (executable.exists() && !alreadyRegistered(pythons, executable))
pythons << Interpreter(executable, "Python from Path", true);
pythons << createInterpreter(executable, "Python from Path", true);
}
} else {
const QStringList filters = {"python",
@@ -515,7 +512,7 @@ static void addPythonsFromPath(QList<Interpreter> &pythons)
for (const QFileInfo &fi : dir.entryInfoList(filters)) {
const FilePath executable = Utils::FilePath::fromFileInfo(fi);
if (executable.exists() && !alreadyRegistered(pythons, executable))
pythons << Interpreter(executable, "Python from Path");
pythons << createInterpreter(executable, "Python from Path");
}
}
}
@@ -601,7 +598,7 @@ QList<Interpreter> PythonSettings::detectPythonVenvs(const FilePath &path)
= Utils::findOrDefault(PythonSettings::interpreters(),
Utils::equal(&Interpreter::command, python));
if (interpreter.command.isEmpty()) {
interpreter = Interpreter(python, defaultName);
interpreter = createInterpreter(python, defaultName);
PythonSettings::addInterpreter(interpreter);
}
result << interpreter;

View File

@@ -25,6 +25,8 @@
#pragma once
#include <projectexplorer/runconfigurationaspects.h>
#include <utils/fileutils.h>
#include <utils/optional.h>
@@ -33,35 +35,14 @@
namespace Python {
namespace Internal {
class Interpreter
{
public:
Interpreter() = default;
Interpreter(const Utils::FilePath &python,
const QString &defaultName,
bool windowedSuffix = false);
Interpreter(const QString &id,
const QString &name,
const Utils::FilePath &command,
bool autoDetected = true);
inline bool operator==(const Interpreter &other) const
{
return id == other.id && name == other.name && command == other.command;
}
QString id = QUuid::createUuid().toString();
QString name;
Utils::FilePath command;
bool autoDetected = true;
};
class PythonSettings : public QObject
{
Q_OBJECT
public:
static void init();
using Interpreter = ProjectExplorer::Interpreter;
static QList<Interpreter> interpreters();
static Interpreter defaultInterpreter();
static Interpreter interpreter(const QString &interpreterId);

View File

@@ -39,6 +39,7 @@
#include <utils/mimeutils.h>
#include <utils/qtcprocess.h>
using namespace ProjectExplorer;
using namespace Utils;
namespace Python {
@@ -51,15 +52,15 @@ FilePath detectPython(const FilePath &documentPath)
PythonProject *project = documentPath.isEmpty()
? nullptr
: qobject_cast<PythonProject *>(
ProjectExplorer::SessionManager::projectForFile(documentPath));
SessionManager::projectForFile(documentPath));
if (!project)
project = qobject_cast<PythonProject *>(ProjectExplorer::SessionManager::startupProject());
project = qobject_cast<PythonProject *>(SessionManager::startupProject());
if (project) {
if (auto target = project->activeTarget()) {
if (auto runConfig = qobject_cast<PythonRunConfiguration *>(
target->activeRunConfiguration())) {
python = runConfig->interpreter().command;
if (auto runConfig = target->activeRunConfiguration()) {
if (auto interpreter = runConfig->aspect<InterpreterAspect>())
python = interpreter->currentInterpreter().command;
}
}
}
@@ -99,7 +100,7 @@ void openPythonRepl(QObject *parent, const FilePath &file, ReplType type)
{
static const auto workingDir = [](const FilePath &file) {
if (file.isEmpty()) {
if (ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject())
if (Project *project = SessionManager::startupProject())
return project->projectDirectory();
return FilePath::fromString(QDir::currentPath());
}
@@ -147,7 +148,7 @@ QString pythonName(const FilePath &pythonPath)
PythonProject *pythonProjectForFile(const FilePath &pythonFile)
{
for (ProjectExplorer::Project *project : ProjectExplorer::SessionManager::projects()) {
for (Project *project : SessionManager::projects()) {
if (auto pythonProject = qobject_cast<PythonProject *>(project)) {
if (pythonProject->isKnownFile(pythonFile))
return pythonProject;