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; Utils::FilePaths projectSourceFiles;
// Used by Script debugging // Used by Script debugging
QString interpreter; Utils::FilePath interpreter;
QString mainScript; Utils::FilePath mainScript;
// Used by AttachCrashedExternal. // Used by AttachCrashedExternal.
QString crashParameter; QString crashParameter;

View File

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

View File

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

View File

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

View File

@@ -32,6 +32,8 @@
#include "runconfiguration.h" #include "runconfiguration.h"
#include "target.h" #include "target.h"
#include <coreplugin/icore.h>
#include <utils/detailsbutton.h> #include <utils/detailsbutton.h>
#include <utils/fancylineedit.h> #include <utils/fancylineedit.h>
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
@@ -40,11 +42,13 @@
#include <utils/utilsicons.h> #include <utils/utilsicons.h>
#include <QCheckBox> #include <QCheckBox>
#include <QComboBox>
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
#include <QFormLayout> #include <QFormLayout>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QToolButton> #include <QToolButton>
#include <QPushButton>
using namespace Utils; using namespace Utils;
@@ -764,4 +768,109 @@ RunAsRootAspect::RunAsRootAspect()
setLabel(tr("Run as root user"), LabelPlacement::AtCheckBox); 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 } // namespace ProjectExplorer

View File

@@ -33,6 +33,7 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QCheckBox; class QCheckBox;
class QComboBox;
class QPlainTextEdit; class QPlainTextEdit;
class QToolButton; class QToolButton;
QT_END_NAMESPACE QT_END_NAMESPACE
@@ -214,4 +215,61 @@ public:
SymbolFileAspect() = default; 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 } // namespace ProjectExplorer

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -135,220 +135,80 @@ private:
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
class InterpreterAspect : public BaseAspect class PythonRunConfiguration : public RunConfiguration
{ {
Q_OBJECT
public: 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; connect(interpreterAspect, &InterpreterAspect::changed, this, [this, interpreterAspect] {
void updateInterpreters(const QList<Interpreter> &interpreters); using namespace LanguageClient;
void setDefaultInterpreter(const Interpreter &interpreter) { m_defaultId = interpreter.id; } const FilePath python = interpreterAspect->currentInterpreter().command;
void setCurrentInterpreter(const Interpreter &interpreter);
void fromMap(const QVariantMap &) override; for (FilePath &file : project()->files(Project::AllFiles)) {
void toMap(QVariantMap &) const override; if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) {
void addToLayout(LayoutBuilder &builder) override; if (document->mimeType() == Constants::C_PY_MIMETYPE) {
PyLSConfigureAssistant::instance()->openDocumentWithPython(python, document);
private: PySideInstaller::instance()->checkPySideInstallation(python, document);
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);
} }
} });
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() PythonRunConfigurationFactory::PythonRunConfigurationFactory()
{ {
registerRunConfiguration<PythonRunConfiguration>("PythonEditor.RunConfiguration."); registerRunConfiguration<PythonRunConfiguration>(Constants::C_PYTHONRUNCONFIGURATION_ID);
addSupportedProjectType(PythonProjectId); addSupportedProjectType(PythonProjectId);
} }
@@ -363,5 +223,3 @@ PythonOutputFormatterFactory::PythonOutputFormatterFactory()
} // namespace Internal } // namespace Internal
} // namespace Python } // namespace Python
#include "pythonrunconfiguration.moc"

View File

@@ -31,31 +31,6 @@
namespace Python { namespace Python {
namespace Internal { 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 class PythonRunConfigurationFactory : public ProjectExplorer::RunConfigurationFactory
{ {
public: public:

View File

@@ -50,11 +50,38 @@
#include <QTreeView> #include <QTreeView>
#include <QWidget> #include <QWidget>
using namespace ProjectExplorer;
using namespace Utils;
using namespace Layouting;
namespace Python { namespace Python {
namespace Internal { namespace Internal {
using namespace Utils; static Interpreter createInterpreter(const FilePath &python,
using namespace Layouting; 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 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 &interpreterOptionsPage()
{ {
static InterpreterOptionsPage page; static InterpreterOptionsPage page;
@@ -480,10 +477,10 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
const FilePath &path = FilePath::fromUserInput(regVal.toString()); const FilePath &path = FilePath::fromUserInput(regVal.toString());
const FilePath python = path.pathAppended("python").withExecutableSuffix(); const FilePath python = path.pathAppended("python").withExecutableSuffix();
if (python.exists() && !alreadyRegistered(pythons, python)) if (python.exists() && !alreadyRegistered(pythons, python))
pythons << Interpreter(python, "Python " + versionGroup); pythons << createInterpreter(python, "Python " + versionGroup);
const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix(); const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix();
if (pythonw.exists() && !alreadyRegistered(pythons, pythonw)) if (pythonw.exists() && !alreadyRegistered(pythons, pythonw))
pythons << Interpreter(pythonw, "Python " + versionGroup, true); pythons << createInterpreter(pythonw, "Python " + versionGroup, true);
} }
pythonRegistry.endGroup(); pythonRegistry.endGroup();
} }
@@ -499,11 +496,11 @@ static void addPythonsFromPath(QList<Interpreter> &pythons)
if (executable.toFileInfo().size() == 0) if (executable.toFileInfo().size() == 0)
continue; continue;
if (executable.exists() && !alreadyRegistered(pythons, executable)) 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")) { for (const FilePath &executable : env.findAllInPath("pythonw")) {
if (executable.exists() && !alreadyRegistered(pythons, executable)) if (executable.exists() && !alreadyRegistered(pythons, executable))
pythons << Interpreter(executable, "Python from Path", true); pythons << createInterpreter(executable, "Python from Path", true);
} }
} else { } else {
const QStringList filters = {"python", const QStringList filters = {"python",
@@ -515,7 +512,7 @@ static void addPythonsFromPath(QList<Interpreter> &pythons)
for (const QFileInfo &fi : dir.entryInfoList(filters)) { for (const QFileInfo &fi : dir.entryInfoList(filters)) {
const FilePath executable = Utils::FilePath::fromFileInfo(fi); const FilePath executable = Utils::FilePath::fromFileInfo(fi);
if (executable.exists() && !alreadyRegistered(pythons, executable)) 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::findOrDefault(PythonSettings::interpreters(),
Utils::equal(&Interpreter::command, python)); Utils::equal(&Interpreter::command, python));
if (interpreter.command.isEmpty()) { if (interpreter.command.isEmpty()) {
interpreter = Interpreter(python, defaultName); interpreter = createInterpreter(python, defaultName);
PythonSettings::addInterpreter(interpreter); PythonSettings::addInterpreter(interpreter);
} }
result << interpreter; result << interpreter;

View File

@@ -25,6 +25,8 @@
#pragma once #pragma once
#include <projectexplorer/runconfigurationaspects.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/optional.h> #include <utils/optional.h>
@@ -33,35 +35,14 @@
namespace Python { namespace Python {
namespace Internal { 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 class PythonSettings : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
static void init(); static void init();
using Interpreter = ProjectExplorer::Interpreter;
static QList<Interpreter> interpreters(); static QList<Interpreter> interpreters();
static Interpreter defaultInterpreter(); static Interpreter defaultInterpreter();
static Interpreter interpreter(const QString &interpreterId); static Interpreter interpreter(const QString &interpreterId);

View File

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