Initial Python bindings pass for ProjectManager
Adds dependency on pybind and interface to call o3de.py functions from c++
This commit is contained in:
@@ -37,6 +37,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded);
|
||||
|
||||
QString m_path;
|
||||
QString m_name;
|
||||
QString m_displayName;
|
||||
AZ::Uuid m_uuid;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ProjectInfo.h"
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
ProjectInfo::ProjectInfo(const QString& path, const QString& projectName, const QString& productName, const AZ::Uuid projectId)
|
||||
: m_path(path)
|
||||
, m_projectName(projectName)
|
||||
, m_productName(productName)
|
||||
, m_projectId(projectId)
|
||||
{
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzCore/Math/Uuid.h>
|
||||
#include <QString>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class ProjectInfo
|
||||
{
|
||||
public:
|
||||
ProjectInfo() = default;
|
||||
ProjectInfo(const QString& path, const QString& projectName, const QString& productName, const AZ::Uuid projectId);
|
||||
|
||||
// from o3de_manifest.json and o3de_projects.json
|
||||
QString m_path;
|
||||
|
||||
// from project.json
|
||||
QString m_projectName;
|
||||
QString m_productName;
|
||||
AZ::Uuid m_projectId;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -28,6 +28,8 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_pythonBindings = AZStd::make_unique<PythonBindings>(engineRootPath);
|
||||
|
||||
ConnectSlotsAndSignals();
|
||||
|
||||
QDir rootDir = QString::fromUtf8(engineRootPath.Native().data(), aznumeric_cast<int>(engineRootPath.Native().size()));
|
||||
@@ -44,6 +46,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
ProjectManagerWindow::~ProjectManagerWindow()
|
||||
{
|
||||
m_pythonBindings.reset();
|
||||
}
|
||||
|
||||
void ProjectManagerWindow::BuildScreens()
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include <QMainWindow>
|
||||
#include <QStackedWidget>
|
||||
|
||||
#include <AzCore/IO/Path/Path_fwd.h>
|
||||
#include <PythonBindings.h>
|
||||
#endif
|
||||
|
||||
namespace Ui
|
||||
@@ -52,6 +52,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
private:
|
||||
QScopedPointer<Ui::ProjectManagerWindowClass> m_ui;
|
||||
AZStd::unique_ptr<PythonBindings> m_pythonBindings;
|
||||
};
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#include <Source/ui_ProjectsHome.h>
|
||||
|
||||
#include <PythonBindingsInterface.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
ProjectsHome::ProjectsHome(ProjectManagerWindow* window)
|
||||
@@ -23,6 +25,9 @@ namespace O3DE::ProjectManager
|
||||
m_ui->setupUi(this);
|
||||
|
||||
ConnectSlotsAndSignals();
|
||||
|
||||
// example of how to get the current project name
|
||||
ProjectInfo currentProject = PythonBindingsInterface::Get()->GetCurrentProject();
|
||||
}
|
||||
|
||||
ProjectsHome::~ProjectsHome()
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <PythonBindings.h>
|
||||
|
||||
// Qt defines slots, which interferes with the use here.
|
||||
#pragma push_macro("slots")
|
||||
#undef slots
|
||||
#include <Python.h>
|
||||
#include <pybind11/functional.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/embed.h>
|
||||
#include <pybind11/eval.h>
|
||||
#pragma pop_macro("slots")
|
||||
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzCore/StringFunc/StringFunc.h>
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
bool InsertPythonLibraryPath(
|
||||
AZStd::unordered_set<AZStd::string>& paths, const char* pythonPackage, const char* engineRoot, const char* subPath)
|
||||
{
|
||||
// append lib path to Python paths
|
||||
AZ::IO::FixedMaxPath libPath = engineRoot;
|
||||
libPath /= AZ::IO::FixedMaxPathString::format(subPath, pythonPackage);
|
||||
libPath = libPath.LexicallyNormal();
|
||||
if (AZ::IO::SystemFile::Exists(libPath.c_str()))
|
||||
{
|
||||
paths.insert(libPath.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ_Warning("python", false, "Python library path should exist. path:%s", libPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implemented in each different platform's PAL implentation files, as it differs per platform.
|
||||
AZStd::string GetPythonHomePath(const char* pythonPackage, const char* engineRoot);
|
||||
|
||||
} // namespace Platform
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
PythonBindings::PythonBindings(const AZ::IO::PathView& enginePath)
|
||||
: m_enginePath(enginePath)
|
||||
{
|
||||
StartPython();
|
||||
}
|
||||
|
||||
PythonBindings::~PythonBindings()
|
||||
{
|
||||
StopPython();
|
||||
}
|
||||
|
||||
bool PythonBindings::StartPython()
|
||||
{
|
||||
if (Py_IsInitialized())
|
||||
{
|
||||
AZ_Warning("python", false, "Python is already active");
|
||||
return false;
|
||||
}
|
||||
|
||||
// set PYTHON_HOME
|
||||
AZStd::string pyBasePath = Platform::GetPythonHomePath(PY_PACKAGE, m_enginePath.c_str());
|
||||
if (!AZ::IO::SystemFile::Exists(pyBasePath.c_str()))
|
||||
{
|
||||
AZ_Warning("python", false, "Python home path must exist. path:%s", pyBasePath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
AZStd::wstring pyHomePath;
|
||||
AZStd::to_wstring(pyHomePath, pyBasePath);
|
||||
Py_SetPythonHome(pyHomePath.c_str());
|
||||
|
||||
// display basic Python information
|
||||
AZ_TracePrintf("python", "Py_GetVersion=%s \n", Py_GetVersion());
|
||||
AZ_TracePrintf("python", "Py_GetPath=%ls \n", Py_GetPath());
|
||||
AZ_TracePrintf("python", "Py_GetExecPrefix=%ls \n", Py_GetExecPrefix());
|
||||
AZ_TracePrintf("python", "Py_GetProgramFullPath=%ls \n", Py_GetProgramFullPath());
|
||||
|
||||
try
|
||||
{
|
||||
// ignore system location for sites site-packages
|
||||
Py_IsolatedFlag = 1; // -I - Also sets Py_NoUserSiteDirectory. If removed PyNoUserSiteDirectory should be set.
|
||||
Py_IgnoreEnvironmentFlag = 1; // -E
|
||||
|
||||
const bool initializeSignalHandlers = true;
|
||||
pybind11::initialize_interpreter(initializeSignalHandlers);
|
||||
|
||||
// Acquire GIL before calling Python code
|
||||
AZStd::lock_guard<decltype(m_lock)> lock(m_lock);
|
||||
pybind11::gil_scoped_acquire acquire;
|
||||
|
||||
// Setup sys.path
|
||||
int result = PyRun_SimpleString("import sys");
|
||||
AZ_Warning("ProjectManagerWindow", result != -1, "Import sys failed");
|
||||
result = PyRun_SimpleString(AZStd::string::format("sys.path.append('%s')", m_enginePath.c_str()).c_str());
|
||||
AZ_Warning("ProjectManagerWindow", result != -1, "Append to sys path failed");
|
||||
|
||||
return result == 0 && !PyErr_Occurred();
|
||||
} catch ([[maybe_unused]] const std::exception& e)
|
||||
{
|
||||
AZ_Warning("python", false, "Py_Initialize() failed with %s", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PythonBindings::StopPython()
|
||||
{
|
||||
if (Py_IsInitialized())
|
||||
{
|
||||
pybind11::finalize_interpreter();
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("python", false, "Did not finalize since Py_IsInitialized() was false");
|
||||
}
|
||||
return !PyErr_Occurred();
|
||||
}
|
||||
|
||||
void PythonBindings::ExecuteWithLock(AZStd::function<void()> executionCallback)
|
||||
{
|
||||
AZStd::lock_guard<decltype(m_lock)> lock(m_lock);
|
||||
pybind11::gil_scoped_release release;
|
||||
pybind11::gil_scoped_acquire acquire;
|
||||
executionCallback();
|
||||
}
|
||||
|
||||
ProjectInfo PythonBindings::GetCurrentProject()
|
||||
{
|
||||
ProjectInfo project;
|
||||
|
||||
ExecuteWithLock([&] {
|
||||
auto currentProjectTool = pybind11::module::import("cmake.Tools.current_project");
|
||||
auto getCurrentProject = currentProjectTool.attr("get_current_project");
|
||||
auto currentProject = getCurrentProject(m_enginePath.c_str());
|
||||
|
||||
project.m_path = currentProject.cast<std::string>().c_str();
|
||||
});
|
||||
|
||||
return project;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/std/parallel/semaphore.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class PythonBindings
|
||||
: public PythonBindingsInterface::Registrar
|
||||
{
|
||||
public:
|
||||
PythonBindings() = default;
|
||||
PythonBindings(const AZ::IO::PathView& enginePath);
|
||||
~PythonBindings() override;
|
||||
|
||||
// PythonBindings overrides
|
||||
ProjectInfo GetCurrentProject() override;
|
||||
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(PythonBindings);
|
||||
|
||||
void ExecuteWithLock(AZStd::function<void()> executionCallback);
|
||||
bool StartPython();
|
||||
bool StopPython();
|
||||
|
||||
AZ::IO::FixedMaxPath m_enginePath;
|
||||
AZStd::recursive_mutex m_lock;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
|
||||
#include <GemCatalog/GemInfo.h>
|
||||
#include <ProjectInfo.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
//! Interface used to interact with the o3de cli python functions
|
||||
class IPythonBindings
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(O3DE::ProjectManager::IPythonBindings, "{C2B72CA4-56A9-4601-A584-3B40E83AA17C}");
|
||||
AZ_DISABLE_COPY_MOVE(IPythonBindings);
|
||||
|
||||
IPythonBindings() = default;
|
||||
virtual ~IPythonBindings() = default;
|
||||
|
||||
//! Get the current project
|
||||
virtual ProjectInfo GetCurrentProject() = 0;
|
||||
};
|
||||
|
||||
using PythonBindingsInterface = AZ::Interface<IPythonBindings>;
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -35,21 +35,29 @@ int main(int argc, char* argv[])
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware);
|
||||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
// Need to use settings registry to get EngineRootFolder
|
||||
AZ::IO::FixedMaxPath engineRootPath;
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
|
||||
int runSuccess = 0;
|
||||
{
|
||||
AZ::ComponentApplication componentApplication;
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
QApplication app(argc, argv);
|
||||
|
||||
// Need to use settings registry to get EngineRootFolder
|
||||
AZ::IO::FixedMaxPath engineRootPath;
|
||||
{
|
||||
AZ::ComponentApplication componentApplication;
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
}
|
||||
|
||||
AzQtComponents::StyleManager styleManager(&app);
|
||||
styleManager.initialize(&app, engineRootPath);
|
||||
|
||||
O3DE::ProjectManager::ProjectManagerWindow window(nullptr, engineRootPath);
|
||||
window.show();
|
||||
|
||||
runSuccess = app.exec();
|
||||
}
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
|
||||
|
||||
AzQtComponents::StyleManager styleManager(&app);
|
||||
styleManager.initialize(&app, engineRootPath);
|
||||
|
||||
O3DE::ProjectManager::ProjectManagerWindow window(nullptr, engineRootPath);
|
||||
window.show();
|
||||
|
||||
return app.exec();
|
||||
return runSuccess;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user