mirror of
https://github.com/ModOrganizer2/modorganizer-plugin_python.git
synced 2026-07-27 14:03:33 -07:00
Global re-organization to split mobase from runner.
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
[submodule "pybind11"]
|
||||
path = pybind11
|
||||
url = https://github.com/pybind/pybind11
|
||||
+7
-2
@@ -14,14 +14,19 @@ set(PYTHON_EXECUTABLE ${PYTHON_BUILD_PATH}/python.exe)
|
||||
set(PYTHON_INCLUDE_DIRS ${PYTHON_ROOT}/Include)
|
||||
set(PYTHON_LIBRARIES ${MO2_INSTALL_LIBS_PATH}/python${PYVERSION}.lib)
|
||||
|
||||
add_subdirectory(pybind11)
|
||||
add_subdirectory(${MO2_BUILD_PATH}/pybind11 ${CMAKE_CURRENT_BINARY_DIR}/pybind11)
|
||||
|
||||
project(plugin_python)
|
||||
|
||||
# order matters!
|
||||
add_subdirectory(src/runner-pybind11)
|
||||
add_subdirectory(src/pybind11-qt)
|
||||
add_subdirectory(src/mobase)
|
||||
add_subdirectory(src/runner)
|
||||
add_subdirectory(src/proxy)
|
||||
|
||||
# force plugin_python to build mobase
|
||||
add_dependencies(plugin_python mobase)
|
||||
|
||||
# add_subdirectory(tests)
|
||||
|
||||
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT plugin_python)
|
||||
|
||||
-1
Submodule pybind11 deleted from 9bc2704430
@@ -0,0 +1,14 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
pybind11_add_module(mobase MODULE)
|
||||
mo2_configure_library(mobase
|
||||
SOURCE_TREE
|
||||
WARNINGS OFF
|
||||
AUTOMOC ON
|
||||
TRANSLATIONS OFF
|
||||
PRIVATE_DEPENDS uibase Qt::Core
|
||||
)
|
||||
target_link_libraries(mobase PRIVATE pybind11::qt)
|
||||
target_include_directories(mobase PRIVATE ${PYTHON_ROOT}/Include)
|
||||
|
||||
install(TARGETS mobase DESTINATION bin/plugins/plugin_python/libs)
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "deprecation.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <set>
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
namespace mo2::python {
|
||||
|
||||
void show_deprecation_warning(std::string_view name, std::string_view message,
|
||||
bool show_once)
|
||||
{
|
||||
|
||||
// Contains the list of filename / line number for which a deprecation
|
||||
// warning has already been shown.
|
||||
static std::set<std::pair<std::string, int>> DeprecatedLines;
|
||||
|
||||
// Find the caller:
|
||||
auto inspect = py::module_::import("inspect");
|
||||
auto current_frame = inspect.attr("currentframe")();
|
||||
py::sequence callable_frame = inspect.attr("getouterframes")(current_frame, 2);
|
||||
auto filename = callable_frame[-1].attr("filename").cast<std::string>();
|
||||
auto function = callable_frame[-1].attr("function").cast<std::string>();
|
||||
auto lineno = callable_frame[-1].attr("lineno").cast<int>();
|
||||
|
||||
// Only show once if requested:
|
||||
if (show_once && DeprecatedLines.contains({filename, lineno})) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Register the deprecation:
|
||||
DeprecatedLines.emplace(filename, lineno);
|
||||
|
||||
auto path = relative(std::filesystem::path(filename),
|
||||
QCoreApplication::applicationDirPath().toStdWString());
|
||||
|
||||
// Show the message:
|
||||
if (message.empty()) {
|
||||
MOBase::log::warn("[deprecated] {} in {} [{}:{}].", name, function,
|
||||
path.native(), lineno);
|
||||
}
|
||||
else {
|
||||
MOBase::log::warn("[deprecated] {} in {} [{}:{}]: {}", name, function,
|
||||
path.native(), lineno, message);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mo2::python
|
||||
@@ -7,19 +7,6 @@
|
||||
|
||||
namespace mo2::python {
|
||||
|
||||
/**
|
||||
* @brief Configure Python stdout and stderr to log to MO2.
|
||||
*
|
||||
*/
|
||||
void configure_python_stream();
|
||||
|
||||
/**
|
||||
* @brief Configure logging for MO2 python plugin.
|
||||
*
|
||||
* @param mobase The mobase module.
|
||||
*/
|
||||
void configure_python_logging(pybind11::module_ mobase);
|
||||
|
||||
/**
|
||||
* @brief Show a deprecation warning.
|
||||
*
|
||||
@@ -0,0 +1,269 @@
|
||||
#pragma warning(disable : 4100)
|
||||
#pragma warning(disable : 4996)
|
||||
|
||||
#include <tuple>
|
||||
#include <variant>
|
||||
|
||||
#include "pybind11_all.h"
|
||||
|
||||
#include "wrappers/pyfiletree.h"
|
||||
#include "wrappers/wrappers.h"
|
||||
|
||||
// TODO: remove these include (only for testing)
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QWidget>
|
||||
#include <fmt/format.h>
|
||||
#include <iplugin.h>
|
||||
#include <iplugindiagnose.h>
|
||||
#include <ipluginfilemapper.h>
|
||||
#include <iplugingame.h>
|
||||
#include <iplugininstaller.h>
|
||||
#include <iplugininstallersimple.h>
|
||||
#include <ipluginlist.h>
|
||||
#include <ipluginmodpage.h>
|
||||
#include <ipluginpreview.h>
|
||||
#include <iplugintool.h>
|
||||
#include <isavegame.h>
|
||||
#include <isavegameinfowidget.h>
|
||||
|
||||
using namespace MOBase;
|
||||
namespace py = pybind11;
|
||||
|
||||
PYBIND11_MODULE(mobase, m)
|
||||
{
|
||||
using namespace mo2::python;
|
||||
|
||||
py::module_::import("PyQt6.QtCore");
|
||||
py::module_::import("PyQt6.QtWidgets");
|
||||
|
||||
// bindings
|
||||
//
|
||||
mo2::python::add_basic_bindings(m);
|
||||
mo2::python::add_wrapper_bindings(m);
|
||||
|
||||
// game features must be added before plugins
|
||||
mo2::python::add_game_feature_bindings(m);
|
||||
|
||||
mo2::python::add_plugins_bindings(m);
|
||||
|
||||
// widgets
|
||||
//
|
||||
py::module_ widgets(
|
||||
py::reinterpret_borrow<py::module_>(PyImport_AddModule("mobase.widgets")));
|
||||
m.attr("widgets") = widgets;
|
||||
mo2::python::add_widget_bindings(widgets);
|
||||
|
||||
// functions
|
||||
//
|
||||
m.def("getFileVersion", &MOBase::getFileVersion, py::arg("filepath"));
|
||||
m.def("getProductVersion", &MOBase::getProductVersion, py::arg("executable"));
|
||||
m.def("getIconForExecutable", &MOBase::iconForExecutable, py::arg("executable"));
|
||||
|
||||
// expose MoVariant - MoVariant is a fake object whose only purpose is to be
|
||||
// used as a type-hint on the python side (e.g., def foo(x:
|
||||
// mobase.MoVariant))
|
||||
//
|
||||
// the real MoVariant is defined in the generated stubs, since it's only
|
||||
// relevant when doing type-checking, but this needs to be defined,
|
||||
// otherwise MoVariant is not found when actually running plugins through
|
||||
// MO2, making them crash
|
||||
m.attr("MoVariant") = py::none();
|
||||
|
||||
// private stuff for debugging/test
|
||||
py::module_ moprivate(
|
||||
py::reinterpret_borrow<py::module_>(PyImport_AddModule("mobase.private")));
|
||||
m.attr("private") = moprivate;
|
||||
|
||||
// expose a function to create a particular tree, only for debugging
|
||||
// purpose, not in mobase.
|
||||
mo2::python::add_make_tree_function(moprivate);
|
||||
moprivate.def("extract_plugins", &mo2::python::extract_plugins);
|
||||
|
||||
// == BEGIN TESTS ==
|
||||
|
||||
m.def("testPlugin", [](py::object pyobj) {
|
||||
py::scoped_ostream_redirect s{std::cout};
|
||||
std::cout << "type: " << pyobj.get_type().attr("__name__").cast<std::string>()
|
||||
<< "\n";
|
||||
auto qobjects = mo2::python::extract_plugins(pyobj);
|
||||
std::cout << " found " << qobjects.size() << " plugins\n";
|
||||
|
||||
// cast as IPlugin
|
||||
for (int i = 0; i < qobjects.size(); ++i) {
|
||||
IPlugin* plugin = qobject_cast<IPlugin*>(qobjects[i]);
|
||||
std::cout << fmt::format(" plugin {}: {} -> {}\n", i, (void*)qobjects[i],
|
||||
(void*)plugin);
|
||||
std::cout << fmt::format(" name: {}\n", plugin->name().toStdString());
|
||||
// std::cout << fmt::format(
|
||||
// " installer?: {}\n",
|
||||
// (void*)qobject_cast<IPluginInstaller*>(qobjects[i]));
|
||||
// std::cout << fmt::format(
|
||||
// " installer simple?: {}\n",
|
||||
// (void*)qobject_cast<IPluginInstallerSimple*>(qobjects[i]));
|
||||
|
||||
if (IPluginGame* game = dynamic_cast<IPluginGame*>(plugin)) {
|
||||
auto saves = game->listSaves(QDir());
|
||||
std::cout << " saves: " << saves.size() << "\n";
|
||||
for (auto& save : saves) {
|
||||
std::cout << " save: " << (void*)save.get() << ", "
|
||||
<< py::reinterpret_borrow<py::object>(py::cast(save))
|
||||
<< ", " << save->getFilepath().toStdString() << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
py::detail::type_caster<QString> t1;
|
||||
py::detail::type_caster<QVariant> t2;
|
||||
|
||||
m.def("testQStringList", [](QStringList const& list) {
|
||||
QStringList res = list;
|
||||
for (QString& value : res) {
|
||||
value = value + "_CPP";
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
m.def("testGuessedString", [](GuessedValue<QString> const& value) {
|
||||
return std::make_tuple(value.operator const QString&(), value.variants());
|
||||
});
|
||||
|
||||
m.def("testQStringList", [](QStringList const& list) {
|
||||
QStringList res = list;
|
||||
for (QString& value : res) {
|
||||
value = value + "_CPP";
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
m.def("testQMap1", [](QMap<QString, QString> const& map) {
|
||||
QMap<QString, int> res;
|
||||
for (auto it = map.begin(); it != map.end(); ++it) {
|
||||
res[it.key()] = it.value().size();
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
m.def("testQMap2", [](QMap<QString, int> const& map) {
|
||||
QMap<QString, QString> res;
|
||||
for (auto it = map.begin(); it != map.end(); ++it) {
|
||||
res[it.key()] = QString::number(it.value());
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
m.def("testDateTime1", []() {
|
||||
return QDateTime::fromString("2022-02-15T12:33:45", Qt::ISODate);
|
||||
});
|
||||
|
||||
m.def("testDateTime2", [](QDateTime const& datetime) {
|
||||
return datetime.toString();
|
||||
});
|
||||
|
||||
m.def("testEnum0", []() {
|
||||
return Qt::GlobalColor::darkRed;
|
||||
});
|
||||
|
||||
m.def("testEnum", [](Qt::GlobalColor color) {
|
||||
return py::make_tuple(color, QMessageBox::Icon::Information);
|
||||
});
|
||||
|
||||
m.def("testPixmap", [](QPixmap const& pixmap) {
|
||||
return pixmap.size();
|
||||
});
|
||||
|
||||
m.def("createSaveGame", []() -> ISaveGame* {
|
||||
class SaveGame : public ISaveGame {
|
||||
QString getFilepath() const override { return "filepath"; }
|
||||
QDateTime getCreationTime() const override
|
||||
{
|
||||
return QDateTime::fromString("2022-02-15T12:33:45", Qt::ISODate);
|
||||
}
|
||||
QString getName() const override { return "name"; }
|
||||
QString getSaveGroupIdentifier() const override { return "group"; }
|
||||
QStringList allFiles() const override { return {"file1", "file2"}; }
|
||||
};
|
||||
return new SaveGame();
|
||||
});
|
||||
|
||||
m.def("testSaveGameWidget", [](ISaveGameInfoWidget* widget) {
|
||||
class SaveGame : public ISaveGame {
|
||||
QString getFilepath() const override { return "filepath-c++"; }
|
||||
QDateTime getCreationTime() const override
|
||||
{
|
||||
return QDateTime::fromString("2022-02-15T12:33:45", Qt::ISODate);
|
||||
}
|
||||
QString getName() const override { return "name"; }
|
||||
QString getSaveGroupIdentifier() const override { return "group"; }
|
||||
QStringList allFiles() const override { return {"file1", "file2"}; }
|
||||
};
|
||||
static SaveGame s;
|
||||
widget->setSave(s);
|
||||
});
|
||||
|
||||
m.def("testSaveGameRef", [](const ISaveGame& game) {
|
||||
std::cout << "getFilepath(): " << game.getFilepath().toStdString() << "\n";
|
||||
std::cout << "getCreationTime(): "
|
||||
<< game.getCreationTime().toString().toStdString() << "\n";
|
||||
std::cout << "getName(): " << game.getName().toStdString() << "\n";
|
||||
std::cout << "getSaveGroupIdentifier(): "
|
||||
<< game.getSaveGroupIdentifier().toStdString() << "\n";
|
||||
std::cout << "allFiles(): [ " << game.allFiles().join(" ").toStdString()
|
||||
<< " ]\n";
|
||||
});
|
||||
|
||||
m.def("testSaveGamePtr", [](const ISaveGame* game) {
|
||||
std::cout << "getFilepath(): " << game->getFilepath().toStdString() << "\n";
|
||||
std::cout << "getCreationTime(): "
|
||||
<< game->getCreationTime().toString().toStdString() << "\n";
|
||||
std::cout << "getName(): " << game->getName().toStdString() << "\n";
|
||||
std::cout << "getSaveGroupIdentifier(): "
|
||||
<< game->getSaveGroupIdentifier().toStdString() << "\n";
|
||||
std::cout << "allFiles(): [ " << game->allFiles().join(" ").toStdString()
|
||||
<< " ]\n";
|
||||
});
|
||||
|
||||
m.def("testWidget1", [](QWidget* w) {
|
||||
if (w) {
|
||||
std::cout << "background role: " << w->backgroundRole() << "\n";
|
||||
}
|
||||
else {
|
||||
std::cout << "null widget\n";
|
||||
}
|
||||
});
|
||||
|
||||
m.def("testWidget2", []() {
|
||||
QWidget* w = new QWidget();
|
||||
w->setBackgroundRole(QPalette::ColorRole::HighlightedText);
|
||||
return w;
|
||||
});
|
||||
|
||||
m.def("testFlags1", [](IPluginList::PluginStates states) {
|
||||
std::vector<std::string> res;
|
||||
if (states.testFlag(IPluginList::STATE_MISSING)) {
|
||||
res.push_back("missing");
|
||||
}
|
||||
if (states.testFlag(IPluginList::STATE_INACTIVE)) {
|
||||
res.push_back("inactive");
|
||||
}
|
||||
if (states.testFlag(IPluginList::STATE_ACTIVE)) {
|
||||
res.push_back("active");
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
m.def("testFlags2", [](QStringList const& states) {
|
||||
IPluginList::PluginStates res;
|
||||
if (states.contains("missing")) {
|
||||
res |= IPluginList::STATE_MISSING;
|
||||
}
|
||||
if (states.contains("inactive")) {
|
||||
res |= IPluginList::STATE_INACTIVE;
|
||||
}
|
||||
if (states.contains("active")) {
|
||||
res |= IPluginList::STATE_ACTIVE;
|
||||
}
|
||||
return res;
|
||||
});
|
||||
}
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
#include <pluginsetting.h>
|
||||
#include <versioninfo.h>
|
||||
|
||||
#include "../pythonutils.h"
|
||||
#include "../deprecation.h"
|
||||
#include "pyfiletree.h"
|
||||
|
||||
using namespace MOBase;
|
||||
@@ -3,12 +3,7 @@
|
||||
#include <tuple>
|
||||
#include <variant>
|
||||
|
||||
#include <pybind11/functional.h>
|
||||
#include <pybind11/operators.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
#include "../pybind11_qt/pybind11_qt.h"
|
||||
#include "../pybind11_all.h"
|
||||
|
||||
#include <ifiletree.h>
|
||||
#include <log.h>
|
||||
@@ -4,58 +4,58 @@
|
||||
<context>
|
||||
<name>ProxyPython</name>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="162"/>
|
||||
<location filename="proxypython.cpp" line="166"/>
|
||||
<source>Python Proxy</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="172"/>
|
||||
<location filename="proxypython.cpp" line="176"/>
|
||||
<source>Proxy Plugin to allow plugins written in python to be loaded</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="244"/>
|
||||
<location filename="proxypython.cpp" line="248"/>
|
||||
<source>ModOrganizer path contains a semicolon</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="246"/>
|
||||
<location filename="proxypython.cpp" line="250"/>
|
||||
<source>Python DLL not found</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="248"/>
|
||||
<location filename="proxypython.cpp" line="252"/>
|
||||
<source>Invalid Python DLL</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="250"/>
|
||||
<location filename="proxypython.cpp" line="254"/>
|
||||
<source>Initializing Python failed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="252"/>
|
||||
<location filename="proxypython.cpp" line="282"/>
|
||||
<location filename="proxypython.cpp" line="256"/>
|
||||
<location filename="proxypython.cpp" line="286"/>
|
||||
<source>invalid problem key %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="proxypython.cpp" line="260"/>
|
||||
<location filename="proxypython.cpp" line="264"/>
|
||||
<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="271"/>
|
||||
<location filename="proxypython.cpp" line="275"/>
|
||||
<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="274"/>
|
||||
<location filename="proxypython.cpp" line="278"/>
|
||||
<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="279"/>
|
||||
<location filename="proxypython.cpp" line="283"/>
|
||||
<source>The initialization of the Python plugin DLL failed, unfortunately without any details.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -63,12 +63,7 @@
|
||||
<context>
|
||||
<name>QObject</name>
|
||||
<message>
|
||||
<location filename="../runner/error.h" line="68"/>
|
||||
<source>An unexpected C++ exception was thrown in python code.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../runner/error.h" line="117"/>
|
||||
<location filename="../runner/error.h" line="75"/>
|
||||
<source>An unknown exception was thrown in python code.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user