From b5b547e041430bfd3f32b10ce4df4c8017c4e2b2 Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 24 Jul 2018 01:51:04 -0500 Subject: [PATCH] Changes to compile and run with python3 (WIP) --- src/runner/pythonrunner.cpp | 2688 +++++++++++++++++------------------ 1 file changed, 1344 insertions(+), 1344 deletions(-) diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index a998e17..cc4a8cf 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -1,1344 +1,1344 @@ -#include "pythonrunner.h" - -#pragma warning( disable : 4100 ) -#pragma warning( disable : 4996 ) - -#include -#include -#include -#include -#include "uibasewrappers.h" -#include "proxypluginwrappers.h" -#include "gamefeatureswrappers.h" -#include "sipApiAccess.h" - -#include -#include -#include -#include -#include -#include - -// sip and qt slots seems to conflict -#include - -#ifndef Q_MOC_RUN -#include -#endif - - -MOBase::IOrganizer *s_Organizer = nullptr; - - - -class PythonRunner : public IPythonRunner -{ - -public: - PythonRunner(const MOBase::IOrganizer *moInfo); - bool initPython(const QString &pythonDir); - QList instantiate(const QString &pluginName); - bool isPythonInstalled() const; - bool isPythonVersionSupported() const; - -private: - - void initPath(); - -private: - std::map m_PythonObjects; - const MOBase::IOrganizer *m_MOInfo; - wchar_t *m_PythonHome; -}; - - -IPythonRunner *CreatePythonRunner(MOBase::IOrganizer *moInfo, const QString &pythonDir) -{ - s_Organizer = moInfo; - PythonRunner *result = new PythonRunner(moInfo); - if (result->initPython(pythonDir)) { - return result; - } else { - delete result; - return nullptr; - } -} - - -using namespace MOBase; - -namespace bpy = boost::python; - -struct QString_to_python_str -{ - static PyObject *convert(const QString &str) { - // It's safer to explicitly convert to unicode as if we don't, this can return either str or unicode without it being easy to know which to expect - bpy::object pyStr = bpy::object(str.toUtf8().constData()); - if (SIPBytes_Check(pyStr.ptr())) - pyStr = pyStr.attr("decode")("utf-8"); - return bpy::incref(pyStr.ptr()); - } -}; - -template -struct QFlags_to_int -{ - static PyObject *convert(const QFlags &flags) { - return bpy::incref(bpy::object(static_cast(flags)).ptr()); - } -}; - - -struct QString_from_python_str -{ - QString_from_python_str() { - bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id()); - } - - static void *convertible(PyObject *objPtr) { - return SIPBytes_Check(objPtr) || PyUnicode_Check(objPtr) ? objPtr : nullptr; - } - - static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { - // Ensure the string uses 8-bit characters - PyObject *strPtr = PyUnicode_Check(objPtr) ? PyUnicode_AsUTF8String(objPtr) : objPtr; - // Extract the character data from the python string - const char* value = SIPBytes_AsString(strPtr); - // Deallocate local copy if one was made - if (strPtr != objPtr) - Py_DecRef(strPtr); - assert(value != nullptr); - - // allocate storage - void *storage = ((bpy::converter::rvalue_from_python_storage*)data)->storage.bytes; - // construct QString in the allocated mr - new (storage) QString(value); - - data->convertible = storage; - } -}; - - -template -struct GuessedValue_converters -{ - struct GuessedValue_to_python - { - static PyObject *convert(const GuessedValue &var) { - bpy::list result; - const std::set &values = var.variants(); - for (auto iter = values.begin(); iter != values.end(); ++iter) { - result.append(bpy::make_tuple(*iter, GUESS_GOOD)); - } - return bpy::incref(result.ptr()); - } - }; - - struct GuessedValue_from_python - { - GuessedValue_from_python() { - bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id >()); - } - - static void *convertible(PyObject *objPtr) { - if PyList_Check(objPtr) { - return objPtr; - } else { - return nullptr; - } - } - - static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data* data) { - void *storage = ((bpy::converter::rvalue_from_python_storage >*)data)->storage.bytes; - GuessedValue *result = new (storage) GuessedValue(); - - bpy::list source(bpy::handle<>(bpy::borrowed(objPtr))); - int length = bpy::len(source); - for (int i = 0; i < length; ++i) { - bpy::tuple cell = bpy::extract(source[i]); - result->update(bpy::extract(cell[0]), bpy::extract(cell[1])); - } - - data->convertible = storage; - } - }; - - GuessedValue_converters() - { - GuessedValue_from_python(); - bpy::to_python_converter, GuessedValue_to_python>(); - } -}; - - -template -struct QMap_converters -{ - struct QMap_to_python - { - static PyObject *convert(const QMap &map) { - bpy::dict result; - QMapIterator iter(map); - while (iter.hasNext()) { - iter.next(); - result[bpy::object(iter.key())] = bpy::object(iter.value()); - } - return bpy::incref(result.ptr()); - } - }; - - struct QMap_from_python - { - QMap_from_python() { - bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); - } - - static void *convertible(PyObject *objPtr) { - return PyDict_Check(objPtr) ? objPtr : nullptr; - } - - static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { - void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; - QMap *result = new (storage) QMap(); - bpy::dict source(bpy::handle<>(bpy::borrowed(objPtr))); - bpy::list keys = source.keys(); - int len = bpy::len(keys); - for (int i = 0; i < len; ++i) { - bpy::object pyKey = keys[i]; - (*result)[bpy::extract(pyKey)] = bpy::extract(source[pyKey]); - } - - data->convertible = storage; - } - }; - - QMap_converters() - { - QMap_from_python(); - bpy::to_python_converter, QMap_to_python >(); - } -}; - - -struct QVariant_to_python_obj -{ - static PyObject *convert(const QVariant &var) { - switch (var.type()) { - case QVariant::Int: return SIPLong_FromLong(var.toInt()); - case QVariant::UInt: return PyLong_FromUnsignedLong(var.toUInt()); - case QVariant::Bool: return PyBool_FromLong(var.toBool()); - case QVariant::String: return bpy::incref(bpy::object(var.toString()).ptr()); - case QVariant::List: { - return bpy::incref(bpy::object(var.toList()).ptr()); - } break; - case QVariant::Map: { - return bpy::incref(bpy::object(var.toMap()).ptr()); - } break; - default: { - PyErr_Format(PyExc_TypeError, "type unsupported: %d", var.type()); - throw bpy::error_already_set(); - } break; - } - } -}; - - -struct QVariant_from_python_obj -{ - QVariant_from_python_obj() { - bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id()); - } - - static void *convertible(PyObject *objPtr) { - if (!SIPBytes_Check(objPtr) && !PyLong_Check(objPtr) && - !PyBool_Check(objPtr) && !PyList_Check(objPtr)) { - return nullptr; - } - return objPtr; - } - - template - static void constructVariant(const T &value, bpy::converter::rvalue_from_python_stage1_data *data) { - void* storage = ((bpy::converter::rvalue_from_python_storage*)data)->storage.bytes; - - new (storage) QVariant(value); - - data->convertible = storage; - } - - static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { - // PyBools will also return true for SIPLong_Check but not the other way around, so the order - // here is relevant - if (PyList_Check(objPtr)) { - constructVariant(bpy::extract(objPtr)(), data); - } else if (PyDict_Check(objPtr)) { - constructVariant(bpy::extract(objPtr)(), data); - } else if (SIPBytes_Check(objPtr) || PyUnicode_Check(objPtr)) { - constructVariant(bpy::extract(objPtr)(), data); - } else if (PyBool_Check(objPtr)) { - constructVariant(bpy::extract(objPtr)(), data); - } else if (SIPLong_Check(objPtr)) { - //QVariant doesn't have long. It has int or long long. Given that on m/s, - //long is 32 bits for 32- and 64- bit code... - constructVariant(bpy::extract(objPtr)(), data); - } else { - PyErr_SetString(PyExc_TypeError, "type unsupported"); - throw bpy::error_already_set(); - } - } -}; - - -template -struct QList_to_python_list -{ - static PyObject *convert(const QList &list) - { - bpy::list pyList; - - try { - for (const T &item : list) { - pyList.append(item); - } - } catch (const bpy::error_already_set&) { - reportPythonError(); - } - PyObject *res = bpy::incref(pyList.ptr()); - return res; - } -}; - - -template -struct QList_from_python_obj -{ - QList_from_python_obj() { - bpy::converter::registry::push_back( - &convertible, - &construct, - bpy::type_id >()); - } - - static void* convertible(PyObject *objPtr) { - if (PyList_Check(objPtr)) return objPtr; - return nullptr; - } - - static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { - void *storage = ((bpy::converter::rvalue_from_python_storage >*)data)->storage.bytes; - QList *result = new (storage) QList(); - bpy::list source(bpy::handle<>(bpy::borrowed(objPtr))); - int length = bpy::len(source); - for (int i = 0; i < length; ++i) { - result->append(bpy::extract(source[i])); - } - - data->convertible = storage; - } -}; - - -template -struct std_vector_to_python_list -{ - static PyObject *convert(const std::vector &vector) - { - bpy::list pyList; - - try { - for (const T &item : vector) - pyList.append(item); - } - catch (const bpy::error_already_set&) { - reportPythonError(); - } - - return bpy::incref(pyList.ptr()); - } -}; - - -template -struct std_vector_from_python_obj -{ - std_vector_from_python_obj() { - bpy::converter::registry::push_back( - &convertible, - &construct, - bpy::type_id >()); - } - - static void* convertible(PyObject *objPtr) { - if (PyList_Check(objPtr)) return objPtr; - return nullptr; - } - - static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { - void *storage = ((bpy::converter::rvalue_from_python_storage >*)data)->storage.bytes; - std::vector *result = new (storage) std::vector(); - bpy::list source(bpy::handle<>(bpy::borrowed(objPtr))); - int length = bpy::len(source); - for (int i = 0; i < length; ++i) { - result->push_back(bpy::extract(source[i])); - } - - data->convertible = storage; - } -}; - - -template -struct QFlags_from_python_obj -{ - QFlags_from_python_obj() { - bpy::converter::registry::push_back( - &convertible, - &construct, - bpy::type_id>()); - } - - static void* convertible(PyObject *objPtr) { - return SIPLong_Check(objPtr) ? objPtr : nullptr; - } - - static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { - int intVersion = (int)SIPLong_AsLong(objPtr); - T tVersion = (T)intVersion; - void *storage = ((bpy::converter::rvalue_from_python_storage> *)data)->storage.bytes; - new (storage) QFlags(tVersion); - - data->convertible = storage; - } -}; - - -template -struct stdset_from_python_list -{ - stdset_from_python_list() { - bpy::converter::registry::push_back( - &convertible, - &construct, - bpy::type_id >()); - } - - static void* convertible(PyObject *objPtr) { - if (PyList_Check(objPtr)) return objPtr; - return nullptr; - } - - static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { - void *storage = ((bpy::converter::rvalue_from_python_storage >*)data)->storage.bytes; - std::set *result = new (storage) std::set(); - - bpy::list source(bpy::handle<>(bpy::borrowed(objPtr))); - int length = bpy::len(source); - for (int i = 0; i < length; ++i) { - result->insert(bpy::extract(source[i])); - } - - data->convertible = storage; - } -}; - - -struct IModRepositoryBridge_to_python -{ - static PyObject *convert(IModRepositoryBridge *bridge) - { - ModRepositoryBridgeWrapper wrapper(bridge); - - return bpy::incref(bpy::object(wrapper).ptr()); - } -}; - - - -template struct MetaData; - -template <> struct MetaData { static const char *className() { return "QObject"; } }; -template <> struct MetaData { static const char *className() { return "QObject"; } }; -template <> struct MetaData { static const char *className() { return "QObject"; } }; -template <> struct MetaData { static const char *className() { return "QWidget"; } }; -template <> struct MetaData { static const char *className() { return "QDateTime"; } }; -template <> struct MetaData { static const char *className() { return "QDir"; } }; -template <> struct MetaData { static const char *className() { return "QFileInfo"; } }; -template <> struct MetaData { static const char *className() { return "QIcon"; } }; -template <> struct MetaData { static const char *className() { return "QSize"; } }; -template <> struct MetaData { static const char *className() { return "QStringList"; } }; -template <> struct MetaData { static const char *className() { return "QUrl"; } }; -template <> struct MetaData { static const char *className() { return "QVariant"; } }; - - -template -PyObject *toPyQt(T *objPtr) -{ - if (objPtr == nullptr) { - qDebug("no input object"); - return bpy::incref(Py_None); - } - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); - - if (type == nullptr) { - qDebug("failed to determine type: %s", MetaData::className()); - return bpy::incref(Py_None); - } - - PyObject *sipObj = sipAPI()->api_convert_from_type(objPtr, type, 0); - if (sipObj == nullptr) { - qDebug("failed to convert"); - return bpy::incref(Py_None); - } - return bpy::incref(sipObj); -} - - -template -struct QClass_converters -{ - struct QClass_to_PyQt - { - template - static typename std::enable_if_t, T*> getSafeCopy(T *qClass) - { - return new T(*qClass); - } - - template - static typename std::enable_if_t, T*> getSafeCopy(T *qClass) - { - return qClass; - } - - static PyObject *convert(const T &object) { - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); - if (type == nullptr) { - return bpy::incref(Py_None); - } - - PyObject *sipObj = sipAPI()->api_convert_from_type((void*)getSafeCopy((T*)&object), type, 0); - if (sipObj == nullptr) { - return bpy::incref(Py_None); - } - - if (std::is_copy_constructible_v) - // Ensure Python deletes the C++ component - sipAPI()->api_transfer_back(sipObj); - - return bpy::incref(sipObj); - } - - static PyObject *convert(T *object) { - if (object == nullptr) { - return bpy::incref(Py_None); - } - - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); - if (type == nullptr) { - return bpy::incref(Py_None); - } - - PyObject *sipObj = sipAPI()->api_convert_from_type(getSafeCopy(object), type, 0); - if (sipObj == nullptr) { - return bpy::incref(Py_None); - } - - if (std::is_copy_constructible_v) - // Ensure Python deletes the C++ component - sipAPI()->api_transfer_back(sipObj); - - return bpy::incref(sipObj); - } - - static PyObject *convert(const T *object) { - return convert((T*)object); - } - }; - - static void *QClass_from_PyQt(PyObject *objPtr) - { - // This would transfer responsibility for deconstructing the object to C++, but Boost assumes l-value converters (such as this) don't do that - // Instead, this should be called within the wrappers for functions which return deletable pointers. - //sipAPI()->api_transfer_to(objPtr, 0); - if (PyObject_TypeCheck(objPtr, sipAPI()->api_simplewrapper_type)) { - sipSimpleWrapper *wrapper; - wrapper = reinterpret_cast(objPtr); - return wrapper->data; - } else if (PyObject_TypeCheck(objPtr, sipAPI()->api_wrapper_type)) { - sipWrapper *wrapper; - wrapper = reinterpret_cast(objPtr); - return wrapper->super.data; - } else { - if (std::is_same_v) - { - // QStringLists aren't wrapped by PyQt - regular Python string/unicode lists are used instead - bpy::extract> extractor(objPtr); - if (extractor.check()) - return new QStringList(extractor()); - } - PyErr_SetString(PyExc_TypeError, "type not wrapped"); - bpy::throw_error_already_set(); - } - return new void*; - } - - QClass_converters() - { - bpy::converter::registry::insert(&QClass_from_PyQt, bpy::type_id()); - bpy::to_python_converter(); - bpy::to_python_converter(); - bpy::to_python_converter(); - } -}; - - -template -struct QInterface_converters -{ - struct QInterface_to_PyQt - { - static PyObject *convert(const T &object) { - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); - if (type == nullptr) { - return bpy::incref(Py_None); - } - - PyObject *sipObj = sipAPI()->api_convert_from_type((void*)(&object), type, 0); - if (sipObj == nullptr) { - return bpy::incref(Py_None); - } - - return bpy::incref(sipObj); - } - - static PyObject *convert(T *object) { - if (object == nullptr) { - return bpy::incref(Py_None); - } - - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); - if (type == nullptr) { - return bpy::incref(Py_None); - } - - PyObject *sipObj = sipAPI()->api_convert_from_type(object, type, 0); - if (sipObj == nullptr) { - return bpy::incref(Py_None); - } - - return bpy::incref(sipObj); - } - - static PyObject *convert(const T *object) { - return convert((T*)object); - } - }; - - static void *QInterface_from_PyQt(PyObject *objPtr) - { - if (!PyObject_TypeCheck(objPtr, sipAPI()->api_wrapper_type)) { - bpy::throw_error_already_set(); - } - - // This would transfer responsibility for deconstructing the object to C++, but Boost assumes l-value converters (such as this) don't do that - // Instead, this should be called within the wrappers for functions which return deletable pointers. - //sipAPI()->api_transfer_to(objPtr, 0); - - sipSimpleWrapper *wrapper = reinterpret_cast(objPtr); - return wrapper->data; - } - - QInterface_converters() - { - bpy::converter::registry::insert(&QInterface_from_PyQt, bpy::type_id()); - bpy::to_python_converter(); - bpy::to_python_converter(); - } -}; - - -int getArgCount(PyObject *object) { - int result = 0; - PyObject *funcCode = PyObject_GetAttrString(object, "__code__"); - if (funcCode) { - PyObject *argCount = PyObject_GetAttrString(funcCode, "co_argcount"); - if(argCount) { - result = SIPLong_AsLong(argCount); - Py_DECREF(argCount); - } - Py_DECREF(funcCode); - } - return result; -} - -template -struct Functor0_converter -{ - - struct FunctorWrapper - { - FunctorWrapper(boost::python::object callable) : m_Callable(callable) { - } - - RET operator()() { - GILock lock; - return (RET) m_Callable(); - } - - boost::python::object m_Callable; - }; - - Functor0_converter() - { - bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); - } - - static void *convertible(PyObject *object) - { - if (!PyCallable_Check(object) - || (getArgCount(object) != 0)) { - return nullptr; - } - return object; - } - - static void construct(PyObject *object, bpy::converter::rvalue_from_python_stage1_data *data) - { - bpy::object callable(bpy::handle<>(bpy::borrowed(object))); - void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; - new (storage) std::function(FunctorWrapper(callable)); - data->convertible = storage; - } -}; - - -template -struct Functor1_converter -{ - - struct FunctorWrapper - { - FunctorWrapper(boost::python::object callable) : m_Callable(callable) { - } - - RET operator()(const PAR1 ¶m1) { - GILock lock; - return (RET) m_Callable(param1); - } - - boost::python::object m_Callable; - }; - - Functor1_converter() - { - bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); - } - - static void *convertible(PyObject *object) - { - if (!PyCallable_Check(object) - || (getArgCount(object) != 1)) { - return nullptr; - } - return object; - } - - static void construct(PyObject *object, bpy::converter::rvalue_from_python_stage1_data *data) - { - bpy::object callable(bpy::handle<>(bpy::borrowed(object))); - void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; - new (storage) std::function(FunctorWrapper(callable)); - data->convertible = storage; - } -}; - - -template -struct Functor2_converter -{ - - struct FunctorWrapper - { - FunctorWrapper(boost::python::object callable) : m_Callable(callable) { - } - - RET operator()(const PAR1 ¶m1, const PAR2 ¶m2) { - GILock lock; - return (RET) m_Callable(param1, param2); - } - - boost::python::object m_Callable; - }; - - Functor2_converter() - { - bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); - } - - static void *convertible(PyObject *object) - { - if (!PyCallable_Check(object) - || (getArgCount(object) != 2)) { - return nullptr; - } - return object; - } - - static void construct(PyObject *object, bpy::converter::rvalue_from_python_stage1_data *data) - { - bpy::object callable(bpy::handle<>(bpy::borrowed(object))); - void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; - new (storage) std::function(FunctorWrapper(callable)); - data->convertible = storage; - } -}; - - -BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(updateWithQuality, MOBase::GuessedValue::update, 2, 2) - - -BOOST_PYTHON_MODULE(mobase) -{ - PyEval_InitThreads(); - bpy::to_python_converter(); - QVariant_from_python_obj(); - - bpy::to_python_converter(); - QString_from_python_str(); - - //QClass_converters(); - QClass_converters(); - QClass_converters(); - QClass_converters(); - QClass_converters(); - QClass_converters(); - QClass_converters(); - QClass_converters(); - QClass_converters(); - QInterface_converters(); - - - bpy::def("toPyQt", &toPyQt); - bpy::def("toPyQt", &toPyQt); - - bpy::enum_("ReleaseType") - .value("final", MOBase::VersionInfo::RELEASE_FINAL) - .value("candidate", MOBase::VersionInfo::RELEASE_CANDIDATE) - .value("beta", MOBase::VersionInfo::RELEASE_BETA) - .value("alpha", MOBase::VersionInfo::RELEASE_ALPHA) - .value("prealpha", MOBase::VersionInfo::RELEASE_PREALPHA) - ; - - bpy::enum_("VersionScheme") - .value("discover", MOBase::VersionInfo::SCHEME_DISCOVER) - .value("regular", MOBase::VersionInfo::SCHEME_REGULAR) - .value("decimalmark", MOBase::VersionInfo::SCHEME_DECIMALMARK) - .value("numbersandletters", MOBase::VersionInfo::SCHEME_NUMBERSANDLETTERS) - .value("date", MOBase::VersionInfo::SCHEME_DATE) - .value("literal", MOBase::VersionInfo::SCHEME_LITERAL) - ; - - bpy::class_("VersionInfo") - .def(bpy::init()) - .def(bpy::init()) - .def(bpy::init()) - .def(bpy::init()) - .def(bpy::init()) - .def(bpy::init()) - .def("clear", &VersionInfo::clear) - .def("parse", &VersionInfo::parse) - .def("canonicalString", &VersionInfo::canonicalString) - .def("displayString", &VersionInfo::displayString) - .def("isValid", &VersionInfo::isValid) - .def("scheme", &VersionInfo::scheme) - .def(bpy::self < bpy::self) - .def(bpy::self > bpy::self) - .def(bpy::self <= bpy::self) - .def(bpy::self >= bpy::self) - .def(bpy::self != bpy::self) - .def(bpy::self == bpy::self) - ; - - bpy::class_("PluginSetting", bpy::init()); - - bpy::class_("ExecutableInfo", bpy::init()) - .def("withArgument", &ExecutableInfo::withArgument, bpy::return_value_policy()) - .def("withWorkingDirectory", &ExecutableInfo::withWorkingDirectory, bpy::return_value_policy()) - .def("withSteamAppId", &ExecutableInfo::withSteamAppId, bpy::return_value_policy()) - .def("asCustom", &ExecutableInfo::asCustom, bpy::return_value_policy()) - .def("isValid", &ExecutableInfo::isValid) - .def("title", &ExecutableInfo::title) - .def("binary", &ExecutableInfo::binary) - .def("arguments", &ExecutableInfo::arguments) - .def("workingDirectory", &ExecutableInfo::workingDirectory) - .def("steamAppID", &ExecutableInfo::steamAppID) - .def("isCustom", &ExecutableInfo::isCustom) - ; - - bpy::class_("ISaveGame") - .def("getFilename", bpy::pure_virtual(&ISaveGame::getFilename)) - .def("getCreationTime", bpy::pure_virtual(&ISaveGame::getCreationTime)) - .def("getSaveGroupIdentifier", bpy::pure_virtual(&ISaveGame::getSaveGroupIdentifier)) - .def("allFiles", bpy::pure_virtual(&ISaveGame::allFiles)) - .def("hasScriptExtenderFile", bpy::pure_virtual(&ISaveGame::hasScriptExtenderFile)) - ; - - // TODO: ISaveGameInfoWidget bindings - - Functor1_converter(); - Functor1_converter(); - Functor1_converter(); - Functor2_converter(); - - bpy::class_("IOrganizer") - .def("createNexusBridge", bpy::pure_virtual(&IOrganizer::createNexusBridge), bpy::return_value_policy()) - .def("profileName", bpy::pure_virtual(&IOrganizer::profileName)) - .def("profilePath", bpy::pure_virtual(&IOrganizer::profilePath)) - .def("downloadsPath", bpy::pure_virtual(&IOrganizer::downloadsPath)) - .def("overwritePath", bpy::pure_virtual(&IOrganizer::overwritePath)) - .def("basePath", bpy::pure_virtual(&IOrganizer::basePath)) - .def("appVersion", bpy::pure_virtual(&IOrganizer::appVersion)) - .def("getMod", bpy::pure_virtual(&IOrganizer::getMod), bpy::return_value_policy()) - .def("createMod", bpy::pure_virtual(&IOrganizer::createMod), bpy::return_value_policy()) - .def("getGame", bpy::pure_virtual(&IOrganizer::getGame), bpy::return_value_policy()) - .def("removeMod", bpy::pure_virtual(&IOrganizer::removeMod)) - .def("modDataChanged", bpy::pure_virtual(&IOrganizer::modDataChanged)) - .def("pluginSetting", bpy::pure_virtual(&IOrganizer::pluginSetting)) - .def("setPluginSetting", bpy::pure_virtual(&IOrganizer::pluginSetting)) - .def("persistent", bpy::pure_virtual(&IOrganizer::persistent)) - .def("setPersistent", bpy::pure_virtual(&IOrganizer::setPersistent)) - .def("pluginDataPath", bpy::pure_virtual(&IOrganizer::pluginDataPath)) - .def("installMod", bpy::pure_virtual(&IOrganizer::installMod),(bpy::arg("nameSuggestion")=""), bpy::return_value_policy()) - .def("resolvePath", bpy::pure_virtual(&IOrganizer::resolvePath)) - .def("listDirectories", bpy::pure_virtual(&IOrganizer::listDirectories)) - .def("findFiles", bpy::pure_virtual(&IOrganizer::findFiles)) - .def("getFileOrigins", bpy::pure_virtual(&IOrganizer::getFileOrigins)) - .def("findFileInfos", bpy::pure_virtual(&IOrganizer::findFileInfos)) - .def("downloadManager", bpy::pure_virtual(&IOrganizer::downloadManager), bpy::return_value_policy()) - .def("pluginList", bpy::pure_virtual(&IOrganizer::pluginList), bpy::return_value_policy()) - .def("modList", bpy::pure_virtual(&IOrganizer::modList), bpy::return_value_policy()) - .def("profile", bpy::pure_virtual(&IOrganizer::profile), bpy::return_value_policy()) - .def("startApplication", bpy::pure_virtual(&IOrganizer::startApplication), ((bpy::arg("args")=QStringList()), (bpy::arg("cwd")=""), (bpy::arg("profile")="")), bpy::return_value_policy()) - //.def("waitForApplication", bpy::pure_virtual(&IOrganizer::waitForApplication), (bpy::arg("exitCode")=nullptr), bpy::return_value_policy()) - .def("onModInstalled", bpy::pure_virtual(&IOrganizer::onModInstalled)) - .def("onAboutToRun", bpy::pure_virtual(&IOrganizer::onAboutToRun)) - .def("onFinishedRun", bpy::pure_virtual(&IOrganizer::onFinishedRun)) - .def("refreshModList", bpy::pure_virtual(&IOrganizer::refreshModList), (bpy::arg("saveChanges")=true)) - .def("managedGame", bpy::pure_virtual(&IOrganizer::managedGame), bpy::return_value_policy()) - .def("modsSortedByProfilePriority", bpy::pure_virtual(&IOrganizer::modsSortedByProfilePriority)) - ; - - bpy::class_("IProfile") - .def("name", bpy::pure_virtual(&IProfile::name)) - .def("absolutePath", bpy::pure_virtual(&IProfile::absolutePath)) - .def("localSavesEnabled", bpy::pure_virtual(&IProfile::localSavesEnabled)) - .def("localSettingsEnabled", bpy::pure_virtual(&IProfile::localSettingsEnabled)) - ; - - bpy::class_("ModRepositoryBridge") - .def(bpy::init()) - .def("requestDescription", &ModRepositoryBridgeWrapper::requestDescription) - .def("requestFiles", &ModRepositoryBridgeWrapper::requestFiles) - .def("requestFileInfo", &ModRepositoryBridgeWrapper::requestFileInfo) - .def("requestToggleEndorsement", &ModRepositoryBridgeWrapper::requestToggleEndorsement) - .def("onFilesAvailable", &ModRepositoryBridgeWrapper::onFilesAvailable) - .def("onFileInfoAvailable", &ModRepositoryBridgeWrapper::onFileInfoAvailable) - .def("onDescriptionAvailable", &ModRepositoryBridgeWrapper::onDescriptionAvailable) - .def("onEndorsementToggled", &ModRepositoryBridgeWrapper::onEndorsementToggled) - .def("onRequestFailed", &ModRepositoryBridgeWrapper::onRequestFailed) - ; - - bpy::class_("IModRepositoryBridge") - .def("requestDescription", bpy::pure_virtual(&IModRepositoryBridge::requestDescription)) - .def("requestFiles", bpy::pure_virtual(&IModRepositoryBridge::requestFiles)) - .def("requestFileInfo", bpy::pure_virtual(&IModRepositoryBridge::requestFileInfo)) - .def("requestDownloadURL", bpy::pure_virtual(&IModRepositoryBridge::requestDownloadURL)) - .def("requestToggleEndorsement", bpy::pure_virtual(&IModRepositoryBridge::requestToggleEndorsement)) - ; - - bpy::class_("ModRepositoryFileInfo") - .def(bpy::init()) - .def(bpy::init>()) - .def("toString", &ModRepositoryFileInfo::toString) - .def("createFromJson", &ModRepositoryFileInfo::createFromJson).staticmethod("createFromJson") - .def_readwrite("name", &ModRepositoryFileInfo::name) - .def_readwrite("uri", &ModRepositoryFileInfo::uri) - .def_readwrite("description", &ModRepositoryFileInfo::description) - .def_readwrite("version", &ModRepositoryFileInfo::version) - .def_readwrite("newestVersion", &ModRepositoryFileInfo::newestVersion) - .def_readwrite("categoryID", &ModRepositoryFileInfo::categoryID) - .def_readwrite("modName", &ModRepositoryFileInfo::modName) - .def_readwrite("gameName", &ModRepositoryFileInfo::gameName) - .def_readwrite("modID", &ModRepositoryFileInfo::modID) - .def_readwrite("fileID", &ModRepositoryFileInfo::fileID) - .def_readwrite("fileSize", &ModRepositoryFileInfo::fileSize) - .def_readwrite("fileName", &ModRepositoryFileInfo::fileName) - .def_readwrite("fileCategory", &ModRepositoryFileInfo::fileCategory) - .def_readwrite("fileTime", &ModRepositoryFileInfo::fileTime) - .def_readwrite("repository", &ModRepositoryFileInfo::repository) - .def_readwrite("userData", &ModRepositoryFileInfo::userData) - ; - - bpy::class_("IDownloadManager") - .def("startDownloadURLs", bpy::pure_virtual(&IDownloadManager::startDownloadURLs)) - .def("startDownloadNexusFile", bpy::pure_virtual(&IDownloadManager::startDownloadNexusFile)) - .def("downloadPath", bpy::pure_virtual(&IDownloadManager::downloadPath)) - ; - - bpy::class_("IInstallationManager") - .def("extractFile", bpy::pure_virtual(&IInstallationManager::extractFile)) - .def("extractFiles", bpy::pure_virtual(&IInstallationManager::extractFiles)) - .def("installArchive", bpy::pure_virtual(&IInstallationManager::installArchive)) - .def("setURL", bpy::pure_virtual(&IInstallationManager::setURL)) - ; - - bpy::class_("IModInterface") - .def("name", bpy::pure_virtual(&IModInterface::name)) - .def("absolutePath", bpy::pure_virtual(&IModInterface::absolutePath)) - .def("setVersion", bpy::pure_virtual(&IModInterface::setVersion)) - .def("setNewestVersion", bpy::pure_virtual(&IModInterface::setNewestVersion)) - .def("setIsEndorsed", bpy::pure_virtual(&IModInterface::setIsEndorsed)) - .def("setNexusID", bpy::pure_virtual(&IModInterface::setNexusID)) - .def("addNexusCategory", bpy::pure_virtual(&IModInterface::addNexusCategory)) - .def("addCategory", bpy::pure_virtual(&IModInterface::addCategory)) - .def("removeCategory", bpy::pure_virtual(&IModInterface::removeCategory)) - .def("categories", bpy::pure_virtual(&IModInterface::categories)) - .def("setName", bpy::pure_virtual(&IModInterface::setName)) - .def("remove", bpy::pure_virtual(&IModInterface::remove)) - ; - - bpy::enum_("GuessQuality") - .value("invalid", MOBase::GUESS_INVALID) - .value("fallback", MOBase::GUESS_FALLBACK) - .value("good", MOBase::GUESS_GOOD) - .value("meta", MOBase::GUESS_META) - .value("preset", MOBase::GUESS_PRESET) - .value("user", MOBase::GUESS_USER) - ; - - bpy::class_, boost::noncopyable>("GuessedString") - .def("update", - static_cast &(GuessedValue::*)(const QString&, EGuessQuality)>(&GuessedValue::update), - bpy::return_value_policy(), updateWithQuality()) - .def("variants", &MOBase::GuessedValue::variants, bpy::return_value_policy()) - ; - - bpy::to_python_converter>(); - QFlags_from_python_obj(); - Functor0_converter(); // converter for the onRefreshed-callback - - bpy::class_("IPluginList") - .def("state", bpy::pure_virtual(&MOBase::IPluginList::state)) - .def("priority", bpy::pure_virtual(&MOBase::IPluginList::priority)) - .def("loadOrder", bpy::pure_virtual(&MOBase::IPluginList::loadOrder)) - .def("isMaster", bpy::pure_virtual(&MOBase::IPluginList::isMaster)) - .def("masters", bpy::pure_virtual(&MOBase::IPluginList::masters)) - .def("origin", bpy::pure_virtual(&MOBase::IPluginList::origin)) - .def("onRefreshed", bpy::pure_virtual(&MOBase::IPluginList::onRefreshed)) - .def("onPluginMoved", bpy::pure_virtual(&MOBase::IPluginList::onPluginMoved)) - .def("pluginNames", bpy::pure_virtual(&MOBase::IPluginList::pluginNames)) - .def("setState", bpy::pure_virtual(&MOBase::IPluginList::setState)) - .def("setLoadOrder", bpy::pure_virtual(&MOBase::IPluginList::setLoadOrder)) - ; - - bpy::to_python_converter>(); - QFlags_from_python_obj(); - Functor2_converter(); // converter for the onModStateChanged-callback - - bpy::class_("IModList") - .def("displayName", bpy::pure_virtual(&MOBase::IModList::displayName)) - .def("allMods", bpy::pure_virtual(&MOBase::IModList::allMods)) - .def("state", bpy::pure_virtual(&MOBase::IModList::state)) - .def("setActive", bpy::pure_virtual(&MOBase::IModList::setActive)) - .def("priority", bpy::pure_virtual(&MOBase::IModList::priority)) - .def("setPriority", bpy::pure_virtual(&MOBase::IModList::setPriority)) - .def("onModStateChanged", bpy::pure_virtual(&MOBase::IModList::onModStateChanged)) - .def("onModMoved", bpy::pure_virtual(&MOBase::IModList::onModMoved)) - ; - - bpy::class_("IPlugin"); - - bpy::class_("IPluginDiagnose") - .def("activeProblems", bpy::pure_virtual(&MOBase::IPluginDiagnose::activeProblems)) - .def("shortDescription", bpy::pure_virtual(&MOBase::IPluginDiagnose::shortDescription)) - .def("fullDescription", bpy::pure_virtual(&MOBase::IPluginDiagnose::fullDescription)) - .def("hasGuidedFix", bpy::pure_virtual(&MOBase::IPluginDiagnose::hasGuidedFix)) - .def("startGuidedFix", bpy::pure_virtual(&MOBase::IPluginDiagnose::startGuidedFix)) - .def("_invalidate", &IPluginDiagnoseWrapper::invalidate) - ; - - bpy::class_("Mapping") - .def_readwrite("source", &Mapping::source) - .def_readwrite("destination", &Mapping::destination) - .def_readwrite("isDirectory", &Mapping::isDirectory) - .def_readwrite("createTarget", &Mapping::createTarget) - ; - - bpy::class_("IPluginFileMapper") - .def("mappings", bpy::pure_virtual(&MOBase::IPluginFileMapper::mappings)) - ; - - bpy::enum_("LoadOrderMechanism") - .value("FileTime", MOBase::IPluginGame::LoadOrderMechanism::FileTime) - .value("PluginsTxt", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt) - ; - - bpy::enum_("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 doesn't actually do the conversion, but might be convenient for accessing the names for enum bits - bpy::enum_("ProfileSetting") - .value("mods", MOBase::IPluginGame::MODS) - .value("configuration", MOBase::IPluginGame::CONFIGURATION) - .value("savegames", MOBase::IPluginGame::SAVEGAMES) - .value("preferDefaults", MOBase::IPluginGame::PREFER_DEFAULTS) - ; - - bpy::to_python_converter>(); - QFlags_from_python_obj(); - - bpy::class_("IPluginGame") - .def("gameName", bpy::pure_virtual(&MOBase::IPluginGame::gameName)) - .def("initializeProfile", bpy::pure_virtual(&MOBase::IPluginGame::initializeProfile)) - .def("savegameExtension", bpy::pure_virtual(&MOBase::IPluginGame::savegameExtension)) - .def("savegameSEExtension", bpy::pure_virtual(&MOBase::IPluginGame::savegameSEExtension)) - .def("isInstalled", bpy::pure_virtual(&MOBase::IPluginGame::isInstalled)) - .def("gameIcon", bpy::pure_virtual(&MOBase::IPluginGame::gameIcon)) - .def("gameDirectory", bpy::pure_virtual(&MOBase::IPluginGame::gameDirectory)) - .def("dataDirectory", bpy::pure_virtual(&MOBase::IPluginGame::dataDirectory)) - .def("setGamePath", bpy::pure_virtual(&MOBase::IPluginGame::setGamePath)) - .def("documentsDirectory", bpy::pure_virtual(&MOBase::IPluginGame::documentsDirectory)) - .def("savesDirectory", bpy::pure_virtual(&MOBase::IPluginGame::savesDirectory)) - .def("executables", bpy::pure_virtual(&MOBase::IPluginGame::executables)) - .def("steamAPPId", bpy::pure_virtual(&MOBase::IPluginGame::steamAPPId)) - .def("primaryPlugins", bpy::pure_virtual(&MOBase::IPluginGame::primaryPlugins)) - .def("gameVariants", bpy::pure_virtual(&MOBase::IPluginGame::gameVariants)) - .def("setGameVariant", bpy::pure_virtual(&MOBase::IPluginGame::setGameVariant)) - .def("binaryName", bpy::pure_virtual(&MOBase::IPluginGame::binaryName)) - .def("gameShortName", bpy::pure_virtual(&MOBase::IPluginGame::gameShortName)) - .def("primarySources", bpy::pure_virtual(&MOBase::IPluginGame::primarySources)) - .def("validShortNames", bpy::pure_virtual(&MOBase::IPluginGame::validShortNames)) - .def("gameNexusName", bpy::pure_virtual(&MOBase::IPluginGame::gameNexusName)) - .def("iniFiles", bpy::pure_virtual(&MOBase::IPluginGame::iniFiles)) - .def("DLCPlugins", bpy::pure_virtual(&MOBase::IPluginGame::DLCPlugins)) - .def("CCPlugins", bpy::pure_virtual(&MOBase::IPluginGame::CCPlugins)) - .def("loadOrderMechanism", bpy::pure_virtual(&MOBase::IPluginGame::loadOrderMechanism)) - .def("sortMechanism", bpy::pure_virtual(&MOBase::IPluginGame::sortMechanism)) - .def("nexusModOrganizerID", bpy::pure_virtual(&MOBase::IPluginGame::nexusModOrganizerID)) - .def("nexusGameID", bpy::pure_virtual(&MOBase::IPluginGame::nexusGameID)) - .def("looksValid", bpy::pure_virtual(&MOBase::IPluginGame::looksValid)) - .def("gameVersion", bpy::pure_virtual(&MOBase::IPluginGame::gameVersion)) - .def("getLauncherName", bpy::pure_virtual(&MOBase::IPluginGame::getLauncherName)) - - //Plugin interface. - .def("init", bpy::pure_virtual(&MOBase::IPluginGame::init)) - .def("name", bpy::pure_virtual(&MOBase::IPluginGame::name)) - .def("author", bpy::pure_virtual(&MOBase::IPluginGame::author)) - .def("description", bpy::pure_virtual(&MOBase::IPluginGame::description)) - .def("version", bpy::pure_virtual(&MOBase::IPluginGame::version)) - .def("isActive", bpy::pure_virtual(&MOBase::IPluginGame::isActive)) - .def("settings", bpy::pure_virtual(&MOBase::IPluginGame::settings)) - - // The syntax has to differ slightly from C++ because these are templated - .def("featureBSAInvalidation", &MOBase::IPluginGame::feature, bpy::return_value_policy()) - .def("featureDataArchives", &MOBase::IPluginGame::feature, bpy::return_value_policy()) - .def("featureGamePlugins", &MOBase::IPluginGame::feature, bpy::return_value_policy()) - .def("featureLocalSavegames", &MOBase::IPluginGame::feature, bpy::return_value_policy()) - .def("featureSaveGameInfo", &MOBase::IPluginGame::feature, bpy::return_value_policy()) - .def("featureScriptExtender", &MOBase::IPluginGame::feature, bpy::return_value_policy()) - .def("featureUnmanagedMods", &MOBase::IPluginGame::feature, bpy::return_value_policy()) - ; - - bpy::enum_("InstallResult") - .value("success", MOBase::IPluginInstaller::RESULT_SUCCESS) - .value("failed", MOBase::IPluginInstaller::RESULT_FAILED) - .value("canceled", MOBase::IPluginInstaller::RESULT_CANCELED) - .value("manualRequested", MOBase::IPluginInstaller::RESULT_MANUALREQUESTED) - .value("notAttempted", MOBase::IPluginInstaller::RESULT_NOTATTEMPTED) - ; - - bpy::class_("IPluginInstallerCustom") - .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginInstallerCustom::setParentWidget)) - ; - - bpy::class_("IPluginModPage") - .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginModPage::setParentWidget)) - ; - - bpy::class_("IPluginPreview") - ; - - bpy::class_, boost::noncopyable>("IPluginTool") - .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginTool::setParentWidget)) - ; - - GuessedValue_converters(); - - //bpy::to_python_converter(); - - QList_from_python_obj(); - QList_from_python_obj(); - bpy::to_python_converter, - QList_to_python_list >(); - QList_from_python_obj(); - bpy::to_python_converter, - QList_to_python_list>(); - QList_from_python_obj(); - bpy::to_python_converter, - QList_to_python_list>(); - - QMap_converters(); - QMap_converters(); - - std_vector_from_python_obj(); - std_vector_from_python_obj(); - bpy::to_python_converter, - std_vector_to_python_list>(); - - stdset_from_python_list(); - - registerGameFeaturesPythonConverters(); -} - - -PythonRunner::PythonRunner(const MOBase::IOrganizer *moInfo) - : m_MOInfo(moInfo) -{ - m_PythonHome = new wchar_t[MAX_PATH + 1]; -} - -static const char *argv0 = "ModOrganizer.exe"; - - -bool PythonRunner::initPython(const QString &pythonPath) -{ - try { - if (!pythonPath.isEmpty() && !QFile::exists(pythonPath + "/python.exe")) { - return false; - } - pythonPath.toWCharArray(m_PythonHome); - if (!pythonPath.isEmpty()) { - Py_SetPythonHome(m_PythonHome); - } - - 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); - Py_OptimizeFlag = 2; - Py_NoSiteFlag = 1; - initPath(); - Py_InitializeEx(0); - - if (!Py_IsInitialized()) { - return false; - } - - PySys_SetArgv(0, (wchar_t**)&argBuffer); - - bpy::object mainModule = bpy::import("__main__"); - bpy::object mainNamespace = mainModule.attr("__dict__"); - mainNamespace["sys"] = bpy::import("sys"); - bpy::import("site"); - mainNamespace["io"] = bpy::import("io"); - bpy::exec("s_ErrIO = io.StringIO()\n" - "sys.stderr = s_ErrIO", - mainNamespace); - return true; - } catch (const bpy::error_already_set&) { - qDebug("failed to init python"); - PyErr_Print(); - if (PyErr_Occurred()) { - PyErr_Print(); - } else { - qCritical("An unexpected C++ exception was thrown in python code"); - } - return false; - } -} - - -bool handled_exec_file(bpy::str filename, bpy::object globals = bpy::object(), bpy::object locals = bpy::object()) -{ - return bpy::handle_exception(std::bind(bpy::exec_file, filename, globals, locals)); -} - - -#define TRY_PLUGIN_TYPE(type, var) do { \ - bpy::extract extr(var); \ - if (extr.check()) { \ - QObject *res = extr; \ - interfaceList.append(res); \ - }\ - } while (false) - - -void PythonRunner::initPath() -{ - static QStringList paths = { - QCoreApplication::applicationDirPath() + "/pythoncore", - m_MOInfo->pluginDataPath() - }; - - Py_SetPath(paths.join(';').toStdWString().c_str()); -} - - -QList PythonRunner::instantiate(const QString &pluginName) -{ - try { - GILock lock; - bpy::object mainModule = bpy::import("__main__"); - bpy::object moduleNamespace = mainModule.attr("__dict__"); - - bpy::object sys = bpy::import("sys"); - moduleNamespace["sys"] = sys; - moduleNamespace["mobase"] = bpy::import("mobase"); - - std::string temp = ToString(pluginName); - if (handled_exec_file(temp.c_str(), moduleNamespace)) { - reportPythonError(); - return QList(); - } - m_PythonObjects[pluginName] = moduleNamespace["createPlugin"](); - - bpy::object pluginObj = m_PythonObjects[pluginName]; - QList interfaceList; - TRY_PLUGIN_TYPE(IPluginGame, pluginObj); - // Must try the wrapper because it's only a plugin extension interface in C++, so doesn't extend QObject - TRY_PLUGIN_TYPE(IPluginDiagnoseWrapper, pluginObj); - // Must try the wrapper because it's only a plugin extension interface in C++, so doesn't extend QObject - TRY_PLUGIN_TYPE(IPluginFileMapperWrapper, pluginObj); - TRY_PLUGIN_TYPE(IPluginInstallerCustom, pluginObj); - TRY_PLUGIN_TYPE(IPluginModPage, pluginObj); - TRY_PLUGIN_TYPE(IPluginPreview, pluginObj); - TRY_PLUGIN_TYPE(IPluginTool, pluginObj); - if (interfaceList.isEmpty()) - TRY_PLUGIN_TYPE(IPluginWrapper, pluginObj); - - return interfaceList; - } catch (const bpy::error_already_set&) { - qWarning("failed to run python script \"%s\"", qPrintable(pluginName)); - reportPythonError(); - } - return QList(); -} - -bool PythonRunner::isPythonInstalled() const -{ - return Py_IsInitialized() != 0; -} - - -bool PythonRunner::isPythonVersionSupported() const -{ - const char *version = Py_GetVersion(); - return strstr(version, "3.7") == version; -} - +#include "pythonrunner.h" + +#pragma warning( disable : 4100 ) +#pragma warning( disable : 4996 ) + +#include +#include +#include +#include +#include "uibasewrappers.h" +#include "proxypluginwrappers.h" +#include "gamefeatureswrappers.h" +#include "sipApiAccess.h" + +#include +#include +#include +#include +#include +#include + +// sip and qt slots seems to conflict +#include + +#ifndef Q_MOC_RUN +#include +#endif + + +MOBase::IOrganizer *s_Organizer = nullptr; + + + +class PythonRunner : public IPythonRunner +{ + +public: + PythonRunner(const MOBase::IOrganizer *moInfo); + bool initPython(const QString &pythonDir); + QList instantiate(const QString &pluginName); + bool isPythonInstalled() const; + bool isPythonVersionSupported() const; + +private: + + void initPath(); + +private: + std::map m_PythonObjects; + const MOBase::IOrganizer *m_MOInfo; + wchar_t *m_PythonHome; +}; + + +IPythonRunner *CreatePythonRunner(MOBase::IOrganizer *moInfo, const QString &pythonDir) +{ + s_Organizer = moInfo; + PythonRunner *result = new PythonRunner(moInfo); + if (result->initPython(pythonDir)) { + return result; + } else { + delete result; + return nullptr; + } +} + + +using namespace MOBase; + +namespace bpy = boost::python; + +struct QString_to_python_str +{ + static PyObject *convert(const QString &str) { + // It's safer to explicitly convert to unicode as if we don't, this can return either str or unicode without it being easy to know which to expect + bpy::object pyStr = bpy::object(str.toUtf8().constData()); + if (SIPBytes_Check(pyStr.ptr())) + pyStr = pyStr.attr("decode")("utf-8"); + return bpy::incref(pyStr.ptr()); + } +}; + +template +struct QFlags_to_int +{ + static PyObject *convert(const QFlags &flags) { + return bpy::incref(bpy::object(static_cast(flags)).ptr()); + } +}; + + +struct QString_from_python_str +{ + QString_from_python_str() { + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id()); + } + + static void *convertible(PyObject *objPtr) { + return SIPBytes_Check(objPtr) || PyUnicode_Check(objPtr) ? objPtr : nullptr; + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { + // Ensure the string uses 8-bit characters + PyObject *strPtr = PyUnicode_Check(objPtr) ? PyUnicode_AsUTF8String(objPtr) : objPtr; + // Extract the character data from the python string + const char* value = SIPBytes_AsString(strPtr); + // Deallocate local copy if one was made + if (strPtr != objPtr) + Py_DecRef(strPtr); + assert(value != nullptr); + + // allocate storage + void *storage = ((bpy::converter::rvalue_from_python_storage*)data)->storage.bytes; + // construct QString in the allocated mr + new (storage) QString(value); + + data->convertible = storage; + } +}; + + +template +struct GuessedValue_converters +{ + struct GuessedValue_to_python + { + static PyObject *convert(const GuessedValue &var) { + bpy::list result; + const std::set &values = var.variants(); + for (auto iter = values.begin(); iter != values.end(); ++iter) { + result.append(bpy::make_tuple(*iter, GUESS_GOOD)); + } + return bpy::incref(result.ptr()); + } + }; + + struct GuessedValue_from_python + { + GuessedValue_from_python() { + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id >()); + } + + static void *convertible(PyObject *objPtr) { + if PyList_Check(objPtr) { + return objPtr; + } else { + return nullptr; + } + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data* data) { + void *storage = ((bpy::converter::rvalue_from_python_storage >*)data)->storage.bytes; + GuessedValue *result = new (storage) GuessedValue(); + + bpy::list source(bpy::handle<>(bpy::borrowed(objPtr))); + int length = bpy::len(source); + for (int i = 0; i < length; ++i) { + bpy::tuple cell = bpy::extract(source[i]); + result->update(bpy::extract(cell[0]), bpy::extract(cell[1])); + } + + data->convertible = storage; + } + }; + + GuessedValue_converters() + { + GuessedValue_from_python(); + bpy::to_python_converter, GuessedValue_to_python>(); + } +}; + + +template +struct QMap_converters +{ + struct QMap_to_python + { + static PyObject *convert(const QMap &map) { + bpy::dict result; + QMapIterator iter(map); + while (iter.hasNext()) { + iter.next(); + result[bpy::object(iter.key())] = bpy::object(iter.value()); + } + return bpy::incref(result.ptr()); + } + }; + + struct QMap_from_python + { + QMap_from_python() { + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); + } + + static void *convertible(PyObject *objPtr) { + return PyDict_Check(objPtr) ? objPtr : nullptr; + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { + void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; + QMap *result = new (storage) QMap(); + bpy::dict source(bpy::handle<>(bpy::borrowed(objPtr))); + bpy::list keys = source.keys(); + int len = bpy::len(keys); + for (int i = 0; i < len; ++i) { + bpy::object pyKey = keys[i]; + (*result)[bpy::extract(pyKey)] = bpy::extract(source[pyKey]); + } + + data->convertible = storage; + } + }; + + QMap_converters() + { + QMap_from_python(); + bpy::to_python_converter, QMap_to_python >(); + } +}; + + +struct QVariant_to_python_obj +{ + static PyObject *convert(const QVariant &var) { + switch (var.type()) { + case QVariant::Int: return SIPLong_FromLong(var.toInt()); + case QVariant::UInt: return PyLong_FromUnsignedLong(var.toUInt()); + case QVariant::Bool: return PyBool_FromLong(var.toBool()); + case QVariant::String: return bpy::incref(bpy::object(var.toString()).ptr()); + case QVariant::List: { + return bpy::incref(bpy::object(var.toList()).ptr()); + } break; + case QVariant::Map: { + return bpy::incref(bpy::object(var.toMap()).ptr()); + } break; + default: { + PyErr_Format(PyExc_TypeError, "type unsupported: %d", var.type()); + throw bpy::error_already_set(); + } break; + } + } +}; + + +struct QVariant_from_python_obj +{ + QVariant_from_python_obj() { + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id()); + } + + static void *convertible(PyObject *objPtr) { + if (!SIPBytes_Check(objPtr) && !PyLong_Check(objPtr) && + !PyBool_Check(objPtr) && !PyList_Check(objPtr)) { + return nullptr; + } + return objPtr; + } + + template + static void constructVariant(const T &value, bpy::converter::rvalue_from_python_stage1_data *data) { + void* storage = ((bpy::converter::rvalue_from_python_storage*)data)->storage.bytes; + + new (storage) QVariant(value); + + data->convertible = storage; + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { + // PyBools will also return true for SIPLong_Check but not the other way around, so the order + // here is relevant + if (PyList_Check(objPtr)) { + constructVariant(bpy::extract(objPtr)(), data); + } else if (PyDict_Check(objPtr)) { + constructVariant(bpy::extract(objPtr)(), data); + } else if (SIPBytes_Check(objPtr) || PyUnicode_Check(objPtr)) { + constructVariant(bpy::extract(objPtr)(), data); + } else if (PyBool_Check(objPtr)) { + constructVariant(bpy::extract(objPtr)(), data); + } else if (SIPLong_Check(objPtr)) { + //QVariant doesn't have long. It has int or long long. Given that on m/s, + //long is 32 bits for 32- and 64- bit code... + constructVariant(bpy::extract(objPtr)(), data); + } else { + PyErr_SetString(PyExc_TypeError, "type unsupported"); + throw bpy::error_already_set(); + } + } +}; + + +template +struct QList_to_python_list +{ + static PyObject *convert(const QList &list) + { + bpy::list pyList; + + try { + for (const T &item : list) { + pyList.append(item); + } + } catch (const bpy::error_already_set&) { + reportPythonError(); + } + PyObject *res = bpy::incref(pyList.ptr()); + return res; + } +}; + + +template +struct QList_from_python_obj +{ + QList_from_python_obj() { + bpy::converter::registry::push_back( + &convertible, + &construct, + bpy::type_id >()); + } + + static void* convertible(PyObject *objPtr) { + if (PyList_Check(objPtr)) return objPtr; + return nullptr; + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { + void *storage = ((bpy::converter::rvalue_from_python_storage >*)data)->storage.bytes; + QList *result = new (storage) QList(); + bpy::list source(bpy::handle<>(bpy::borrowed(objPtr))); + int length = bpy::len(source); + for (int i = 0; i < length; ++i) { + result->append(bpy::extract(source[i])); + } + + data->convertible = storage; + } +}; + + +template +struct std_vector_to_python_list +{ + static PyObject *convert(const std::vector &vector) + { + bpy::list pyList; + + try { + for (const T &item : vector) + pyList.append(item); + } + catch (const bpy::error_already_set&) { + reportPythonError(); + } + + return bpy::incref(pyList.ptr()); + } +}; + + +template +struct std_vector_from_python_obj +{ + std_vector_from_python_obj() { + bpy::converter::registry::push_back( + &convertible, + &construct, + bpy::type_id >()); + } + + static void* convertible(PyObject *objPtr) { + if (PyList_Check(objPtr)) return objPtr; + return nullptr; + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { + void *storage = ((bpy::converter::rvalue_from_python_storage >*)data)->storage.bytes; + std::vector *result = new (storage) std::vector(); + bpy::list source(bpy::handle<>(bpy::borrowed(objPtr))); + int length = bpy::len(source); + for (int i = 0; i < length; ++i) { + result->push_back(bpy::extract(source[i])); + } + + data->convertible = storage; + } +}; + + +template +struct QFlags_from_python_obj +{ + QFlags_from_python_obj() { + bpy::converter::registry::push_back( + &convertible, + &construct, + bpy::type_id>()); + } + + static void* convertible(PyObject *objPtr) { + return SIPLong_Check(objPtr) ? objPtr : nullptr; + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { + int intVersion = (int)SIPLong_AsLong(objPtr); + T tVersion = (T)intVersion; + void *storage = ((bpy::converter::rvalue_from_python_storage> *)data)->storage.bytes; + new (storage) QFlags(tVersion); + + data->convertible = storage; + } +}; + + +template +struct stdset_from_python_list +{ + stdset_from_python_list() { + bpy::converter::registry::push_back( + &convertible, + &construct, + bpy::type_id >()); + } + + static void* convertible(PyObject *objPtr) { + if (PyList_Check(objPtr)) return objPtr; + return nullptr; + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { + void *storage = ((bpy::converter::rvalue_from_python_storage >*)data)->storage.bytes; + std::set *result = new (storage) std::set(); + + bpy::list source(bpy::handle<>(bpy::borrowed(objPtr))); + int length = bpy::len(source); + for (int i = 0; i < length; ++i) { + result->insert(bpy::extract(source[i])); + } + + data->convertible = storage; + } +}; + + +struct IModRepositoryBridge_to_python +{ + static PyObject *convert(IModRepositoryBridge *bridge) + { + ModRepositoryBridgeWrapper wrapper(bridge); + + return bpy::incref(bpy::object(wrapper).ptr()); + } +}; + + + +template struct MetaData; + +template <> struct MetaData { static const char *className() { return "QObject"; } }; +template <> struct MetaData { static const char *className() { return "QObject"; } }; +template <> struct MetaData { static const char *className() { return "QObject"; } }; +template <> struct MetaData { static const char *className() { return "QWidget"; } }; +template <> struct MetaData { static const char *className() { return "QDateTime"; } }; +template <> struct MetaData { static const char *className() { return "QDir"; } }; +template <> struct MetaData { static const char *className() { return "QFileInfo"; } }; +template <> struct MetaData { static const char *className() { return "QIcon"; } }; +template <> struct MetaData { static const char *className() { return "QSize"; } }; +template <> struct MetaData { static const char *className() { return "QStringList"; } }; +template <> struct MetaData { static const char *className() { return "QUrl"; } }; +template <> struct MetaData { static const char *className() { return "QVariant"; } }; + + +template +PyObject *toPyQt(T *objPtr) +{ + if (objPtr == nullptr) { + qDebug("no input object"); + return bpy::incref(Py_None); + } + const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + + if (type == nullptr) { + qDebug("failed to determine type: %s", MetaData::className()); + return bpy::incref(Py_None); + } + + PyObject *sipObj = sipAPI()->api_convert_from_type(objPtr, type, 0); + if (sipObj == nullptr) { + qDebug("failed to convert"); + return bpy::incref(Py_None); + } + return bpy::incref(sipObj); +} + + +template +struct QClass_converters +{ + struct QClass_to_PyQt + { + template + static typename std::enable_if_t, T*> getSafeCopy(T *qClass) + { + return new T(*qClass); + } + + template + static typename std::enable_if_t, T*> getSafeCopy(T *qClass) + { + return qClass; + } + + static PyObject *convert(const T &object) { + const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + if (type == nullptr) { + return bpy::incref(Py_None); + } + + PyObject *sipObj = sipAPI()->api_convert_from_type((void*)getSafeCopy((T*)&object), type, 0); + if (sipObj == nullptr) { + return bpy::incref(Py_None); + } + + if (std::is_copy_constructible_v) + // Ensure Python deletes the C++ component + sipAPI()->api_transfer_back(sipObj); + + return bpy::incref(sipObj); + } + + static PyObject *convert(T *object) { + if (object == nullptr) { + return bpy::incref(Py_None); + } + + const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + if (type == nullptr) { + return bpy::incref(Py_None); + } + + PyObject *sipObj = sipAPI()->api_convert_from_type(getSafeCopy(object), type, 0); + if (sipObj == nullptr) { + return bpy::incref(Py_None); + } + + if (std::is_copy_constructible_v) + // Ensure Python deletes the C++ component + sipAPI()->api_transfer_back(sipObj); + + return bpy::incref(sipObj); + } + + static PyObject *convert(const T *object) { + return convert((T*)object); + } + }; + + static void *QClass_from_PyQt(PyObject *objPtr) + { + // This would transfer responsibility for deconstructing the object to C++, but Boost assumes l-value converters (such as this) don't do that + // Instead, this should be called within the wrappers for functions which return deletable pointers. + //sipAPI()->api_transfer_to(objPtr, 0); + if (PyObject_TypeCheck(objPtr, sipAPI()->api_simplewrapper_type)) { + sipSimpleWrapper *wrapper; + wrapper = reinterpret_cast(objPtr); + return wrapper->data; + } else if (PyObject_TypeCheck(objPtr, sipAPI()->api_wrapper_type)) { + sipWrapper *wrapper; + wrapper = reinterpret_cast(objPtr); + return wrapper->super.data; + } else { + if (std::is_same_v) + { + // QStringLists aren't wrapped by PyQt - regular Python string/unicode lists are used instead + bpy::extract> extractor(objPtr); + if (extractor.check()) + return new QStringList(extractor()); + } + PyErr_SetString(PyExc_TypeError, "type not wrapped"); + bpy::throw_error_already_set(); + } + return new void*; + } + + QClass_converters() + { + bpy::converter::registry::insert(&QClass_from_PyQt, bpy::type_id()); + bpy::to_python_converter(); + bpy::to_python_converter(); + bpy::to_python_converter(); + } +}; + + +template +struct QInterface_converters +{ + struct QInterface_to_PyQt + { + static PyObject *convert(const T &object) { + const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + if (type == nullptr) { + return bpy::incref(Py_None); + } + + PyObject *sipObj = sipAPI()->api_convert_from_type((void*)(&object), type, 0); + if (sipObj == nullptr) { + return bpy::incref(Py_None); + } + + return bpy::incref(sipObj); + } + + static PyObject *convert(T *object) { + if (object == nullptr) { + return bpy::incref(Py_None); + } + + const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + if (type == nullptr) { + return bpy::incref(Py_None); + } + + PyObject *sipObj = sipAPI()->api_convert_from_type(object, type, 0); + if (sipObj == nullptr) { + return bpy::incref(Py_None); + } + + return bpy::incref(sipObj); + } + + static PyObject *convert(const T *object) { + return convert((T*)object); + } + }; + + static void *QInterface_from_PyQt(PyObject *objPtr) + { + if (!PyObject_TypeCheck(objPtr, sipAPI()->api_wrapper_type)) { + bpy::throw_error_already_set(); + } + + // This would transfer responsibility for deconstructing the object to C++, but Boost assumes l-value converters (such as this) don't do that + // Instead, this should be called within the wrappers for functions which return deletable pointers. + //sipAPI()->api_transfer_to(objPtr, 0); + + sipSimpleWrapper *wrapper = reinterpret_cast(objPtr); + return wrapper->data; + } + + QInterface_converters() + { + bpy::converter::registry::insert(&QInterface_from_PyQt, bpy::type_id()); + bpy::to_python_converter(); + bpy::to_python_converter(); + } +}; + + +int getArgCount(PyObject *object) { + int result = 0; + PyObject *funcCode = PyObject_GetAttrString(object, "__code__"); + if (funcCode) { + PyObject *argCount = PyObject_GetAttrString(funcCode, "co_argcount"); + if(argCount) { + result = SIPLong_AsLong(argCount); + Py_DECREF(argCount); + } + Py_DECREF(funcCode); + } + return result; +} + +template +struct Functor0_converter +{ + + struct FunctorWrapper + { + FunctorWrapper(boost::python::object callable) : m_Callable(callable) { + } + + RET operator()() { + GILock lock; + return (RET) m_Callable(); + } + + boost::python::object m_Callable; + }; + + Functor0_converter() + { + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); + } + + static void *convertible(PyObject *object) + { + if (!PyCallable_Check(object) + || (getArgCount(object) != 0)) { + return nullptr; + } + return object; + } + + static void construct(PyObject *object, bpy::converter::rvalue_from_python_stage1_data *data) + { + bpy::object callable(bpy::handle<>(bpy::borrowed(object))); + void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; + new (storage) std::function(FunctorWrapper(callable)); + data->convertible = storage; + } +}; + + +template +struct Functor1_converter +{ + + struct FunctorWrapper + { + FunctorWrapper(boost::python::object callable) : m_Callable(callable) { + } + + RET operator()(const PAR1 ¶m1) { + GILock lock; + return (RET) m_Callable(param1); + } + + boost::python::object m_Callable; + }; + + Functor1_converter() + { + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); + } + + static void *convertible(PyObject *object) + { + if (!PyCallable_Check(object) + || (getArgCount(object) != 1)) { + return nullptr; + } + return object; + } + + static void construct(PyObject *object, bpy::converter::rvalue_from_python_stage1_data *data) + { + bpy::object callable(bpy::handle<>(bpy::borrowed(object))); + void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; + new (storage) std::function(FunctorWrapper(callable)); + data->convertible = storage; + } +}; + + +template +struct Functor2_converter +{ + + struct FunctorWrapper + { + FunctorWrapper(boost::python::object callable) : m_Callable(callable) { + } + + RET operator()(const PAR1 ¶m1, const PAR2 ¶m2) { + GILock lock; + return (RET) m_Callable(param1, param2); + } + + boost::python::object m_Callable; + }; + + Functor2_converter() + { + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); + } + + static void *convertible(PyObject *object) + { + if (!PyCallable_Check(object) + || (getArgCount(object) != 2)) { + return nullptr; + } + return object; + } + + static void construct(PyObject *object, bpy::converter::rvalue_from_python_stage1_data *data) + { + bpy::object callable(bpy::handle<>(bpy::borrowed(object))); + void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; + new (storage) std::function(FunctorWrapper(callable)); + data->convertible = storage; + } +}; + + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(updateWithQuality, MOBase::GuessedValue::update, 2, 2) + + +BOOST_PYTHON_MODULE(mobase) +{ + PyEval_InitThreads(); + bpy::to_python_converter(); + QVariant_from_python_obj(); + + bpy::to_python_converter(); + QString_from_python_str(); + + //QClass_converters(); + QClass_converters(); + QClass_converters(); + QClass_converters(); + QClass_converters(); + QClass_converters(); + QClass_converters(); + QClass_converters(); + QClass_converters(); + QInterface_converters(); + + + bpy::def("toPyQt", &toPyQt); + bpy::def("toPyQt", &toPyQt); + + bpy::enum_("ReleaseType") + .value("final", MOBase::VersionInfo::RELEASE_FINAL) + .value("candidate", MOBase::VersionInfo::RELEASE_CANDIDATE) + .value("beta", MOBase::VersionInfo::RELEASE_BETA) + .value("alpha", MOBase::VersionInfo::RELEASE_ALPHA) + .value("prealpha", MOBase::VersionInfo::RELEASE_PREALPHA) + ; + + bpy::enum_("VersionScheme") + .value("discover", MOBase::VersionInfo::SCHEME_DISCOVER) + .value("regular", MOBase::VersionInfo::SCHEME_REGULAR) + .value("decimalmark", MOBase::VersionInfo::SCHEME_DECIMALMARK) + .value("numbersandletters", MOBase::VersionInfo::SCHEME_NUMBERSANDLETTERS) + .value("date", MOBase::VersionInfo::SCHEME_DATE) + .value("literal", MOBase::VersionInfo::SCHEME_LITERAL) + ; + + bpy::class_("VersionInfo") + .def(bpy::init()) + .def(bpy::init()) + .def(bpy::init()) + .def(bpy::init()) + .def(bpy::init()) + .def(bpy::init()) + .def("clear", &VersionInfo::clear) + .def("parse", &VersionInfo::parse) + .def("canonicalString", &VersionInfo::canonicalString) + .def("displayString", &VersionInfo::displayString) + .def("isValid", &VersionInfo::isValid) + .def("scheme", &VersionInfo::scheme) + .def(bpy::self < bpy::self) + .def(bpy::self > bpy::self) + .def(bpy::self <= bpy::self) + .def(bpy::self >= bpy::self) + .def(bpy::self != bpy::self) + .def(bpy::self == bpy::self) + ; + + bpy::class_("PluginSetting", bpy::init()); + + bpy::class_("ExecutableInfo", bpy::init()) + .def("withArgument", &ExecutableInfo::withArgument, bpy::return_value_policy()) + .def("withWorkingDirectory", &ExecutableInfo::withWorkingDirectory, bpy::return_value_policy()) + .def("withSteamAppId", &ExecutableInfo::withSteamAppId, bpy::return_value_policy()) + .def("asCustom", &ExecutableInfo::asCustom, bpy::return_value_policy()) + .def("isValid", &ExecutableInfo::isValid) + .def("title", &ExecutableInfo::title) + .def("binary", &ExecutableInfo::binary) + .def("arguments", &ExecutableInfo::arguments) + .def("workingDirectory", &ExecutableInfo::workingDirectory) + .def("steamAppID", &ExecutableInfo::steamAppID) + .def("isCustom", &ExecutableInfo::isCustom) + ; + + bpy::class_("ISaveGame") + .def("getFilename", bpy::pure_virtual(&ISaveGame::getFilename)) + .def("getCreationTime", bpy::pure_virtual(&ISaveGame::getCreationTime)) + .def("getSaveGroupIdentifier", bpy::pure_virtual(&ISaveGame::getSaveGroupIdentifier)) + .def("allFiles", bpy::pure_virtual(&ISaveGame::allFiles)) + .def("hasScriptExtenderFile", bpy::pure_virtual(&ISaveGame::hasScriptExtenderFile)) + ; + + // TODO: ISaveGameInfoWidget bindings + + Functor1_converter(); + Functor1_converter(); + Functor1_converter(); + Functor2_converter(); + + bpy::class_("IOrganizer") + .def("createNexusBridge", bpy::pure_virtual(&IOrganizer::createNexusBridge), bpy::return_value_policy()) + .def("profileName", bpy::pure_virtual(&IOrganizer::profileName)) + .def("profilePath", bpy::pure_virtual(&IOrganizer::profilePath)) + .def("downloadsPath", bpy::pure_virtual(&IOrganizer::downloadsPath)) + .def("overwritePath", bpy::pure_virtual(&IOrganizer::overwritePath)) + .def("basePath", bpy::pure_virtual(&IOrganizer::basePath)) + .def("appVersion", bpy::pure_virtual(&IOrganizer::appVersion)) + .def("getMod", bpy::pure_virtual(&IOrganizer::getMod), bpy::return_value_policy()) + .def("createMod", bpy::pure_virtual(&IOrganizer::createMod), bpy::return_value_policy()) + .def("getGame", bpy::pure_virtual(&IOrganizer::getGame), bpy::return_value_policy()) + .def("removeMod", bpy::pure_virtual(&IOrganizer::removeMod)) + .def("modDataChanged", bpy::pure_virtual(&IOrganizer::modDataChanged)) + .def("pluginSetting", bpy::pure_virtual(&IOrganizer::pluginSetting)) + .def("setPluginSetting", bpy::pure_virtual(&IOrganizer::pluginSetting)) + .def("persistent", bpy::pure_virtual(&IOrganizer::persistent)) + .def("setPersistent", bpy::pure_virtual(&IOrganizer::setPersistent)) + .def("pluginDataPath", bpy::pure_virtual(&IOrganizer::pluginDataPath)) + .def("installMod", bpy::pure_virtual(&IOrganizer::installMod),(bpy::arg("nameSuggestion")=""), bpy::return_value_policy()) + .def("resolvePath", bpy::pure_virtual(&IOrganizer::resolvePath)) + .def("listDirectories", bpy::pure_virtual(&IOrganizer::listDirectories)) + .def("findFiles", bpy::pure_virtual(&IOrganizer::findFiles)) + .def("getFileOrigins", bpy::pure_virtual(&IOrganizer::getFileOrigins)) + .def("findFileInfos", bpy::pure_virtual(&IOrganizer::findFileInfos)) + .def("downloadManager", bpy::pure_virtual(&IOrganizer::downloadManager), bpy::return_value_policy()) + .def("pluginList", bpy::pure_virtual(&IOrganizer::pluginList), bpy::return_value_policy()) + .def("modList", bpy::pure_virtual(&IOrganizer::modList), bpy::return_value_policy()) + .def("profile", bpy::pure_virtual(&IOrganizer::profile), bpy::return_value_policy()) + .def("startApplication", bpy::pure_virtual(&IOrganizer::startApplication), ((bpy::arg("args")=QStringList()), (bpy::arg("cwd")=""), (bpy::arg("profile")="")), bpy::return_value_policy()) + //.def("waitForApplication", bpy::pure_virtual(&IOrganizer::waitForApplication), (bpy::arg("exitCode")=nullptr), bpy::return_value_policy()) + .def("onModInstalled", bpy::pure_virtual(&IOrganizer::onModInstalled)) + .def("onAboutToRun", bpy::pure_virtual(&IOrganizer::onAboutToRun)) + .def("onFinishedRun", bpy::pure_virtual(&IOrganizer::onFinishedRun)) + .def("refreshModList", bpy::pure_virtual(&IOrganizer::refreshModList), (bpy::arg("saveChanges")=true)) + .def("managedGame", bpy::pure_virtual(&IOrganizer::managedGame), bpy::return_value_policy()) + .def("modsSortedByProfilePriority", bpy::pure_virtual(&IOrganizer::modsSortedByProfilePriority)) + ; + + bpy::class_("IProfile") + .def("name", bpy::pure_virtual(&IProfile::name)) + .def("absolutePath", bpy::pure_virtual(&IProfile::absolutePath)) + .def("localSavesEnabled", bpy::pure_virtual(&IProfile::localSavesEnabled)) + .def("localSettingsEnabled", bpy::pure_virtual(&IProfile::localSettingsEnabled)) + ; + + bpy::class_("ModRepositoryBridge") + .def(bpy::init()) + .def("requestDescription", &ModRepositoryBridgeWrapper::requestDescription) + .def("requestFiles", &ModRepositoryBridgeWrapper::requestFiles) + .def("requestFileInfo", &ModRepositoryBridgeWrapper::requestFileInfo) + .def("requestToggleEndorsement", &ModRepositoryBridgeWrapper::requestToggleEndorsement) + .def("onFilesAvailable", &ModRepositoryBridgeWrapper::onFilesAvailable) + .def("onFileInfoAvailable", &ModRepositoryBridgeWrapper::onFileInfoAvailable) + .def("onDescriptionAvailable", &ModRepositoryBridgeWrapper::onDescriptionAvailable) + .def("onEndorsementToggled", &ModRepositoryBridgeWrapper::onEndorsementToggled) + .def("onRequestFailed", &ModRepositoryBridgeWrapper::onRequestFailed) + ; + + bpy::class_("IModRepositoryBridge") + .def("requestDescription", bpy::pure_virtual(&IModRepositoryBridge::requestDescription)) + .def("requestFiles", bpy::pure_virtual(&IModRepositoryBridge::requestFiles)) + .def("requestFileInfo", bpy::pure_virtual(&IModRepositoryBridge::requestFileInfo)) + .def("requestDownloadURL", bpy::pure_virtual(&IModRepositoryBridge::requestDownloadURL)) + .def("requestToggleEndorsement", bpy::pure_virtual(&IModRepositoryBridge::requestToggleEndorsement)) + ; + + bpy::class_("ModRepositoryFileInfo") + .def(bpy::init()) + .def(bpy::init>()) + .def("toString", &ModRepositoryFileInfo::toString) + .def("createFromJson", &ModRepositoryFileInfo::createFromJson).staticmethod("createFromJson") + .def_readwrite("name", &ModRepositoryFileInfo::name) + .def_readwrite("uri", &ModRepositoryFileInfo::uri) + .def_readwrite("description", &ModRepositoryFileInfo::description) + .def_readwrite("version", &ModRepositoryFileInfo::version) + .def_readwrite("newestVersion", &ModRepositoryFileInfo::newestVersion) + .def_readwrite("categoryID", &ModRepositoryFileInfo::categoryID) + .def_readwrite("modName", &ModRepositoryFileInfo::modName) + .def_readwrite("gameName", &ModRepositoryFileInfo::gameName) + .def_readwrite("modID", &ModRepositoryFileInfo::modID) + .def_readwrite("fileID", &ModRepositoryFileInfo::fileID) + .def_readwrite("fileSize", &ModRepositoryFileInfo::fileSize) + .def_readwrite("fileName", &ModRepositoryFileInfo::fileName) + .def_readwrite("fileCategory", &ModRepositoryFileInfo::fileCategory) + .def_readwrite("fileTime", &ModRepositoryFileInfo::fileTime) + .def_readwrite("repository", &ModRepositoryFileInfo::repository) + .def_readwrite("userData", &ModRepositoryFileInfo::userData) + ; + + bpy::class_("IDownloadManager") + .def("startDownloadURLs", bpy::pure_virtual(&IDownloadManager::startDownloadURLs)) + .def("startDownloadNexusFile", bpy::pure_virtual(&IDownloadManager::startDownloadNexusFile)) + .def("downloadPath", bpy::pure_virtual(&IDownloadManager::downloadPath)) + ; + + bpy::class_("IInstallationManager") + .def("extractFile", bpy::pure_virtual(&IInstallationManager::extractFile)) + .def("extractFiles", bpy::pure_virtual(&IInstallationManager::extractFiles)) + .def("installArchive", bpy::pure_virtual(&IInstallationManager::installArchive)) + .def("setURL", bpy::pure_virtual(&IInstallationManager::setURL)) + ; + + bpy::class_("IModInterface") + .def("name", bpy::pure_virtual(&IModInterface::name)) + .def("absolutePath", bpy::pure_virtual(&IModInterface::absolutePath)) + .def("setVersion", bpy::pure_virtual(&IModInterface::setVersion)) + .def("setNewestVersion", bpy::pure_virtual(&IModInterface::setNewestVersion)) + .def("setIsEndorsed", bpy::pure_virtual(&IModInterface::setIsEndorsed)) + .def("setNexusID", bpy::pure_virtual(&IModInterface::setNexusID)) + .def("addNexusCategory", bpy::pure_virtual(&IModInterface::addNexusCategory)) + .def("addCategory", bpy::pure_virtual(&IModInterface::addCategory)) + .def("removeCategory", bpy::pure_virtual(&IModInterface::removeCategory)) + .def("categories", bpy::pure_virtual(&IModInterface::categories)) + .def("setName", bpy::pure_virtual(&IModInterface::setName)) + .def("remove", bpy::pure_virtual(&IModInterface::remove)) + ; + + bpy::enum_("GuessQuality") + .value("invalid", MOBase::GUESS_INVALID) + .value("fallback", MOBase::GUESS_FALLBACK) + .value("good", MOBase::GUESS_GOOD) + .value("meta", MOBase::GUESS_META) + .value("preset", MOBase::GUESS_PRESET) + .value("user", MOBase::GUESS_USER) + ; + + bpy::class_, boost::noncopyable>("GuessedString") + .def("update", + static_cast &(GuessedValue::*)(const QString&, EGuessQuality)>(&GuessedValue::update), + bpy::return_value_policy(), updateWithQuality()) + .def("variants", &MOBase::GuessedValue::variants, bpy::return_value_policy()) + ; + + bpy::to_python_converter>(); + QFlags_from_python_obj(); + Functor0_converter(); // converter for the onRefreshed-callback + + bpy::class_("IPluginList") + .def("state", bpy::pure_virtual(&MOBase::IPluginList::state)) + .def("priority", bpy::pure_virtual(&MOBase::IPluginList::priority)) + .def("loadOrder", bpy::pure_virtual(&MOBase::IPluginList::loadOrder)) + .def("isMaster", bpy::pure_virtual(&MOBase::IPluginList::isMaster)) + .def("masters", bpy::pure_virtual(&MOBase::IPluginList::masters)) + .def("origin", bpy::pure_virtual(&MOBase::IPluginList::origin)) + .def("onRefreshed", bpy::pure_virtual(&MOBase::IPluginList::onRefreshed)) + .def("onPluginMoved", bpy::pure_virtual(&MOBase::IPluginList::onPluginMoved)) + .def("pluginNames", bpy::pure_virtual(&MOBase::IPluginList::pluginNames)) + .def("setState", bpy::pure_virtual(&MOBase::IPluginList::setState)) + .def("setLoadOrder", bpy::pure_virtual(&MOBase::IPluginList::setLoadOrder)) + ; + + bpy::to_python_converter>(); + QFlags_from_python_obj(); + Functor2_converter(); // converter for the onModStateChanged-callback + + bpy::class_("IModList") + .def("displayName", bpy::pure_virtual(&MOBase::IModList::displayName)) + .def("allMods", bpy::pure_virtual(&MOBase::IModList::allMods)) + .def("state", bpy::pure_virtual(&MOBase::IModList::state)) + .def("setActive", bpy::pure_virtual(&MOBase::IModList::setActive)) + .def("priority", bpy::pure_virtual(&MOBase::IModList::priority)) + .def("setPriority", bpy::pure_virtual(&MOBase::IModList::setPriority)) + .def("onModStateChanged", bpy::pure_virtual(&MOBase::IModList::onModStateChanged)) + .def("onModMoved", bpy::pure_virtual(&MOBase::IModList::onModMoved)) + ; + + bpy::class_("IPlugin"); + + bpy::class_("IPluginDiagnose") + .def("activeProblems", bpy::pure_virtual(&MOBase::IPluginDiagnose::activeProblems)) + .def("shortDescription", bpy::pure_virtual(&MOBase::IPluginDiagnose::shortDescription)) + .def("fullDescription", bpy::pure_virtual(&MOBase::IPluginDiagnose::fullDescription)) + .def("hasGuidedFix", bpy::pure_virtual(&MOBase::IPluginDiagnose::hasGuidedFix)) + .def("startGuidedFix", bpy::pure_virtual(&MOBase::IPluginDiagnose::startGuidedFix)) + .def("_invalidate", &IPluginDiagnoseWrapper::invalidate) + ; + + bpy::class_("Mapping") + .def_readwrite("source", &Mapping::source) + .def_readwrite("destination", &Mapping::destination) + .def_readwrite("isDirectory", &Mapping::isDirectory) + .def_readwrite("createTarget", &Mapping::createTarget) + ; + + bpy::class_("IPluginFileMapper") + .def("mappings", bpy::pure_virtual(&MOBase::IPluginFileMapper::mappings)) + ; + + bpy::enum_("LoadOrderMechanism") + .value("FileTime", MOBase::IPluginGame::LoadOrderMechanism::FileTime) + .value("PluginsTxt", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt) + ; + + bpy::enum_("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 doesn't actually do the conversion, but might be convenient for accessing the names for enum bits + bpy::enum_("ProfileSetting") + .value("mods", MOBase::IPluginGame::MODS) + .value("configuration", MOBase::IPluginGame::CONFIGURATION) + .value("savegames", MOBase::IPluginGame::SAVEGAMES) + .value("preferDefaults", MOBase::IPluginGame::PREFER_DEFAULTS) + ; + + bpy::to_python_converter>(); + QFlags_from_python_obj(); + + bpy::class_("IPluginGame") + .def("gameName", bpy::pure_virtual(&MOBase::IPluginGame::gameName)) + .def("initializeProfile", bpy::pure_virtual(&MOBase::IPluginGame::initializeProfile)) + .def("savegameExtension", bpy::pure_virtual(&MOBase::IPluginGame::savegameExtension)) + .def("savegameSEExtension", bpy::pure_virtual(&MOBase::IPluginGame::savegameSEExtension)) + .def("isInstalled", bpy::pure_virtual(&MOBase::IPluginGame::isInstalled)) + .def("gameIcon", bpy::pure_virtual(&MOBase::IPluginGame::gameIcon)) + .def("gameDirectory", bpy::pure_virtual(&MOBase::IPluginGame::gameDirectory)) + .def("dataDirectory", bpy::pure_virtual(&MOBase::IPluginGame::dataDirectory)) + .def("setGamePath", bpy::pure_virtual(&MOBase::IPluginGame::setGamePath)) + .def("documentsDirectory", bpy::pure_virtual(&MOBase::IPluginGame::documentsDirectory)) + .def("savesDirectory", bpy::pure_virtual(&MOBase::IPluginGame::savesDirectory)) + .def("executables", bpy::pure_virtual(&MOBase::IPluginGame::executables)) + .def("steamAPPId", bpy::pure_virtual(&MOBase::IPluginGame::steamAPPId)) + .def("primaryPlugins", bpy::pure_virtual(&MOBase::IPluginGame::primaryPlugins)) + .def("gameVariants", bpy::pure_virtual(&MOBase::IPluginGame::gameVariants)) + .def("setGameVariant", bpy::pure_virtual(&MOBase::IPluginGame::setGameVariant)) + .def("binaryName", bpy::pure_virtual(&MOBase::IPluginGame::binaryName)) + .def("gameShortName", bpy::pure_virtual(&MOBase::IPluginGame::gameShortName)) + .def("primarySources", bpy::pure_virtual(&MOBase::IPluginGame::primarySources)) + .def("validShortNames", bpy::pure_virtual(&MOBase::IPluginGame::validShortNames)) + .def("gameNexusName", bpy::pure_virtual(&MOBase::IPluginGame::gameNexusName)) + .def("iniFiles", bpy::pure_virtual(&MOBase::IPluginGame::iniFiles)) + .def("DLCPlugins", bpy::pure_virtual(&MOBase::IPluginGame::DLCPlugins)) + .def("CCPlugins", bpy::pure_virtual(&MOBase::IPluginGame::CCPlugins)) + .def("loadOrderMechanism", bpy::pure_virtual(&MOBase::IPluginGame::loadOrderMechanism)) + .def("sortMechanism", bpy::pure_virtual(&MOBase::IPluginGame::sortMechanism)) + .def("nexusModOrganizerID", bpy::pure_virtual(&MOBase::IPluginGame::nexusModOrganizerID)) + .def("nexusGameID", bpy::pure_virtual(&MOBase::IPluginGame::nexusGameID)) + .def("looksValid", bpy::pure_virtual(&MOBase::IPluginGame::looksValid)) + .def("gameVersion", bpy::pure_virtual(&MOBase::IPluginGame::gameVersion)) + .def("getLauncherName", bpy::pure_virtual(&MOBase::IPluginGame::getLauncherName)) + + //Plugin interface. + .def("init", bpy::pure_virtual(&MOBase::IPluginGame::init)) + .def("name", bpy::pure_virtual(&MOBase::IPluginGame::name)) + .def("author", bpy::pure_virtual(&MOBase::IPluginGame::author)) + .def("description", bpy::pure_virtual(&MOBase::IPluginGame::description)) + .def("version", bpy::pure_virtual(&MOBase::IPluginGame::version)) + .def("isActive", bpy::pure_virtual(&MOBase::IPluginGame::isActive)) + .def("settings", bpy::pure_virtual(&MOBase::IPluginGame::settings)) + + // The syntax has to differ slightly from C++ because these are templated + .def("featureBSAInvalidation", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureDataArchives", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureGamePlugins", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureLocalSavegames", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureSaveGameInfo", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureScriptExtender", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureUnmanagedMods", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + ; + + bpy::enum_("InstallResult") + .value("success", MOBase::IPluginInstaller::RESULT_SUCCESS) + .value("failed", MOBase::IPluginInstaller::RESULT_FAILED) + .value("canceled", MOBase::IPluginInstaller::RESULT_CANCELED) + .value("manualRequested", MOBase::IPluginInstaller::RESULT_MANUALREQUESTED) + .value("notAttempted", MOBase::IPluginInstaller::RESULT_NOTATTEMPTED) + ; + + bpy::class_("IPluginInstallerCustom") + .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginInstallerCustom::setParentWidget)) + ; + + bpy::class_("IPluginModPage") + .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginModPage::setParentWidget)) + ; + + bpy::class_("IPluginPreview") + ; + + bpy::class_, boost::noncopyable>("IPluginTool") + .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginTool::setParentWidget)) + ; + + GuessedValue_converters(); + + //bpy::to_python_converter(); + + QList_from_python_obj(); + QList_from_python_obj(); + bpy::to_python_converter, + QList_to_python_list >(); + QList_from_python_obj(); + bpy::to_python_converter, + QList_to_python_list>(); + QList_from_python_obj(); + bpy::to_python_converter, + QList_to_python_list>(); + + QMap_converters(); + QMap_converters(); + + std_vector_from_python_obj(); + std_vector_from_python_obj(); + bpy::to_python_converter, + std_vector_to_python_list>(); + + stdset_from_python_list(); + + registerGameFeaturesPythonConverters(); +} + + +PythonRunner::PythonRunner(const MOBase::IOrganizer *moInfo) + : m_MOInfo(moInfo) +{ + m_PythonHome = new wchar_t[MAX_PATH + 1]; +} + +static const char *argv0 = "ModOrganizer.exe"; + + +bool PythonRunner::initPython(const QString &pythonPath) +{ + try { + if (!pythonPath.isEmpty() && !QFile::exists(pythonPath + "/python.exe")) { + return false; + } + pythonPath.toWCharArray(m_PythonHome); + if (!pythonPath.isEmpty()) { + Py_SetPythonHome(m_PythonHome); + } + + 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); + Py_OptimizeFlag = 2; + Py_NoSiteFlag = 1; + initPath(); + Py_InitializeEx(0); + + if (!Py_IsInitialized()) { + return false; + } + + PySys_SetArgv(0, (wchar_t**)&argBuffer); + + bpy::object mainModule = bpy::import("__main__"); + bpy::object mainNamespace = mainModule.attr("__dict__"); + mainNamespace["sys"] = bpy::import("sys"); + bpy::import("site"); + mainNamespace["io"] = bpy::import("io"); + bpy::exec("s_ErrIO = io.StringIO()\n" + "sys.stderr = s_ErrIO", + mainNamespace); + return true; + } catch (const bpy::error_already_set&) { + qDebug("failed to init python"); + PyErr_Print(); + if (PyErr_Occurred()) { + PyErr_Print(); + } else { + qCritical("An unexpected C++ exception was thrown in python code"); + } + return false; + } +} + + +bool handled_exec_file(bpy::str filename, bpy::object globals = bpy::object(), bpy::object locals = bpy::object()) +{ + return bpy::handle_exception(std::bind(bpy::exec_file, filename, globals, locals)); +} + + +#define TRY_PLUGIN_TYPE(type, var) do { \ + bpy::extract extr(var); \ + if (extr.check()) { \ + QObject *res = extr; \ + interfaceList.append(res); \ + }\ + } while (false) + + +void PythonRunner::initPath() +{ + static QStringList paths = { + QCoreApplication::applicationDirPath() + "/pythoncore", + m_MOInfo->pluginDataPath() + }; + + Py_SetPath(paths.join(';').toStdWString().c_str()); +} + + +QList PythonRunner::instantiate(const QString &pluginName) +{ + try { + GILock lock; + bpy::object mainModule = bpy::import("__main__"); + bpy::object moduleNamespace = mainModule.attr("__dict__"); + + bpy::object sys = bpy::import("sys"); + moduleNamespace["sys"] = sys; + moduleNamespace["mobase"] = bpy::import("mobase"); + + std::string temp = ToString(pluginName); + if (handled_exec_file(temp.c_str(), moduleNamespace)) { + reportPythonError(); + return QList(); + } + m_PythonObjects[pluginName] = moduleNamespace["createPlugin"](); + + bpy::object pluginObj = m_PythonObjects[pluginName]; + QList interfaceList; + TRY_PLUGIN_TYPE(IPluginGame, pluginObj); + // Must try the wrapper because it's only a plugin extension interface in C++, so doesn't extend QObject + TRY_PLUGIN_TYPE(IPluginDiagnoseWrapper, pluginObj); + // Must try the wrapper because it's only a plugin extension interface in C++, so doesn't extend QObject + TRY_PLUGIN_TYPE(IPluginFileMapperWrapper, pluginObj); + TRY_PLUGIN_TYPE(IPluginInstallerCustom, pluginObj); + TRY_PLUGIN_TYPE(IPluginModPage, pluginObj); + TRY_PLUGIN_TYPE(IPluginPreview, pluginObj); + TRY_PLUGIN_TYPE(IPluginTool, pluginObj); + if (interfaceList.isEmpty()) + TRY_PLUGIN_TYPE(IPluginWrapper, pluginObj); + + return interfaceList; + } catch (const bpy::error_already_set&) { + qWarning("failed to run python script \"%s\"", qPrintable(pluginName)); + reportPythonError(); + } + return QList(); +} + +bool PythonRunner::isPythonInstalled() const +{ + return Py_IsInitialized() != 0; +} + + +bool PythonRunner::isPythonVersionSupported() const +{ + const char *version = Py_GetVersion(); + return strstr(version, "3.7") == version; +} +