Run get_python script when Python fails to load (#4482)

Signed-off-by: AMZN-alexpete <26804013+AMZN-alexpete@users.noreply.github.com>
monroegm-disable-blank-issue-2
Alex Peterson 4 years ago committed by GitHub
parent bf7ae12402
commit df419a0990
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -11,5 +11,6 @@
namespace O3DE::ProjectManager
{
const QString ProjectBuildPathPostfix = ProjectBuildDirectoryName + "/linux";
const QString GetPythonScriptPath = "python/get_python.sh";
} // namespace O3DE::ProjectManager

@ -86,5 +86,13 @@ namespace O3DE::ProjectManager
return AZ::Success();
}
AZ::Outcome<QString, QString> RunGetPythonScript(const QString& engineRoot)
{
return ExecuteCommandResultModalDialog(
QString("%1/python/get_python.sh").arg(engineRoot),
{},
QProcessEnvironment::systemEnvironment(),
QObject::tr("Running get_python script..."));
}
} // namespace ProjectUtils
} // namespace O3DE::ProjectManager

@ -11,5 +11,6 @@
namespace O3DE::ProjectManager
{
const QString ProjectBuildPathPostfix = ProjectBuildDirectoryName + "/mac_xcode";
const QString GetPythonScriptPath = "python/get_python.sh";
} // namespace O3DE::ProjectManager

@ -9,6 +9,7 @@
#include <QProcess>
#include <QStandardPaths>
#include <QDir>
namespace O3DE::ProjectManager
{
@ -94,5 +95,14 @@ namespace O3DE::ProjectManager
return AZ::Success();
}
AZ::Outcome<QString, QString> RunGetPythonScript(const QString& engineRoot)
{
return ExecuteCommandResultModalDialog(
QString("%1/python/get_python.sh").arg(engineRoot),
{},
QProcessEnvironment::systemEnvironment(),
QObject::tr("Running get_python script..."));
}
} // namespace ProjectUtils
} // namespace O3DE::ProjectManager

@ -10,5 +10,6 @@
namespace O3DE::ProjectManager
{
const QString ProjectBuildPathPostfix = ProjectBuildDirectoryName + "/windows_vs2019";
const QString GetPythonScriptPath = "python/get_python.bat";
} // namespace O3DE::ProjectManager

@ -130,5 +130,14 @@ namespace O3DE::ProjectManager
return AZ::Success();
}
AZ::Outcome<QString, QString> RunGetPythonScript(const QString& engineRoot)
{
const QString batPath = QString("%1/python/get_python.bat").arg(engineRoot);
return ExecuteCommandResultModalDialog(
"cmd.exe",
QStringList{"/c", batPath},
QProcessEnvironment::systemEnvironment(),
QObject::tr("Running get_python script..."));
}
} // namespace ProjectUtils
} // namespace O3DE::ProjectManager

@ -68,17 +68,46 @@ namespace O3DE::ProjectManager
}
m_pythonBindings = AZStd::make_unique<PythonBindings>(GetEngineRoot());
if (!m_pythonBindings || !m_pythonBindings->PythonStarted())
AZ_Assert(m_pythonBindings, "Failed to create PythonBindings");
if (!m_pythonBindings->PythonStarted())
{
if (interactive)
if (!interactive)
{
QMessageBox::critical(nullptr, QObject::tr("Failed to start Python"),
QObject::tr("This tool requires an O3DE engine with a Python runtime, "
"but either Python is missing or mis-configured. Please rename "
"your python/runtime folder to python/runtime_bak, then run "
"python/get_python.bat to restore the Python runtime folder."));
return false;
}
int result = QMessageBox::warning(nullptr, QObject::tr("Failed to start Python"),
QObject::tr("This tool requires an O3DE engine with a Python runtime, "
"but either Python is missing or mis-configured.<br><br>Press 'OK' to "
"run the %1 script automatically, or 'Cancel' "
" if you want to manually resolve the issue by renaming your "
" python/runtime folder and running %1 yourself.")
.arg(GetPythonScriptPath),
QMessageBox::Cancel, QMessageBox::Ok);
if (result == QMessageBox::Ok)
{
auto getPythonResult = ProjectUtils::RunGetPythonScript(GetEngineRoot());
if (!getPythonResult.IsSuccess())
{
QMessageBox::critical(
nullptr, QObject::tr("Failed to run %1 script").arg(GetPythonScriptPath),
QObject::tr("The %1 script failed, was canceled, or could not be run. "
"Please rename your python/runtime folder and then run "
"<pre>%1</pre>").arg(GetPythonScriptPath));
}
else if (!m_pythonBindings->StartPython())
{
QMessageBox::critical(
nullptr, QObject::tr("Failed to start Python"),
QObject::tr("Failed to start Python after running %1")
.arg(GetPythonScriptPath));
}
}
if (!m_pythonBindings->PythonStarted())
{
return false;
}
return false;
}
const AZ::CommandLine* commandLine = GetCommandLine();

@ -18,6 +18,7 @@ namespace O3DE::ProjectManager
static const QString ProjectBuildDirectoryName = "build";
extern const QString ProjectBuildPathPostfix;
extern const QString GetPythonScriptPath;
static const QString ProjectBuildPathCmakeFiles = "CMakeFiles";
static const QString ProjectBuildErrorLogName = "CMakeProjectBuildError.log";
static const QString ProjectCacheDirectoryName = "Cache";

@ -23,6 +23,11 @@
#include <QProgressDialog>
#include <QSpacerItem>
#include <QGridLayout>
#include <QTextEdit>
#include <QByteArray>
#include <QScrollBar>
#include <QProgressBar>
#include <QLabel>
#include <AzCore/std/chrono/chrono.h>
@ -512,6 +517,97 @@ namespace O3DE::ProjectManager
return ProjectManagerScreen::Invalid;
}
AZ::Outcome<QString, QString> ExecuteCommandResultModalDialog(
const QString& cmd,
const QStringList& arguments,
const QProcessEnvironment& processEnv,
const QString& title)
{
QString resultOutput;
QProcess execProcess;
execProcess.setProcessEnvironment(processEnv);
execProcess.setProcessChannelMode(QProcess::MergedChannels);
QProgressDialog dialog(title, QObject::tr("Cancel"), /*minimum=*/0, /*maximum=*/0);
dialog.setMinimumWidth(500);
dialog.setAutoClose(false);
QProgressBar* bar = new QProgressBar(&dialog);
bar->setTextVisible(false);
bar->setMaximum(0); // infinite
dialog.setBar(bar);
QLabel* progressLabel = new QLabel(&dialog);
QVBoxLayout* layout = new QVBoxLayout();
// pre-fill the field with the title and command
const QString commandOutput = QString("%1<br>%2 %3<br>").arg(title).arg(cmd).arg(arguments.join(' '));
// replace the label with a scrollable text edit
QTextEdit* detailTextEdit = new QTextEdit(commandOutput, &dialog);
detailTextEdit->setReadOnly(true);
layout->addWidget(detailTextEdit);
layout->setMargin(0);
progressLabel->setLayout(layout);
progressLabel->setMinimumHeight(150);
dialog.setLabel(progressLabel);
auto readConnection = QObject::connect(&execProcess, &QProcess::readyReadStandardOutput,
[&]()
{
QScrollBar* scrollBar = detailTextEdit->verticalScrollBar();
bool autoScroll = scrollBar->value() == scrollBar->maximum();
QString output = execProcess.readAllStandardOutput();
detailTextEdit->append(output);
resultOutput.append(output);
if (autoScroll)
{
scrollBar->setValue(scrollBar->maximum());
}
});
auto exitConnection = QObject::connect(&execProcess,
QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
[&](int exitCode, [[maybe_unused]] QProcess::ExitStatus exitStatus)
{
QScrollBar* scrollBar = detailTextEdit->verticalScrollBar();
dialog.setMaximum(100);
dialog.setValue(dialog.maximum());
if (exitCode == 0 && scrollBar->value() == scrollBar->maximum())
{
dialog.close();
}
else
{
// keep the dialog open so the user can look at the output
dialog.setCancelButtonText(QObject::tr("Continue"));
}
});
execProcess.start(cmd, arguments);
dialog.exec();
QObject::disconnect(readConnection);
QObject::disconnect(exitConnection);
if (execProcess.state() == QProcess::Running)
{
execProcess.kill();
return AZ::Failure(QObject::tr("Process for command '%1' was canceled").arg(cmd));
}
int resultCode = execProcess.exitCode();
if (resultCode != 0)
{
return AZ::Failure(QObject::tr("Process for command '%1' failed (result code %2").arg(cmd).arg(resultCode));
}
return AZ::Success(resultOutput);
}
AZ::Outcome<QString, QString> ExecuteCommandResult(
const QString& cmd,
const QStringList& arguments,

@ -35,15 +35,39 @@ namespace O3DE::ProjectManager
ProjectManagerScreen GetProjectManagerScreen(const QString& screen);
/**
* Execute a console command and return the result.
* @param cmd the command
* @param arguments the command argument list
* @param processEnv the environment
* @param commandTimeoutSeconds the amount of time in seconds to let the command run before terminating it
* @return AZ::Outcome with the command result on success
*/
AZ::Outcome<QString, QString> ExecuteCommandResult(
const QString& cmd,
const QStringList& arguments,
const QProcessEnvironment& processEnv,
int commandTimeoutSeconds = ProjectCommandLineTimeoutSeconds);
/**
* Execute a console command, display the progress in a modal dialog and return the result.
* @param cmd the command
* @param arguments the command argument list
* @param processEnv the environment
* @param commandTimeoutSeconds the amount of time in seconds to let the command run before terminating it
* @return AZ::Outcome with the command result on success
*/
AZ::Outcome<QString, QString> ExecuteCommandResultModalDialog(
const QString& cmd,
const QStringList& arguments,
const QProcessEnvironment& processEnv,
const QString& title);
AZ::Outcome<QProcessEnvironment, QString> GetCommandLineProcessEnvironment();
AZ::Outcome<QString, QString> GetProjectBuildPath(const QString& projectPath);
AZ::Outcome<void, QString> OpenCMakeGUI(const QString& projectPath);
AZ::Outcome<QString, QString> RunGetPythonScript(const QString& enginePath);
} // namespace ProjectUtils
} // namespace O3DE::ProjectManager

@ -246,9 +246,11 @@ namespace O3DE::ProjectManager
if (Py_IsInitialized())
{
AZ_Warning("python", false, "Python is already active");
return false;
return m_pythonStarted;
}
m_pythonStarted = false;
// set PYTHON_HOME
AZStd::string pyBasePath = Platform::GetPythonHomePath(PY_PACKAGE, m_enginePath.c_str());
if (!AZ::IO::SystemFile::Exists(pyBasePath.c_str()))
@ -304,7 +306,8 @@ namespace O3DE::ProjectManager
// make sure the engine is registered
RegisterThisEngine();
return !PyErr_Occurred();
m_pythonStarted = !PyErr_Occurred();
return m_pythonStarted;
}
catch ([[maybe_unused]] const std::exception& e)
{

@ -31,6 +31,7 @@ namespace O3DE::ProjectManager
// PythonBindings overrides
bool PythonStarted() override;
bool StartPython() override;
// Engine
AZ::Outcome<EngineInfo> GetEngineInfo() override;
@ -70,7 +71,6 @@ namespace O3DE::ProjectManager
ProjectInfo ProjectInfoFromPath(pybind11::handle path);
ProjectTemplateInfo ProjectTemplateInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath);
bool RegisterThisEngine();
bool StartPython();
bool StopPython();

@ -38,6 +38,14 @@ namespace O3DE::ProjectManager
*/
virtual bool PythonStarted() = 0;
/**
* Attempt to start Python. Normally, Python is started when the bindings are created,
* but this method allows you to attempt to retry starting Python in case the configuration
* has changed.
* @return true if Python was started successfully, false on failure
*/
virtual bool StartPython() = 0;
// Engine
/**

Loading…
Cancel
Save