mirror of
https://github.com/ModOrganizer2/modorganizer-plugin_python.git
synced 2026-07-27 14:03:33 -07:00
Continue implementing plugins.
This commit is contained in:
@@ -4,58 +4,58 @@
|
||||
<context>
|
||||
<name>ProxyPython</name>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="118"/>
|
||||
<location filename="proxypython.cpp" line="120"/>
|
||||
<source>Python Proxy</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="128"/>
|
||||
<location filename="proxypython.cpp" line="130"/>
|
||||
<source>Proxy Plugin to allow plugins written in python to be loaded</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="204"/>
|
||||
<location filename="proxypython.cpp" line="206"/>
|
||||
<source>ModOrganizer path contains a semicolon</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="206"/>
|
||||
<location filename="proxypython.cpp" line="208"/>
|
||||
<source>Python DLL not found</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="208"/>
|
||||
<location filename="proxypython.cpp" line="210"/>
|
||||
<source>Invalid Python DLL</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="210"/>
|
||||
<location filename="proxypython.cpp" line="212"/>
|
||||
<source>Initializing Python failed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="212"/>
|
||||
<location filename="proxypython.cpp" line="234"/>
|
||||
<location filename="proxypython.cpp" line="214"/>
|
||||
<location filename="proxypython.cpp" line="244"/>
|
||||
<source>invalid problem key %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="221"/>
|
||||
<location filename="proxypython.cpp" line="222"/>
|
||||
<source>The path to Mod Organizer (%1) contains a semicolon. <br>While this is legal on NTFS drives, many softwares do not handle it correctly.<br>Unfortunately MO depends on libraries that seem to fall into that group.<br>As a result the python plugin cannot be loaded, and the only solution we canoffer is to remove the semicolon or move MO to a path without a semicolon.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="227"/>
|
||||
<location filename="proxypython.cpp" line="233"/>
|
||||
<source>The Python plugin DLL was not found, maybe your antivirus deleted it. Re-installing MO2 might fix the problem.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="229"/>
|
||||
<location filename="proxypython.cpp" line="236"/>
|
||||
<source>The Python plugin DLL is invalid, maybe your antivirus is blocking it. Re-installing MO2 and adding exclusions for it to your AV might fix the problem.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="232"/>
|
||||
<location filename="proxypython.cpp" line="241"/>
|
||||
<source>The initialization of the Python plugin DLL failed, unfortunately without any details.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
||||
+152
-144
@@ -17,229 +17,237 @@ You should have received a copy of the GNU General Public License
|
||||
along with python proxy plugin. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "proxypython.h"
|
||||
#include "log.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QDirIterator>
|
||||
#include <QMessageBox>
|
||||
#include <QWidget>
|
||||
#include <QtPlugin>
|
||||
#include <utility.h>
|
||||
#include <versioninfo.h>
|
||||
#include <QtPlugin>
|
||||
#include <QDirIterator>
|
||||
#include <QWidget>
|
||||
#include <QMessageBox>
|
||||
#include <QCoreApplication>
|
||||
#include "log.h"
|
||||
|
||||
using namespace MOBase;
|
||||
|
||||
ProxyPython::ProxyPython()
|
||||
: m_MOInfo{ nullptr },
|
||||
m_RunnerLib{ nullptr },
|
||||
m_Runner{ nullptr },
|
||||
m_LoadFailure(FailureType::NONE)
|
||||
: m_MOInfo{nullptr}, m_RunnerLib{nullptr}, m_Runner{nullptr},
|
||||
m_LoadFailure(FailureType::NONE)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool ProxyPython::init(IOrganizer *moInfo)
|
||||
bool ProxyPython::init(IOrganizer* moInfo)
|
||||
{
|
||||
using CreatePythonRunner_func = IPythonRunner * (*)();
|
||||
using CreatePythonRunner_func = IPythonRunner* (*)();
|
||||
|
||||
m_MOInfo = moInfo;
|
||||
m_MOInfo = moInfo;
|
||||
|
||||
if (m_MOInfo && !m_MOInfo->isPluginEnabled(this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (QCoreApplication::applicationDirPath().contains(';')) {
|
||||
m_LoadFailure = FailureType::SEMICOLON;
|
||||
return true;
|
||||
}
|
||||
|
||||
// load the pythonrunner library
|
||||
m_RunnerLib = ::LoadLibraryW(QDir::toNativeSeparators(
|
||||
IOrganizer::getPluginDataPath() + "/pythonRunner.dll").toStdWString().c_str());
|
||||
|
||||
if (!m_RunnerLib) {
|
||||
DWORD error = ::GetLastError();
|
||||
log::error("failed to load python runner ({}): {}", qUtf8Printable(windowsErrorString(error)));
|
||||
if (error == ERROR_MOD_NOT_FOUND) {
|
||||
m_LoadFailure = FailureType::DLL_NOT_FOUND;
|
||||
if (m_MOInfo && !m_MOInfo->isPluginEnabled(this)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
m_LoadFailure = FailureType::INVALID_DLL;
|
||||
|
||||
if (QCoreApplication::applicationDirPath().contains(';')) {
|
||||
m_LoadFailure = FailureType::SEMICOLON;
|
||||
return true;
|
||||
}
|
||||
|
||||
// load the pythonrunner library
|
||||
m_RunnerLib = ::LoadLibraryW(
|
||||
QDir::toNativeSeparators(IOrganizer::getPluginDataPath() + "/pythonRunner.dll")
|
||||
.toStdWString()
|
||||
.c_str());
|
||||
|
||||
if (!m_RunnerLib) {
|
||||
DWORD error = ::GetLastError();
|
||||
log::error("failed to load python runner ({}): {}",
|
||||
qUtf8Printable(windowsErrorString(error)));
|
||||
if (error == ERROR_MOD_NOT_FOUND) {
|
||||
m_LoadFailure = FailureType::DLL_NOT_FOUND;
|
||||
}
|
||||
else {
|
||||
m_LoadFailure = FailureType::INVALID_DLL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const CreatePythonRunner_func createPythonRunner =
|
||||
(CreatePythonRunner_func)::GetProcAddress(m_RunnerLib, "CreatePythonRunner");
|
||||
if (!createPythonRunner) {
|
||||
m_LoadFailure = FailureType::INVALID_DLL;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_MOInfo && m_MOInfo->persistent(name(), "tryInit", false).toBool()) {
|
||||
// m_LoadFailure = FailureType::INITIALIZATION;
|
||||
// if (QMessageBox::question(parentWidget(), tr("Python Initialization failed"),
|
||||
// tr("On a previous start the Python Plugin failed to initialize.\n"
|
||||
// "Do you want to try initializing python again (at the risk of another
|
||||
// crash)?\n" "Suggestion: Select \"no\", and click the warning sign for
|
||||
// further help. Afterwards you have to re-enable the python plugin."),
|
||||
// QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) {
|
||||
|
||||
// // we force enabled here (note: this is a persistent settings since MO2 2.4
|
||||
// or something), plugin
|
||||
// // usually should not handle enabled/disabled themselves but this is a base
|
||||
// plugin so... m_MOInfo->setPersistent(name(), "enabled", false, true);
|
||||
// return true;
|
||||
// }
|
||||
}
|
||||
|
||||
if (m_MOInfo) {
|
||||
m_MOInfo->setPersistent(name(), "tryInit", true);
|
||||
}
|
||||
|
||||
m_Runner = std::unique_ptr<IPythonRunner>{createPythonRunner()};
|
||||
|
||||
if (m_MOInfo) {
|
||||
m_MOInfo->setPersistent(name(), "tryInit", false);
|
||||
}
|
||||
|
||||
if (!m_Runner) {
|
||||
m_LoadFailure = FailureType::INITIALIZATION;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const CreatePythonRunner_func createPythonRunner = (CreatePythonRunner_func)::GetProcAddress(m_RunnerLib, "CreatePythonRunner");
|
||||
if (!createPythonRunner) {
|
||||
m_LoadFailure = FailureType::INVALID_DLL;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_MOInfo && m_MOInfo->persistent(name(), "tryInit", false).toBool()) {
|
||||
// m_LoadFailure = FailureType::INITIALIZATION;
|
||||
// if (QMessageBox::question(parentWidget(), tr("Python Initialization failed"),
|
||||
// tr("On a previous start the Python Plugin failed to initialize.\n"
|
||||
// "Do you want to try initializing python again (at the risk of another crash)?\n"
|
||||
// "Suggestion: Select \"no\", and click the warning sign for further help. Afterwards you have to re-enable the python plugin."),
|
||||
// QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) {
|
||||
|
||||
// // we force enabled here (note: this is a persistent settings since MO2 2.4 or something), plugin
|
||||
// // usually should not handle enabled/disabled themselves but this is a base plugin so...
|
||||
// m_MOInfo->setPersistent(name(), "enabled", false, true);
|
||||
// return true;
|
||||
// }
|
||||
}
|
||||
|
||||
if (m_MOInfo) {
|
||||
m_MOInfo->setPersistent(name(), "tryInit", true);
|
||||
}
|
||||
|
||||
m_Runner = std::unique_ptr<IPythonRunner>{ createPythonRunner() };
|
||||
|
||||
if (m_MOInfo) {
|
||||
m_MOInfo->setPersistent(name(), "tryInit", false);
|
||||
}
|
||||
|
||||
if (!m_Runner) {
|
||||
m_LoadFailure = FailureType::INITIALIZATION;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString ProxyPython::name() const
|
||||
{
|
||||
return "Python Proxy";
|
||||
return "Python Proxy";
|
||||
}
|
||||
|
||||
QString ProxyPython::localizedName() const
|
||||
{
|
||||
return tr("Python Proxy");
|
||||
return tr("Python Proxy");
|
||||
}
|
||||
|
||||
QString ProxyPython::author() const
|
||||
{
|
||||
return "AnyOldName3, Holt59, Silarn, Tannin";
|
||||
return "AnyOldName3, Holt59, Silarn, Tannin";
|
||||
}
|
||||
|
||||
QString ProxyPython::description() const
|
||||
{
|
||||
return tr("Proxy Plugin to allow plugins written in python to be loaded");
|
||||
return tr("Proxy Plugin to allow plugins written in python to be loaded");
|
||||
}
|
||||
|
||||
VersionInfo ProxyPython::version() const
|
||||
{
|
||||
return VersionInfo(2, 3, 0, VersionInfo::RELEASE_FINAL);
|
||||
return VersionInfo(2, 3, 0, VersionInfo::RELEASE_FINAL);
|
||||
}
|
||||
|
||||
QList<PluginSetting> ProxyPython::settings() const
|
||||
{
|
||||
return {};
|
||||
return {};
|
||||
}
|
||||
|
||||
QStringList ProxyPython::pluginList(const QDir& pluginPath) const
|
||||
{
|
||||
QDir dir(pluginPath);
|
||||
dir.setFilter(dir.filter() | QDir::NoDotAndDotDot);
|
||||
QDirIterator iter(dir);
|
||||
QDir dir(pluginPath);
|
||||
dir.setFilter(dir.filter() | QDir::NoDotAndDotDot);
|
||||
QDirIterator iter(dir);
|
||||
|
||||
// Note: We put python script (.py) and directory names, not the __init__.py
|
||||
// files in those since it is easier for the runner to import them.
|
||||
QStringList result;
|
||||
while (iter.hasNext()) {
|
||||
QString name = iter.next();
|
||||
QFileInfo info = iter.fileInfo();
|
||||
// Note: We put python script (.py) and directory names, not the __init__.py
|
||||
// files in those since it is easier for the runner to import them.
|
||||
QStringList result;
|
||||
while (iter.hasNext()) {
|
||||
QString name = iter.next();
|
||||
QFileInfo info = iter.fileInfo();
|
||||
|
||||
if (info.fileName() == "test.py") {
|
||||
result.append(name);
|
||||
if (info.fileName() == "pyCfg.py") {
|
||||
result.append(name);
|
||||
}
|
||||
|
||||
if (info.isFile() && name.endsWith(".py")) {
|
||||
// result.append(name);
|
||||
}
|
||||
else if (info.isDir() && QDir(info.absoluteFilePath()).exists("__init__.py")) {
|
||||
// result.append(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (info.isFile() && name.endsWith(".py")) {
|
||||
// result.append(name);
|
||||
}
|
||||
else if (info.isDir() && QDir(info.absoluteFilePath()).exists("__init__.py")) {
|
||||
// result.append(name);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<QObject*> ProxyPython::load(const QString& identifier)
|
||||
{
|
||||
if (!m_Runner) {
|
||||
return {};
|
||||
}
|
||||
return m_Runner->load(identifier);
|
||||
if (!m_Runner) {
|
||||
return {};
|
||||
}
|
||||
return m_Runner->load(identifier);
|
||||
}
|
||||
|
||||
void ProxyPython::unload(const QString& identifier)
|
||||
{
|
||||
if (m_Runner) {
|
||||
return m_Runner->unload(identifier);
|
||||
}
|
||||
if (m_Runner) {
|
||||
return m_Runner->unload(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<unsigned int> ProxyPython::activeProblems() const
|
||||
{
|
||||
auto failure = m_LoadFailure;
|
||||
auto failure = m_LoadFailure;
|
||||
|
||||
// don't know how this could happen but wth
|
||||
if (m_Runner && !m_Runner->isPythonInitialized()) {
|
||||
failure = FailureType::INITIALIZATION;
|
||||
}
|
||||
// don't know how this could happen but wth
|
||||
if (m_Runner && !m_Runner->isPythonInitialized()) {
|
||||
failure = FailureType::INITIALIZATION;
|
||||
}
|
||||
|
||||
if (failure != FailureType::NONE) {
|
||||
return { static_cast<std::underlying_type_t<FailureType>>(failure) };
|
||||
}
|
||||
if (failure != FailureType::NONE) {
|
||||
return {static_cast<std::underlying_type_t<FailureType>>(failure)};
|
||||
}
|
||||
|
||||
return {};
|
||||
return {};
|
||||
}
|
||||
|
||||
QString ProxyPython::shortDescription(unsigned int key) const
|
||||
{
|
||||
switch (static_cast<FailureType>(key)) {
|
||||
switch (static_cast<FailureType>(key)) {
|
||||
case FailureType::SEMICOLON:
|
||||
return tr("ModOrganizer path contains a semicolon");
|
||||
return tr("ModOrganizer path contains a semicolon");
|
||||
case FailureType::DLL_NOT_FOUND:
|
||||
return tr("Python DLL not found");
|
||||
return tr("Python DLL not found");
|
||||
case FailureType::INVALID_DLL:
|
||||
return tr("Invalid Python DLL");
|
||||
return tr("Invalid Python DLL");
|
||||
case FailureType::INITIALIZATION:
|
||||
return tr("Initializing Python failed");
|
||||
return tr("Initializing Python failed");
|
||||
default:
|
||||
return tr("invalid problem key %1").arg(key);
|
||||
}
|
||||
return tr("invalid problem key %1").arg(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QString ProxyPython::fullDescription(unsigned int key) const
|
||||
{
|
||||
switch (static_cast<FailureType>(key)) {
|
||||
case FailureType::SEMICOLON:
|
||||
return tr("The path to Mod Organizer (%1) contains a semicolon. <br>"
|
||||
"While this is legal on NTFS drives, many softwares do not handle it correctly.<br>"
|
||||
"Unfortunately MO depends on libraries that seem to fall into that group.<br>"
|
||||
"As a result the python plugin cannot be loaded, and the only solution we can"
|
||||
"offer is to remove the semicolon or move MO to a path without a semicolon.").arg(QCoreApplication::applicationDirPath());
|
||||
case FailureType::DLL_NOT_FOUND:
|
||||
return tr("The Python plugin DLL was not found, maybe your antivirus deleted it. Re-installing MO2 might fix the problem.");
|
||||
case FailureType::INVALID_DLL:
|
||||
return tr("The Python plugin DLL is invalid, maybe your antivirus is blocking it. "
|
||||
"Re-installing MO2 and adding exclusions for it to your AV might fix the problem.");
|
||||
case FailureType::INITIALIZATION:
|
||||
return tr("The initialization of the Python plugin DLL failed, unfortunately without any details.");
|
||||
default:
|
||||
return tr("invalid problem key %1").arg(key);
|
||||
}
|
||||
switch (static_cast<FailureType>(key)) {
|
||||
case FailureType::SEMICOLON:
|
||||
return tr("The path to Mod Organizer (%1) contains a semicolon. <br>"
|
||||
"While this is legal on NTFS drives, many softwares do not handle it "
|
||||
"correctly.<br>"
|
||||
"Unfortunately MO depends on libraries that seem to fall into that "
|
||||
"group.<br>"
|
||||
"As a result the python plugin cannot be loaded, and the only "
|
||||
"solution we can"
|
||||
"offer is to remove the semicolon or move MO to a path without a "
|
||||
"semicolon.")
|
||||
.arg(QCoreApplication::applicationDirPath());
|
||||
case FailureType::DLL_NOT_FOUND:
|
||||
return tr("The Python plugin DLL was not found, maybe your antivirus deleted "
|
||||
"it. Re-installing MO2 might fix the problem.");
|
||||
case FailureType::INVALID_DLL:
|
||||
return tr(
|
||||
"The Python plugin DLL is invalid, maybe your antivirus is blocking it. "
|
||||
"Re-installing MO2 and adding exclusions for it to your AV might fix the "
|
||||
"problem.");
|
||||
case FailureType::INITIALIZATION:
|
||||
return tr("The initialization of the Python plugin DLL failed, unfortunately "
|
||||
"without any details.");
|
||||
default:
|
||||
return tr("invalid problem key %1").arg(key);
|
||||
}
|
||||
}
|
||||
|
||||
bool ProxyPython::hasGuidedFix(unsigned int key) const
|
||||
{
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProxyPython::startGuidedFix(unsigned int key) const
|
||||
{
|
||||
}
|
||||
void ProxyPython::startGuidedFix(unsigned int key) const {}
|
||||
|
||||
@@ -300,19 +300,19 @@ PythonRunner::PythonRunner() {}
|
||||
|
||||
PythonRunner::~PythonRunner()
|
||||
{
|
||||
// We need the GIL lock when destroying Python objects.
|
||||
py::gil_scoped_acquire lock;
|
||||
// m_Interpreter.reset();
|
||||
|
||||
// Boost.Python does not handle cyclic garbace collection, so we need to
|
||||
// release everything hold by the objects before deleting the objects
|
||||
// themselves: for (auto& [name, objects] : m_PythonObjects) {
|
||||
// for (auto& obj : objects) {
|
||||
// obj.attr("__dict__").attr("clear")();
|
||||
// }
|
||||
// Boost.Python does not handle cyclic garbace collection, so we need to release
|
||||
// everything hold by the objects before deleting the objects themselves:
|
||||
// for (auto& [name, objects] : m_PythonObjects) {
|
||||
// for (auto& obj : objects) {
|
||||
// obj.attr("__dict__").attr("clear")();
|
||||
// }
|
||||
// }
|
||||
|
||||
// m_PythonObjects.clear();
|
||||
// we need to clear this here otherwise there is a crash in
|
||||
// finalize_interpreter()
|
||||
m_PythonObjects.clear();
|
||||
|
||||
py::finalize_interpreter();
|
||||
}
|
||||
|
||||
// ErrWrapper is in error.h
|
||||
@@ -331,13 +331,6 @@ bool PythonRunner::initPython()
|
||||
try {
|
||||
static const char* argv0 = "ModOrganizer.exe";
|
||||
|
||||
// we initialize the interpreter "the old way" because
|
||||
// scoped_interpreter does not seem to work well with PyQt
|
||||
wchar_t argBuffer[MAX_PATH];
|
||||
const size_t cSize = strlen(argv0) + 1;
|
||||
mbstowcs(argBuffer, argv0, MAX_PATH);
|
||||
Py_SetProgramName(argBuffer);
|
||||
|
||||
PyImport_AppendInittab("mobase", &PyInit_mobase);
|
||||
PyImport_AppendInittab("moprivate", &PyInit_moprivate);
|
||||
|
||||
@@ -345,7 +338,8 @@ bool PythonRunner::initPython()
|
||||
Py_NoSiteFlag = 1;
|
||||
|
||||
initPath();
|
||||
Py_InitializeEx(0);
|
||||
|
||||
py::initialize_interpreter(false, 1, &argv0);
|
||||
|
||||
if (!Py_IsInitialized()) {
|
||||
return false;
|
||||
|
||||
@@ -56,13 +56,15 @@ namespace mo2::python {
|
||||
py::arg("write") = py::cpp_function([](std::string_view message) {
|
||||
static PrintWrapper wrapper(MOBase::log::Debug);
|
||||
wrapper.write(message);
|
||||
}));
|
||||
}),
|
||||
py::arg("flush") = py::cpp_function([] {}));
|
||||
auto errorWrapper = make_python_type(
|
||||
"MO2ErrorWrapper", py::make_tuple(),
|
||||
py::arg("write") = py::cpp_function([](std::string_view message) {
|
||||
static PrintWrapper wrapper(MOBase::log::Error);
|
||||
wrapper.write(message);
|
||||
}));
|
||||
}),
|
||||
py::arg("flush") = py::cpp_function([] {}));
|
||||
py::module_ sys = py::module_::import("sys");
|
||||
sys.attr("stdout") = printWrapper();
|
||||
sys.attr("stderr") = errorWrapper();
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
#include "wrappers.h"
|
||||
|
||||
#include "pyplugins.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
using namespace pybind11::literals;
|
||||
using namespace MOBase;
|
||||
|
||||
namespace mo2::python {
|
||||
|
||||
// this one is kind of big so it has its own function
|
||||
void add_iplugingame_bindings(pybind11::module_ m)
|
||||
{
|
||||
|
||||
py::enum_<MOBase::IPluginGame::LoadOrderMechanism>(m, "LoadOrderMechanism")
|
||||
.value("FileTime", MOBase::IPluginGame::LoadOrderMechanism::FileTime)
|
||||
.value("PluginsTxt", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt)
|
||||
|
||||
.value("FILE_TIME", MOBase::IPluginGame::LoadOrderMechanism::FileTime)
|
||||
.value("PLUGINS_TXT", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt);
|
||||
|
||||
py::enum_<MOBase::IPluginGame::SortMechanism>(m, "SortMechanism")
|
||||
.value("NONE", MOBase::IPluginGame::SortMechanism::NONE)
|
||||
.value("MLOX", MOBase::IPluginGame::SortMechanism::MLOX)
|
||||
.value("BOSS", MOBase::IPluginGame::SortMechanism::BOSS)
|
||||
.value("LOOT", MOBase::IPluginGame::SortMechanism::LOOT);
|
||||
|
||||
// this does not actually do the conversion, but might be convenient
|
||||
// for accessing the names for enum bits
|
||||
py::enum_<MOBase::IPluginGame::ProfileSetting>(m, "ProfileSetting")
|
||||
.value("mods", MOBase::IPluginGame::MODS)
|
||||
.value("configuration", MOBase::IPluginGame::CONFIGURATION)
|
||||
.value("savegames", MOBase::IPluginGame::SAVEGAMES)
|
||||
.value("preferDefaults", MOBase::IPluginGame::PREFER_DEFAULTS)
|
||||
|
||||
.value("MODS", MOBase::IPluginGame::MODS)
|
||||
.value("CONFIGURATION", MOBase::IPluginGame::CONFIGURATION)
|
||||
.value("SAVEGAMES", MOBase::IPluginGame::SAVEGAMES)
|
||||
.value("PREFER_DEFAULTS", MOBase::IPluginGame::PREFER_DEFAULTS);
|
||||
|
||||
// py::class_<IPluginGameWrapper, py::bases<IPlugin>,
|
||||
// boost::noncopyable>("IPluginGame")
|
||||
// .def("localizedName", &MOBase::IPlugin::localizedName,
|
||||
// &IPluginGameWrapper::localizedName_Default) .def("master",
|
||||
// &MOBase::IPlugin::master, &IPluginGameWrapper::master_Default)
|
||||
|
||||
// .def("detectGame",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::detectGame))
|
||||
// .def("gameName",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::gameName))
|
||||
// .def("initializeProfile",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::initializeProfile),
|
||||
// (py::arg("directory"), "settings")) .def("listSaves",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::listSaves),
|
||||
// py::arg("folder")) .def("isInstalled",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::isInstalled))
|
||||
// .def("gameIcon",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::gameIcon))
|
||||
// .def("gameDirectory",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::gameDirectory))
|
||||
// .def("dataDirectory",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::dataDirectory))
|
||||
// .def("setGamePath",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::setGamePath),
|
||||
// py::arg("path")) .def("documentsDirectory",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::documentsDirectory))
|
||||
// .def("savesDirectory",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::savesDirectory))
|
||||
// .def("executables",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::executables))
|
||||
// .def("executableForcedLoads",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::executableForcedLoads))
|
||||
// .def("steamAPPId",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::steamAPPId))
|
||||
// .def("primaryPlugins",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::primaryPlugins))
|
||||
// .def("gameVariants",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::gameVariants))
|
||||
// .def("setGameVariant",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::setGameVariant),
|
||||
// py::arg("variant")) .def("binaryName",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::binaryName))
|
||||
// .def("gameShortName",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::gameShortName))
|
||||
// .def("primarySources",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::primarySources))
|
||||
// .def("validShortNames",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::validShortNames))
|
||||
// .def("gameNexusName",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::gameNexusName))
|
||||
// .def("iniFiles",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::iniFiles))
|
||||
// .def("DLCPlugins",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::DLCPlugins))
|
||||
// .def("CCPlugins",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::CCPlugins))
|
||||
// .def("loadOrderMechanism",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::loadOrderMechanism))
|
||||
// .def("sortMechanism",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::sortMechanism))
|
||||
// .def("nexusModOrganizerID",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::nexusModOrganizerID))
|
||||
// .def("nexusGameID",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::nexusGameID))
|
||||
// .def("looksValid",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::looksValid),
|
||||
// py::arg("directory")) .def("gameVersion",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::gameVersion))
|
||||
// .def("getLauncherName",
|
||||
// py::pure_virtual(&MOBase::IPluginGame::getLauncherName))
|
||||
|
||||
// .def("featureList", +[](MOBase::IPluginGame* p) {
|
||||
// // Constructing a dict from class name to actual object:
|
||||
// py::dict dict;
|
||||
// mp11::mp_for_each<
|
||||
// // Must user pointers because mp_for_each construct object:
|
||||
// mp11::mp_transform<std::add_pointer_t, MpGameFeaturesList>
|
||||
// >([&](auto* pt) {
|
||||
// using T = std::remove_pointer_t<decltype(pt)>;
|
||||
// typename py::reference_existing_object::apply<T*>::type
|
||||
// converter;
|
||||
|
||||
// // Retrieve the python class object:
|
||||
// const py::converter::registration* registration =
|
||||
// py::converter::registry::query(py::type_id<T>()); py::object
|
||||
// key
|
||||
// =
|
||||
// py::object(py::handle<>(py::borrowed(registration->get_class_object())));
|
||||
|
||||
// // Set the object:
|
||||
// dict[key] = py::handle<>(converter(p->feature<T>()));
|
||||
// });
|
||||
// return dict;
|
||||
// })
|
||||
|
||||
// .def("feature", +[](MOBase::IPluginGame* p, py::object clsObj) {
|
||||
// py::object feature;
|
||||
// mp11::mp_for_each<
|
||||
// // Must user pointers because mp_for_each construct object:
|
||||
// mp11::mp_transform<std::add_pointer_t, MpGameFeaturesList>
|
||||
// >([&](auto* pt) {
|
||||
// using T = std::remove_pointer_t<decltype(pt)>;
|
||||
// typename py::reference_existing_object::apply<T*>::type
|
||||
// converter;
|
||||
|
||||
// // Retrieve the python class object:
|
||||
// const py::converter::registration* registration =
|
||||
// py::converter::registry::query(py::type_id<T>());
|
||||
|
||||
// if (clsObj.ptr() == (PyObject*)
|
||||
// registration->get_class_object())
|
||||
// {
|
||||
// feature =
|
||||
// py::object(py::handle<>(converter(p->feature<T>())));
|
||||
// }
|
||||
// });
|
||||
// return feature;
|
||||
// }, py::arg("feature_type"))
|
||||
// ;
|
||||
}
|
||||
|
||||
// multiple installers
|
||||
void add_iplugininstaller_bindings(pybind11::module_ m)
|
||||
{
|
||||
py::enum_<MOBase::IPluginInstaller::EInstallResult>(m, "InstallResult")
|
||||
.value("SUCCESS", MOBase::IPluginInstaller::RESULT_SUCCESS)
|
||||
.value("FAILED", MOBase::IPluginInstaller::RESULT_FAILED)
|
||||
.value("CANCELED", MOBase::IPluginInstaller::RESULT_CANCELED)
|
||||
.value("MANUAL_REQUESTED", MOBase::IPluginInstaller::RESULT_MANUALREQUESTED)
|
||||
.value("NOT_ATTEMPTED", MOBase::IPluginInstaller::RESULT_NOTATTEMPTED);
|
||||
|
||||
// this is bind but should not be inherited in Python - does not make sense,
|
||||
// having it makes it simpler to bind the Simple and Custom installers
|
||||
py::class_<IPluginInstaller, PyPluginInstallerBase<IPluginInstaller>, IPlugin,
|
||||
std::unique_ptr<IPluginInstaller, py::nodelete>>(
|
||||
m, "IPluginInstaller", py::multiple_inheritance())
|
||||
.def("isArchiveSupported", &IPluginInstaller::isArchiveSupported, "tree"_a)
|
||||
.def("priority", &IPluginInstaller::priority)
|
||||
.def("onInstallationStart", &IPluginInstaller::onInstallationStart,
|
||||
"archive"_a, "reinstallation"_a, "current_mod"_a)
|
||||
.def("onInstallationEnd", &IPluginInstaller::onInstallationEnd, "result"_a,
|
||||
"new_mod"_a)
|
||||
.def("isManualInstaller", &IPluginInstaller::isManualInstaller)
|
||||
.def("setParentWidget", &IPluginInstaller::setParentWidget, "parent"_a)
|
||||
.def("setInstallationManager", &IPluginInstaller::setInstallationManager,
|
||||
"manager"_a)
|
||||
.def("_parentWidget",
|
||||
&PyPluginInstallerBase<IPluginInstaller>::parentWidget)
|
||||
.def("_manager", &PyPluginInstallerBase<IPluginInstaller>::manager,
|
||||
py::return_value_policy::reference);
|
||||
|
||||
py::class_<IPluginInstallerSimple, PyPluginInstallerSimple, IPluginInstaller,
|
||||
std::unique_ptr<IPluginInstallerSimple, py::nodelete>>(
|
||||
m, "IPluginInstallerSimple", py::multiple_inheritance())
|
||||
.def(py::init<>())
|
||||
|
||||
// note: keeping the variant here even if we always return a tuple
|
||||
// to be consistent with the wrapper and have proper stubs generation.
|
||||
.def(
|
||||
"install",
|
||||
[](IPluginInstallerSimple* p, GuessedValue<QString>& modName,
|
||||
std::shared_ptr<IFileTree>& tree, QString& version,
|
||||
int& nexusID) -> PyPluginInstallerSimple::py_install_return_type {
|
||||
auto result = p->install(modName, tree, version, nexusID);
|
||||
return std::make_tuple(result, tree, version, nexusID);
|
||||
},
|
||||
"name"_a, "tree"_a, "version"_a, "nexus_id"_a);
|
||||
|
||||
py::class_<IPluginInstallerCustom, PyPluginInstallerCustom, IPluginInstaller,
|
||||
std::unique_ptr<IPluginInstallerCustom, py::nodelete>>(
|
||||
m, "IPluginInstallerCustom", py::multiple_inheritance())
|
||||
.def(py::init<>())
|
||||
.def("isArchiveSupported", &IPluginInstallerCustom::isArchiveSupported,
|
||||
"archive_name"_a)
|
||||
.def("supportedExtensions", &IPluginInstallerCustom::supportedExtensions)
|
||||
.def("install", &IPluginInstallerCustom::install, "mod_name"_a,
|
||||
"game_name"_a, "archive_name"_a, "version"_a, "nexus_id"_a);
|
||||
}
|
||||
|
||||
void add_plugins_bindings(pybind11::module_ m)
|
||||
{
|
||||
py::class_<IPlugin, PyPlugin, std::unique_ptr<IPlugin, py::nodelete>>(
|
||||
m, "IPluginBase", py::multiple_inheritance())
|
||||
.def(py::init<>())
|
||||
.def("init", &MOBase::IPlugin::init, "organizer"_a)
|
||||
.def("name", &MOBase::IPlugin::name)
|
||||
.def("localizedName", &MOBase::IPlugin::localizedName)
|
||||
.def("master", &MOBase::IPlugin::master)
|
||||
.def("author", &MOBase::IPlugin::author)
|
||||
.def("description", &MOBase::IPlugin::description)
|
||||
.def("version", &MOBase::IPlugin::version)
|
||||
.def("requirements", &MOBase::IPlugin::requirements)
|
||||
.def("settings", &MOBase::IPlugin::settings)
|
||||
.def("enabledByDefault", &MOBase::IPlugin::enabledByDefault);
|
||||
|
||||
py::class_<IPyPlugin, PyPlugin, IPlugin,
|
||||
std::unique_ptr<IPyPlugin, py::nodelete>>(m, "IPlugin",
|
||||
py::multiple_inheritance())
|
||||
.def(py::init<>());
|
||||
|
||||
py::class_<IPyPluginFileMapper, PyPluginFileMapper, IPlugin,
|
||||
std::unique_ptr<IPyPluginFileMapper, py::nodelete>>(
|
||||
m, "IPluginFileMapper", py::multiple_inheritance())
|
||||
.def(py::init<>())
|
||||
.def("mappings", &IPluginFileMapper::mappings);
|
||||
|
||||
py::class_<IPyPluginDiagnose, PyPluginDiagnose, IPlugin,
|
||||
std::unique_ptr<IPyPluginDiagnose, py::nodelete>>(
|
||||
m, "IPluginDiagnose", py::multiple_inheritance())
|
||||
.def(py::init<>())
|
||||
.def("activeProblems", &MOBase::IPluginDiagnose::activeProblems)
|
||||
.def("shortDescription", &MOBase::IPluginDiagnose::shortDescription,
|
||||
py::arg("key"))
|
||||
.def("fullDescription", &MOBase::IPluginDiagnose::fullDescription,
|
||||
py::arg("key"))
|
||||
.def("hasGuidedFix", &MOBase::IPluginDiagnose::hasGuidedFix, py::arg("key"))
|
||||
.def("startGuidedFix", &MOBase::IPluginDiagnose::startGuidedFix,
|
||||
py::arg("key"))
|
||||
.def("_invalidate", &PyPluginDiagnose::invalidate);
|
||||
|
||||
py::class_<IPluginTool, PyPluginTool, IPlugin,
|
||||
std::unique_ptr<IPluginTool, py::nodelete>>(
|
||||
m, "IPluginTool", py::multiple_inheritance())
|
||||
.def(py::init<>())
|
||||
.def("displayName", &IPluginTool::displayName)
|
||||
.def("tooltip", &IPluginTool::tooltip)
|
||||
.def("icon", &IPluginTool::icon)
|
||||
.def("display", &IPluginTool::display)
|
||||
.def("setParentWidget", &IPluginTool::setParentWidget)
|
||||
.def("_parentWidget", &PyPluginTool::parentWidget);
|
||||
|
||||
py::class_<IPluginPreview, PyPluginPreview, IPlugin,
|
||||
std::unique_ptr<IPluginPreview, py::nodelete>>(
|
||||
m, "IPluginPreview", py::multiple_inheritance())
|
||||
.def(py::init<>())
|
||||
.def("supportedExtensions", &IPluginPreview::supportedExtensions)
|
||||
.def("genFilePreview", &IPluginPreview::genFilePreview, "filename"_a,
|
||||
"max_size"_a);
|
||||
|
||||
py::class_<IPluginModPage, PyPluginModPage, IPluginModPage,
|
||||
std::unique_ptr<IPluginModPage, py::nodelete>>(
|
||||
m, "IPluginModPage", py::multiple_inheritance())
|
||||
.def(py::init<>())
|
||||
.def("displayName", &IPluginModPage::displayName)
|
||||
.def("icon", &IPluginModPage::icon)
|
||||
.def("pageURL", &IPluginModPage::pageURL)
|
||||
.def("useIntegratedBrowser", &IPluginModPage::useIntegratedBrowser)
|
||||
.def("handlesDownload", &IPluginModPage::handlesDownload, "page_url"_a,
|
||||
"download_url"_a, "fileinfo"_a)
|
||||
.def("setParentWidget", &IPluginModPage::setParentWidget, "parent"_a)
|
||||
.def("_parentWidget", &PyPluginModPage::parentWidget);
|
||||
|
||||
add_iplugingame_bindings(m);
|
||||
add_iplugininstaller_bindings(m);
|
||||
}
|
||||
|
||||
struct extract_plugins_helper {
|
||||
QList<QObject*> objects;
|
||||
|
||||
template <class IPluginClass>
|
||||
void append_if_instance(pybind11::object plugin_obj)
|
||||
{
|
||||
if (py::isinstance<IPluginClass>(plugin_obj)) {
|
||||
objects.append(plugin_obj.cast<IPluginClass*>());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
QList<QObject*> extract_plugins(pybind11::object plugin_obj)
|
||||
{
|
||||
extract_plugins_helper helper;
|
||||
|
||||
// we need to check the trampoline class for these since the interfaces do not
|
||||
// extend IPlugin
|
||||
helper.append_if_instance<IPyPluginFileMapper>(plugin_obj);
|
||||
helper.append_if_instance<IPyPluginDiagnose>(plugin_obj);
|
||||
|
||||
helper.append_if_instance<IPluginModPage>(plugin_obj);
|
||||
helper.append_if_instance<IPluginPreview>(plugin_obj);
|
||||
helper.append_if_instance<IPluginTool>(plugin_obj);
|
||||
|
||||
// helper.append_if_instance<IPluginGame>(plugin_obj);
|
||||
helper.append_if_instance<IPluginInstallerSimple>(plugin_obj);
|
||||
helper.append_if_instance<IPluginInstallerCustom>(plugin_obj);
|
||||
|
||||
if (helper.objects.isEmpty()) {
|
||||
if (py::isinstance<IPyPlugin>(plugin_obj)) {
|
||||
helper.objects.append(plugin_obj.cast<IPyPlugin*>());
|
||||
}
|
||||
}
|
||||
|
||||
return helper.objects;
|
||||
}
|
||||
|
||||
} // namespace mo2::python
|
||||
@@ -0,0 +1,334 @@
|
||||
#ifndef PYTHON_WRAPPERS_PYPLUGINS_H
|
||||
#define PYTHON_WRAPPERS_PYPLUGINS_H
|
||||
|
||||
#include <pybind11/functional.h>
|
||||
#include <pybind11/operators.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "../pybind11_qt/pybind11_qt.h"
|
||||
|
||||
#include <iinstallationmanager.h>
|
||||
|
||||
#include <iplugin.h>
|
||||
#include <iplugindiagnose.h>
|
||||
#include <ipluginfilemapper.h>
|
||||
#include <iplugingame.h>
|
||||
#include <iplugininstaller.h>
|
||||
#include <iplugininstallercustom.h>
|
||||
#include <iplugininstallersimple.h>
|
||||
#include <ipluginmodpage.h>
|
||||
#include <ipluginpreview.h>
|
||||
#include <iplugintool.h>
|
||||
|
||||
// these needs to be defined in a header file for automoc - this file is included only
|
||||
// in pyplugins.cpp
|
||||
namespace mo2::python {
|
||||
|
||||
using namespace MOBase;
|
||||
|
||||
class IPyPlugin : public QObject, public IPlugin {};
|
||||
|
||||
class IPyPluginFileMapper : public IPyPlugin, public IPluginFileMapper {};
|
||||
|
||||
class IPyPluginDiagnose : public IPyPlugin, public IPluginDiagnose {};
|
||||
|
||||
template <class PluginBase>
|
||||
class PyPluginBase : public PluginBase {
|
||||
public:
|
||||
using PluginBase::PluginBase;
|
||||
|
||||
bool init(IOrganizer* organizer) override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(bool, PluginBase, init, organizer);
|
||||
}
|
||||
QString name() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QString, PluginBase, name, );
|
||||
}
|
||||
QString localizedName() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE(QString, PluginBase, localizedName, );
|
||||
}
|
||||
QString master() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE(QString, PluginBase, master, );
|
||||
}
|
||||
std::vector<std::shared_ptr<const IPluginRequirement>> requirements() const
|
||||
{
|
||||
PYBIND11_OVERRIDE(std::vector<std::shared_ptr<const IPluginRequirement>>,
|
||||
PluginBase, requirements, );
|
||||
}
|
||||
QString author() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QString, PluginBase, author, );
|
||||
}
|
||||
QString description() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QString, PluginBase, description, );
|
||||
}
|
||||
VersionInfo version() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(VersionInfo, PluginBase, version, );
|
||||
}
|
||||
QList<PluginSetting> settings() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QList<PluginSetting>, PluginBase, settings, );
|
||||
}
|
||||
bool enabledByDefault() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE(bool, PluginBase, enabledByDefault, );
|
||||
}
|
||||
};
|
||||
|
||||
class PyPlugin : public PyPluginBase<IPyPlugin> {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(MOBase::IPlugin)
|
||||
};
|
||||
|
||||
class PyPluginFileMapper : public PyPluginBase<IPyPluginFileMapper> {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginFileMapper)
|
||||
public:
|
||||
MappingType mappings() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(MappingType, IPluginFileMapper, mappings, );
|
||||
}
|
||||
};
|
||||
|
||||
class PyPluginDiagnose : public PyPluginBase<IPyPluginDiagnose> {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginDiagnose)
|
||||
public:
|
||||
std::vector<unsigned int> activeProblems() const
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(std::vector<unsigned int>, IPluginDiagnose,
|
||||
activeProblems, );
|
||||
}
|
||||
|
||||
QString shortDescription(unsigned int key) const
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QString, IPluginDiagnose, shortDescription, key);
|
||||
}
|
||||
|
||||
QString fullDescription(unsigned int key) const
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QString, IPluginDiagnose, fullDescription, key);
|
||||
}
|
||||
|
||||
bool hasGuidedFix(unsigned int key) const
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(bool, IPluginDiagnose, hasGuidedFix, key);
|
||||
}
|
||||
|
||||
void startGuidedFix(unsigned int key) const
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(void, IPluginDiagnose, startGuidedFix, key);
|
||||
}
|
||||
|
||||
// we need to bring this in public scope
|
||||
using IPluginDiagnose::invalidate;
|
||||
};
|
||||
|
||||
class PyPluginTool : public PyPluginBase<IPluginTool> {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool)
|
||||
public:
|
||||
QString displayName() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QString, IPluginTool, displayName, );
|
||||
}
|
||||
QString tooltip() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QString, IPluginTool, tooltip, );
|
||||
}
|
||||
QIcon icon() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QIcon, IPluginTool, icon, );
|
||||
}
|
||||
void setParentWidget(QWidget* widget) override
|
||||
{
|
||||
PYBIND11_OVERRIDE(void, IPluginTool, setParentWidget, widget);
|
||||
}
|
||||
void display() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(void, IPluginTool, display, );
|
||||
}
|
||||
|
||||
// we need to bring this in public scope
|
||||
using IPluginTool::parentWidget;
|
||||
};
|
||||
|
||||
class PyPluginPreview : public PyPluginBase<IPluginPreview> {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginPreview)
|
||||
public:
|
||||
std::set<QString> supportedExtensions() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(std::set<QString>, IPluginPreview,
|
||||
supportedExtensions, );
|
||||
}
|
||||
|
||||
QWidget* genFilePreview(const QString& fileName,
|
||||
const QSize& maxSize) const override
|
||||
{
|
||||
// TODO: transfer ownership to C++
|
||||
PYBIND11_OVERRIDE_PURE(QWidget*, IPluginPreview, genFilePreview, fileName,
|
||||
maxSize);
|
||||
}
|
||||
};
|
||||
|
||||
class PyPluginModPage : public PyPluginBase<IPluginModPage> {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginModPage)
|
||||
public:
|
||||
QString displayName() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QString, IPluginModPage, displayName, );
|
||||
}
|
||||
|
||||
QIcon icon() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QIcon, IPluginModPage, icon, );
|
||||
}
|
||||
|
||||
QUrl pageURL() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(QUrl, IPluginModPage, pageURL, );
|
||||
}
|
||||
|
||||
bool useIntegratedBrowser() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(bool, IPluginModPage, useIntegratedBrowser, );
|
||||
}
|
||||
|
||||
bool handlesDownload(const QUrl& pageURL, const QUrl& downloadURL,
|
||||
ModRepositoryFileInfo& fileInfo) const override
|
||||
{
|
||||
// TODO: cannot modify fileInfo from Python
|
||||
PYBIND11_OVERRIDE_PURE(bool, IPluginModPage, handlesDownload, pageURL,
|
||||
downloadURL, &fileInfo);
|
||||
}
|
||||
|
||||
void setParentWidget(QWidget* widget) override
|
||||
{
|
||||
PYBIND11_OVERRIDE(void, IPluginModPage, setParentWidget, widget);
|
||||
}
|
||||
|
||||
// we need to bring this in public scope
|
||||
using IPluginModPage::parentWidget;
|
||||
};
|
||||
|
||||
// installers
|
||||
template <class PluginInstallerBase>
|
||||
class PyPluginInstallerBase : public PyPluginBase<PluginInstallerBase> {
|
||||
public:
|
||||
using PyPluginBase<PluginInstallerBase>::PyPluginBase;
|
||||
|
||||
unsigned int priority() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(unsigned int, PluginInstallerBase, priority);
|
||||
}
|
||||
|
||||
bool isManualInstaller() const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(bool, PluginInstallerBase, isManualInstaller, );
|
||||
}
|
||||
|
||||
void onInstallationStart(QString const& archive, bool reinstallation,
|
||||
IModInterface* currentMod)
|
||||
{
|
||||
PYBIND11_OVERRIDE(void, PluginInstallerBase, onInstallationStart, archive,
|
||||
reinstallation, currentMod);
|
||||
}
|
||||
|
||||
void onInstallationEnd(IPluginInstaller::EInstallResult result,
|
||||
IModInterface* newMod)
|
||||
{
|
||||
PYBIND11_OVERRIDE(void, PluginInstallerBase, onInstallationEnd, result,
|
||||
newMod);
|
||||
}
|
||||
|
||||
bool isArchiveSupported(std::shared_ptr<const IFileTree> tree) const override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(bool, PluginInstallerBase, isArchiveSupported, tree);
|
||||
}
|
||||
|
||||
// we need to bring these in public scope
|
||||
using PluginInstallerBase::manager;
|
||||
using PluginInstallerBase::parentWidget;
|
||||
};
|
||||
|
||||
class PyPluginInstallerCustom
|
||||
: public PyPluginInstallerBase<IPluginInstallerCustom> {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstallerCustom)
|
||||
public:
|
||||
bool isArchiveSupported(const QString& archiveName) const
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(bool, IPluginInstallerCustom, isArchiveSupported,
|
||||
archiveName);
|
||||
}
|
||||
|
||||
std::set<QString> supportedExtensions() const
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(std::set<QString>, IPluginInstallerCustom,
|
||||
supportedExtensions, );
|
||||
}
|
||||
|
||||
EInstallResult install(GuessedValue<QString>& modName, QString gameName,
|
||||
const QString& archiveName, const QString& version,
|
||||
int nexusID) override
|
||||
{
|
||||
PYBIND11_OVERRIDE_PURE(EInstallResult, IPluginInstallerCustom, install,
|
||||
modName, gameName, archiveName, version, nexusID);
|
||||
}
|
||||
};
|
||||
|
||||
class PyPluginInstallerSimple
|
||||
: public PyPluginInstallerBase<IPluginInstallerSimple> {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstallerSimple)
|
||||
public:
|
||||
using py_install_return_type =
|
||||
std::variant<IPluginInstaller::EInstallResult, std::shared_ptr<IFileTree>,
|
||||
std::tuple<IPluginInstaller::EInstallResult,
|
||||
std::shared_ptr<IFileTree>, QString, int>>;
|
||||
|
||||
EInstallResult install(GuessedValue<QString>& modName,
|
||||
std::shared_ptr<IFileTree>& tree, QString& version,
|
||||
int& nexusID) override
|
||||
{
|
||||
const auto result = [&, this]() {
|
||||
PYBIND11_OVERRIDE_PURE(py_install_return_type, IPluginInstallerSimple,
|
||||
install, modName, tree, version, nexusID);
|
||||
}();
|
||||
|
||||
return std::visit(
|
||||
[&modName, &tree, &version, &nexusID](auto const& t) {
|
||||
using type = std::decay_t<decltype(t)>;
|
||||
if constexpr (std::is_same_v<type, EInstallResult>) {
|
||||
return t;
|
||||
}
|
||||
else if constexpr (std::is_same_v<type,
|
||||
std::shared_ptr<IFileTree>>) {
|
||||
tree = t;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
else if constexpr (std::is_same_v<
|
||||
type, std::tuple<EInstallResult,
|
||||
std::shared_ptr<IFileTree>,
|
||||
QString, int>>) {
|
||||
tree = std::get<1>(t);
|
||||
version = std::get<2>(t);
|
||||
nexusID = std::get<3>(t);
|
||||
return std::get<0>(t);
|
||||
}
|
||||
},
|
||||
result);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mo2::python
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user