diff --git a/src/proxy/INexusBridge.sip b/src/proxy/INexusBridge.sip deleted file mode 100644 index b7f6cd0..0000000 --- a/src/proxy/INexusBridge.sip +++ /dev/null @@ -1,52 +0,0 @@ -%Import QtCore/QtCoremod.sip -%Import QtGui/QtGuimod.sip - - -namespace MOBase -{ - -class ModRepositoryFileInfo -{ - -%TypeHeaderCode -#include -%End - -public: - - ModRepositoryFileInfo(); - ModRepositoryFileInfo(const QString &data); - ~ModRepositoryFileInfo(); - - QString toString() const; - -}; - - -class IModRepositoryBridge : QObject -{ - -%TypeHeaderCode -#include -%End - -public: - - virtual void requestDescription(int modID, QVariant userData) = 0; - virtual void requestFiles(int modID, QVariant userData) = 0; - virtual void requestFileInfo(int modID, int fileID, QVariant userData) = 0; - virtual void requestDownloadURL(int modID, int fileID, QVariant userData) = 0; - virtual void requestToggleEndorsement(int modID, bool endorse, QVariant userData) = 0; - -signals: - - void descriptionAvailable(int modID, QVariant userData, QVariant resultData); - void filesAvailable(int modID, QVariant userData, const QList &resultData); - void fileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData); - void downloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData); - void endorsementToggled(int modID, QVariant userData, QVariant resultData); - void requestFailed(int modID, QVariant userData, const QString &errorMessage); - -}; - -}; diff --git a/src/proxy/embedrunner.rc b/src/proxy/embedrunner.rc new file mode 100644 index 0000000..df824ca --- /dev/null +++ b/src/proxy/embedrunner.rc @@ -0,0 +1,3 @@ +#include "resource.h" + +IDR_LOADER_DLL BINARY MOVEABLE PURE "..\\..\\pythonRunner\\release\\pythonRunner.dll" diff --git a/src/proxy/interfaces.sip b/src/proxy/interfaces.sip deleted file mode 100644 index 593887e..0000000 --- a/src/proxy/interfaces.sip +++ /dev/null @@ -1,3 +0,0 @@ -%Module interfaces - -%Include INexusBridge.sip diff --git a/src/proxy/proxyPython.pro b/src/proxy/proxyPython.pro index 6015a64..a8db273 100644 --- a/src/proxy/proxyPython.pro +++ b/src/proxy/proxyPython.pro @@ -23,38 +23,27 @@ CONFIG(release, debug|release) { DEFINES += PROXYPYTHON_LIBRARY -INCLUDEPATH += "$(BOOSTPATH)" "$$(PYTHONPATH)/include" "$$(PYTHONPATH)/Lib/site-packages/PyQt4/include" -# INCLUDEPATH += "$$(SIPPATH)/siplib" - -SOURCES += proxypython.cpp \ - proxypluginwrappers.cpp \ - error.cpp \ - gilock.cpp - +SOURCES += proxypython.cpp HEADERS += proxypython.h \ - proxypluginwrappers.h \ - uibasewrappers.h \ - error.h \ - gilock.h + resource.h -LIBS += -L"$$(PYTHONPATH)/libs" -L"$(BOOSTPATH)/stage/lib" -lpython27 +OTHER_FILES += \ + proxypython.json \ + embedrunner.rc + +RC_FILE += \ + embedrunner.rc include(../plugin_template.pri) -OTHER_FILES += \ - INexusBridge.sip \ - interfaces.sip \ - setup.py \ - setup.cfg \ - proxypython.json +INCLUDEPATH += "../../pythonRunner" WINPWD = $$PWD WINPWD ~= s,/,$$QMAKE_DIR_SEP,g -SIPPATH="" +//QMAKE_POST_LINK += SET VS90COMNTOOLS=%VS100COMNTOOLS% $$escape_expand(\\n) -QMAKE_POST_LINK += SET VS90COMNTOOLS=%VS100COMNTOOLS% $$escape_expand(\\n) QMAKE_POST_LINK += copy $$(PYTHONPATH)\\lib\\site-packages\\sip.pyd $$quote($$DSTDIR)\\plugins\\data\\ $$escape_expand(\\n) QMAKE_POST_LINK += copy $$(PYTHONPATH)\\lib\\site-packages\\PyQt4\\QtCore.pyd $$quote($$DSTDIR)\\plugins\\data\\ $$escape_expand(\\n) diff --git a/src/proxy/proxypython.cpp b/src/proxy/proxypython.cpp index 5541775..d6df84b 100644 --- a/src/proxy/proxypython.cpp +++ b/src/proxy/proxypython.cpp @@ -4,8 +4,6 @@ #include "proxypython.h" -#include - #pragma warning( pop ) #include @@ -13,634 +11,101 @@ #include #include #include -#include "proxypluginwrappers.h" -#include "uibasewrappers.h" -#include "error.h" - -// sip and qt slots seems to conflict -#include +#include "resource.h" using namespace MOBase; -namespace bpy = boost::python; const char *ProxyPython::s_DownloadPythonURL = "http://www.python.org/download/releases/"; -MOBase::IOrganizer *s_Organizer = NULL; - - -struct ModRepositoryFileInfo_to_python_dict +HMODULE GetOwnModuleHandle() { - static PyObject *convert(const ModRepositoryFileInfo &info) { - PyObject *res = PyDict_New(); - PyDict_SetItemString(res, "uri", bpy::incref(bpy::object(info.uri).ptr())); - PyDict_SetItemString(res, "name", bpy::incref(bpy::object(info.name).ptr())); - PyDict_SetItemString(res, "description", bpy::incref(bpy::object(info.description.toUtf8().constData()).ptr())); - PyDict_SetItemString(res, "categoryID", PyLong_FromLong(info.categoryID)); - PyDict_SetItemString(res, "fileID", PyLong_FromLong(info.fileID)); - PyDict_SetItemString(res, "fileSize", PyLong_FromLong(info.fileSize)); - PyDict_SetItemString(res, "version", bpy::incref(bpy::object(info.version).ptr())); - return bpy::incref(res); - } -}; + HMODULE hMod = NULL; + GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(&GetOwnModuleHandle), &hMod); - -struct QString_to_python_str -{ - static PyObject *convert(const QString &str) { - return bpy::incref(bpy::object(str.toUtf8().constData()).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 PyString_Check(objPtr) ? objPtr : NULL; - } - - static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { - // Extract the character data from the python string - const char* value = PyString_AsString(objPtr); - assert(value != NULL); - - // 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 NULL; - } - } - - - 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>(); - } -}; - - -struct QVariant_to_python_obj -{ - static PyObject *convert(const QVariant &var) { - switch (var.type()) { - case QVariant::Int: return PyLong_FromLong(var.toInt()); - case QVariant::Bool: return PyBool_FromLong(var.toBool()); - case QVariant::String: return bpy::incref(bpy::object(var.toString().toUtf8().constData()).ptr()); - case QVariant::List: { - QVariantList list = var.toList(); - PyObject *result = PyList_New(list.count()); - foreach (QVariant var, list) { - PyList_Append(result, convert(var)); - } - return result; - } break; - default: { - PyErr_SetString(PyExc_TypeError, "type unsupported"); - 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 (!PyString_Check(objPtr) && !PyInt_Check(objPtr) && - !PyBool_Check(objPtr) && !PyList_Check(objPtr)) { - return NULL; - } - 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 QVariant variantFromPyObject(PyObject *objPtr) { - QVariant result; - if (PyList_Check(objPtr)) { - QVariantList resultList; - for (int i = 0; i < PyList_Size(objPtr); ++i) { - resultList.append(variantFromPyObject(PyList_GetItem(objPtr, i))); - } - result = resultList; - } else if (PyString_Check(objPtr)) { - result = PyString_AsString(objPtr); - } else if (PyBool_Check(objPtr)) { - result = (objPtr == Py_True); - } else if (PyInt_Check(objPtr)) { - result = PyInt_AsLong(objPtr); - } else { - PyErr_SetString(PyExc_TypeError, "type unsupported"); - throw bpy::error_already_set(); - } - return result; - } - - static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { - // PyBools will also return true for PyInt_Check but not the other way around, so the order - // here is relevant - if (PyList_Check(objPtr)) { - QVariantList result; - for (int i = 0; i < PyList_Size(objPtr); ++i) { - result.append(variantFromPyObject(PyList_GetItem(objPtr, i))); - } - constructVariant(result, data); - } else if (PyString_Check(objPtr)) { - const char *value = PyString_AsString(objPtr); - constructVariant(value, data); - } else if (PyBool_Check(objPtr)) { - bool value = (objPtr == Py_True); - constructVariant(value, data); - } else if (PyInt_Check(objPtr)) { - long value = PyInt_AsLong(objPtr); - constructVariant(value, 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 { - foreach (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 NULL; - } - - 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 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 NULL; - } - - 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; - } -}; - - -static const sipAPIDef *sipAPI() -{ - static const sipAPIDef *sipApi = NULL; - if (sipApi == NULL) { - sipApi = (const sipAPIDef *)PyCapsule_Import("sip._C_API", 0); - } - - return sipApi; + return hMod; } -template struct MetaData; - -template <> struct MetaData { static const char *className() { return "MOBase::INexusBridge"; } }; -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 "QIcon"; } }; -template <> struct MetaData { static const char *className() { return "QVariant"; } }; - - -template -PyObject *toPyQt(T *objPtr) +QString ExtractResource(WORD resourceID, const QString &szFilename) { - if (objPtr == NULL) { - qDebug("no input object"); - return bpy::incref(Py_None); + bool success = false; + + HMODULE mod = GetOwnModuleHandle(); + + HRSRC hResource = FindResourceW(mod, MAKEINTRESOURCE(resourceID), L"BINARY"); + if (hResource == NULL) { + throw MyException("embedded dll not available: " + windowsErrorString(::GetLastError())); } - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); - - if (type == NULL) { - qDebug("failed to determine type: %s", MetaData::className()); - return bpy::incref(Py_None); + HGLOBAL hFileResource = LoadResource(mod, hResource); + if (hFileResource == NULL) { + throw MyException("failed to load embedded dll resource: " + windowsErrorString(::GetLastError())); } - PyObject *sipObj = sipAPI()->api_convert_from_type(objPtr, type, 0); - if (sipObj == NULL) { - qDebug("failed to convert"); - return bpy::incref(Py_None); - } - return bpy::incref(sipObj); + LPVOID lpFile = LockResource(hFileResource); + DWORD dwSize = SizeofResource(mod, hResource); + + QString outFile = QDir::tempPath() + "/" + szFilename; + + HANDLE hFile = CreateFileW(ToWString(outFile).c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, dwSize, NULL); + LPVOID lpAddress = MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, 0); + + CopyMemory(lpAddress, lpFile, dwSize); + + UnmapViewOfFile(lpAddress); + + CloseHandle(hFileMap); + CloseHandle(hFile); + + return outFile; } -template -struct QClass_converters -{ - struct QClass_to_PyQt - { - static PyObject *convert(const T &object) { - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); - if (type == NULL) { - return bpy::incref(Py_None); - } - - PyObject *sipObj = sipAPI()->api_convert_from_type((void*)(&object), type, 0); - if (sipObj == NULL) { - return bpy::incref(Py_None); - } - - return bpy::incref(sipObj); - } - - static PyObject *convert(T *object) { - if (object == NULL) { - return bpy::incref(Py_None); - } - - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); - if (type == NULL) { - return bpy::incref(Py_None); - } - - PyObject *sipObj = sipAPI()->api_convert_from_type(object, type, 0); - if (sipObj == NULL) { - return bpy::incref(Py_None); - } - - return bpy::incref(sipObj); - } - - static PyObject *convert(const T *object) { - return convert((T*)object); - } - }; - - static void *QClass_from_PyQt(PyObject *objPtr) - { - if (!PyObject_TypeCheck(objPtr, sipAPI()->api_wrapper_type)) { - PyErr_SetString(PyExc_TypeError, "type not wrapped"); - bpy::throw_error_already_set(); - } - - sipAPI()->api_transfer_to(objPtr, 0); - - sipSimpleWrapper *wrapper = reinterpret_cast(objPtr); - return wrapper->data; - } - 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 == NULL) { - return bpy::incref(Py_None); - } - - PyObject *sipObj = sipAPI()->api_convert_from_type((void*)(&object), type, 0); - if (sipObj == NULL) { - return bpy::incref(Py_None); - } - - return bpy::incref(sipObj); - } - - static PyObject *convert(T *object) { - if (object == NULL) { - return bpy::incref(Py_None); - } - - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); - if (type == NULL) { - return bpy::incref(Py_None); - } - - PyObject *sipObj = sipAPI()->api_convert_from_type(object, type, 0); - if (sipObj == NULL) { - 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(); - } - - 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(); - } -}; - - -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(); - QInterface_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_("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::enum_("GameType") - .value("oblivion", MOBase::IGameInfo::TYPE_OBLIVION) - .value("fallout3", MOBase::IGameInfo::TYPE_FALLOUT3) - .value("falloutnv", MOBase::IGameInfo::TYPE_FALLOUTNV) - .value("skyrim", MOBase::IGameInfo::TYPE_SKYRIM); - - bpy::class_("VersionInfo") - .def(bpy::init()) - .def("parse", &MOBase::VersionInfo::parse) - .def("canonicalString", &MOBase::VersionInfo::canonicalString); - - bpy::class_("PluginSetting", bpy::init()); - - bpy::class_("GameInfo") - .def("type", bpy::pure_virtual(&MOBase::IGameInfo::type)) - .def("path", bpy::pure_virtual(&MOBase::IGameInfo::path)) - .def("binaryName", bpy::pure_virtual(&MOBase::IGameInfo::binaryName)); - - bpy::class_("IOrganizer") - .def("gameInfo", bpy::pure_virtual(&MOBase::IOrganizer::gameInfo), bpy::return_value_policy()) - //.def("createNexusBridge", bpy::pure_virtual(&MOBase::IOrganizer::createNexusBridge), bpy::return_value_policy()) - .def("profileName", bpy::pure_virtual(&MOBase::IOrganizer::profileName)) - .def("profilePath", bpy::pure_virtual(&MOBase::IOrganizer::profilePath)) - .def("downloadsPath", bpy::pure_virtual(&MOBase::IOrganizer::downloadsPath)) - .def("appVersion", bpy::pure_virtual(&MOBase::IOrganizer::appVersion)) - .def("getMod", bpy::pure_virtual(&MOBase::IOrganizer::getMod), bpy::return_value_policy()) - .def("createMod", bpy::pure_virtual(&MOBase::IOrganizer::createMod), bpy::return_value_policy()) - .def("removeMod", bpy::pure_virtual(&MOBase::IOrganizer::removeMod)) - .def("modDataChanged", bpy::pure_virtual(&MOBase::IOrganizer::modDataChanged)) - .def("pluginSetting", bpy::pure_virtual(&IOrganizer::pluginSetting)) - .def("pluginDataPath", bpy::pure_virtual(&IOrganizer::pluginDataPath)) - .def("installMod", bpy::pure_virtual(&IOrganizer::installMod)) - .def("downloadManager", bpy::pure_virtual(&IOrganizer::downloadManager), bpy::return_value_policy()); - - bpy::class_("ModRepositoryBridge") - .def("requestDescription", &ModRepositoryBridgeWrapper::requestDescription) - .def("requestFiles", &ModRepositoryBridgeWrapper::requestFiles) - .def("requestFileInfo", &ModRepositoryBridgeWrapper::requestFileInfo) - .def("requestDownloadURL", &ModRepositoryBridgeWrapper::requestDownloadURL) - .def("requestToggleEndorsement", &ModRepositoryBridgeWrapper::requestToggleEndorsement) - .def("onFilesAvailable", &ModRepositoryBridgeWrapper::onFilesAvailable) - .def("onRequestFailed", &ModRepositoryBridgeWrapper::onRequestFailed); - - 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::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::class_("IPluginTool") - .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginTool::setParentWidget)); - bpy::class_("IPluginInstallerCustom") - .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginInstallerCustom::setParentWidget)); - - GuessedValue_converters(); - - bpy::to_python_converter(); - - QList_from_python_obj(); - bpy::to_python_converter, - QList_to_python_list >(); - - stdset_from_python_list(); -} - - -static char* argv0 = "ModOrganizer.exe"; ProxyPython::ProxyPython() - : m_MOInfo(NULL) + : m_MOInfo(NULL), m_Runner(NULL), m_LoadFailure(FAIL_NOTINIT) { - m_PythonHome = new char[MAX_PATH + 1]; } -bool ProxyPython::initPython() +ProxyPython::~ProxyPython() { - try { - QString pythonPath = m_MOInfo->pluginSetting(name(), "python_dir").toString(); - strncpy(m_PythonHome, pythonPath.toUtf8().constData(), MAX_PATH); - if (!pythonPath.isEmpty()) { - Py_SetPythonHome(m_PythonHome); - } - - Py_SetProgramName(argv0); - PyImport_AppendInittab("mobase", &initmobase); - Py_Initialize(); - if (!Py_IsInitialized()) { - return false; - } - PySys_SetArgv(0, &argv0); - - bpy::object main_module = bpy::import("__main__"); - bpy::object main_namespace = main_module.attr("__dict__"); - main_namespace["cStringIO"] = bpy::import("cStringIO"); - main_namespace["sys"] = bpy::import("sys"); - bpy::exec("s_ErrIO = cStringIO.StringIO()\n" - "sys.stderr = s_ErrIO", - main_namespace); - 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; + if (!m_TempRunnerFile.isEmpty()) { + ::FreeLibrary(m_RunnerLib); + QFile(m_TempRunnerFile).remove(); } } +typedef IPythonRunner* (*CreatePythonRunner_func)(const MOBase::IOrganizer *moInfo, const QString &pythonPath); + + bool ProxyPython::init(IOrganizer *moInfo) { m_MOInfo = moInfo; - s_Organizer = moInfo; - return initPython(); + + m_LoadFailure = FAIL_OTHER; + + m_TempRunnerFile = ExtractResource(IDR_LOADER_DLL, "__pythonRunner.dll"); + m_RunnerLib = ::LoadLibraryW(ToWString(m_TempRunnerFile).c_str()); + if (m_RunnerLib != NULL) { + CreatePythonRunner_func CreatePythonRunner = (CreatePythonRunner_func)::GetProcAddress(m_RunnerLib, "CreatePythonRunner"); + if (CreatePythonRunner == NULL) { + throw MyException("embedded dll is invalid: " + windowsErrorString(::GetLastError())); + } + m_Runner = CreatePythonRunner(moInfo, m_MOInfo->pluginSetting(name(), "python_dir").toString()); + m_LoadFailure = FAIL_NONE; + return true; + } else { + DWORD error = ::GetLastError(); + qCritical("Failed to load python runner: %s", qPrintable(windowsErrorString(error))); + if (error == ERROR_MOD_NOT_FOUND) { + m_LoadFailure = FAIL_MISSINGDEPENDENCIES; + } + return true; + } } QString ProxyPython::name() const @@ -660,18 +125,18 @@ QString ProxyPython::description() const VersionInfo ProxyPython::version() const { - return VersionInfo(1, 0, 2, VersionInfo::RELEASE_FINAL); + return VersionInfo(1, 1, 0, VersionInfo::RELEASE_FINAL); } bool ProxyPython::isActive() const { - return isPythonInstalled(); + return m_LoadFailure == FAIL_NONE; } QList ProxyPython::settings() const { QList result; - result.push_back(PluginSetting("python_dir", "Path to your python installation. Leave empty for auto-detection", Py_GetExecPrefix())); + result.push_back(PluginSetting("python_dir", "Path to your python installation. Leave empty for auto-detection", "")); return result; } @@ -688,74 +153,29 @@ QStringList ProxyPython::pluginList(const QString &pluginPath) const } -bool handled_exec_file(bpy::str filename, bpy::object globals = bpy::object(), bpy::object locals = bpy::object()) -{ - return bpy::handle_exception(boost::bind(bpy::exec_file, filename, globals, locals)); -} - - -#define TRY_PLUGIN_TYPE(type, var) do { \ - bpy::extract extr(var); \ - if (extr.check()) { \ - QObject *res = extr; \ - return res; \ - }\ - } while (false) - - QObject *ProxyPython::instantiate(const QString &pluginName) { - try { - GILock lock; - bpy::object main_module = bpy::import("__main__"); - bpy::object main_namespace = main_module.attr("__dict__"); - - bpy::object mobase_module((bpy::handle<>(PyImport_ImportModule("mobase")))); - main_namespace["sys"] = bpy::import("sys"); - main_namespace["mobase"] = mobase_module; - QString appendDataPath = QString("sys.path.insert(0, \"%1\")").arg(m_MOInfo->pluginDataPath()); - - bpy::eval(appendDataPath.toUtf8().constData(), main_namespace); - - std::string temp = ToString(pluginName); - if (handled_exec_file(temp.c_str(), main_namespace)) { - reportPythonError(); - return NULL; - } - m_PythonObjects[pluginName] = main_namespace["createPlugin"](); - - bpy::object pluginObj = m_PythonObjects[pluginName]; - TRY_PLUGIN_TYPE(IPluginInstallerCustom, pluginObj); - TRY_PLUGIN_TYPE(IPluginTool, pluginObj); - } catch (const bpy::error_already_set&) { - qWarning("failed to run python script \"%s\"", qPrintable(pluginName)); - reportPythonError(); + if (m_Runner != NULL) { + return m_Runner->instantiate(pluginName); + } else { + return NULL; } - return NULL; -} - - -bool ProxyPython::isPythonInstalled() const -{ - return Py_IsInitialized() != 0; -} - - -bool ProxyPython::isPythonVersionSupported() const -{ - const char *version = Py_GetVersion(); - return strstr(version, "2.7") == version; } std::vector ProxyPython::activeProblems() const { std::vector result; - - if (!isPythonInstalled()) { + if (m_LoadFailure == FAIL_MISSINGDEPENDENCIES) { result.push_back(PROBLEM_PYTHONMISSING); - } else if (!isPythonVersionSupported()) { - result.push_back(PROBLEM_PYTHONWRONGVERSION); + } else if (m_Runner != NULL) { + if (!m_Runner->isPythonInstalled()) { + // don't know how this could happen but wth + result.push_back(PROBLEM_PYTHONMISSING); + } + if (!m_Runner->isPythonVersionSupported()) { + result.push_back(PROBLEM_PYTHONWRONGVERSION); + } } return result; @@ -781,8 +201,8 @@ QString ProxyPython::fullDescription(unsigned int key) const switch (key) { case PROBLEM_PYTHONMISSING: { return tr("Some plugins require the python interpreter to be installed. " - "These plugins will not even show up in settings->plugins.\n" - "If you want to use those plugins, please install Python 2.7.x from %1.").arg(s_DownloadPythonURL); + "These plugins will not even show up in settings->plugins.
" + "If you want to use those plugins, please install the 32-bit version of Python 2.7.x from %1.").arg(s_DownloadPythonURL); } break; case PROBLEM_PYTHONWRONGVERSION: { return tr("Your installed python version has a different version than 2.7. " diff --git a/src/proxy/proxypython.h b/src/proxy/proxypython.h index a5e3a07..3be83a4 100644 --- a/src/proxy/proxypython.h +++ b/src/proxy/proxypython.h @@ -6,10 +6,7 @@ #include #include #include - -#ifndef Q_MOC_RUN -#include -#endif +#include class ProxyPython : public QObject, MOBase::IPluginProxy, MOBase::IPluginDiagnose @@ -22,6 +19,7 @@ class ProxyPython : public QObject, MOBase::IPluginProxy, MOBase::IPluginDiagnos public: ProxyPython(); + ~ProxyPython(); virtual bool init(MOBase::IOrganizer *moInfo); virtual QString name() const; @@ -50,20 +48,21 @@ public: // IPluginDiagnose private: - bool initPython(); - bool isPythonInstalled() const; - bool isPythonVersionSupported() const; - -private: - - const MOBase::IOrganizer *m_MOInfo; - - std::map m_PythonObjects; - static const unsigned int PROBLEM_PYTHONMISSING = 1; static const unsigned int PROBLEM_PYTHONWRONGVERSION = 2; static const char *s_DownloadPythonURL; - char *m_PythonHome; + + const MOBase::IOrganizer *m_MOInfo; + QString m_TempRunnerFile; + HMODULE m_RunnerLib; + IPythonRunner *m_Runner; + + enum { + FAIL_NONE, + FAIL_NOTINIT, + FAIL_MISSINGDEPENDENCIES, + FAIL_OTHER + } m_LoadFailure; }; diff --git a/src/proxy/resource.h b/src/proxy/resource.h new file mode 100644 index 0000000..5d1c273 --- /dev/null +++ b/src/proxy/resource.h @@ -0,0 +1,6 @@ +#ifndef RESOURCE_H +#define RESOURCE_H + +#define IDR_LOADER_DLL 100 + +#endif // RESOURCE_H diff --git a/src/proxy/setup.cfg b/src/proxy/setup.cfg deleted file mode 100644 index 42529f5..0000000 --- a/src/proxy/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[build_ext] -sip-opts = -e -g -I $$(PYTHONPATH)/Lib/site-packages/PyQt4/sip/PyQt4 diff --git a/src/proxy/setup.py b/src/proxy/setup.py deleted file mode 100644 index 82ef29c..0000000 --- a/src/proxy/setup.py +++ /dev/null @@ -1,27 +0,0 @@ -from distutils.core import setup, Extension -import sipdistutils -import os, sys - -path = os.path.dirname(sys.argv[0]) -if len(path) > 0: - os.chdir(path) -os.environ['PATH'] += r";C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC" -print(os.environ['PATH']) - -os.environ['VS90COMNTOOLS'] = os.environ['VS100COMNTOOLS'] - -setup( - name = 'interfaces', - versione = '1.0', - ext_modules=[ - Extension("interfaces", [ "interfaces.sip" ], - include_dirs=[ r"C:\QtSDK\Desktop\Qt\4.8.4\include", - r"C:\QtSDK\Desktop\Qt\4.8.4\include\QtCore", - path + r"\..\..\uibase" ], - library_dirs=[ r"C:\QtSDK\Desktop\Qt\4.8.4\lib" ], - libraries=[ "QtCore4" ] - ), - ], - - cmdclass = {'build_ext': sipdistutils.build_ext} -) diff --git a/src/proxy/error.cpp b/src/runner/error.cpp similarity index 93% rename from src/proxy/error.cpp rename to src/runner/error.cpp index 59d869d..a1ae993 100644 --- a/src/proxy/error.cpp +++ b/src/runner/error.cpp @@ -1,4 +1,3 @@ -#include "utility.h" #include #include #include diff --git a/src/proxy/error.h b/src/runner/error.h similarity index 100% rename from src/proxy/error.h rename to src/runner/error.h diff --git a/src/proxy/gilock.cpp b/src/runner/gilock.cpp similarity index 100% rename from src/proxy/gilock.cpp rename to src/runner/gilock.cpp diff --git a/src/proxy/gilock.h b/src/runner/gilock.h similarity index 100% rename from src/proxy/gilock.h rename to src/runner/gilock.h diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp new file mode 100644 index 0000000..7835a1b --- /dev/null +++ b/src/runner/proxypluginwrappers.cpp @@ -0,0 +1,213 @@ +#include "proxypluginwrappers.h" +#include +#include +#include "error.h" +#include "gilock.h" + +namespace bpy = boost::python; + +using namespace MOBase; + + +#define PYCATCH catch (const bpy::error_already_set &) { reportPythonError(); throw MyException("unhandled exception"); }\ + catch (...) { throw MyException("An unknown exception was thrown in python code"); } + + +///////////////////////////// +/// IPluginTool Wrapper + + +bool IPluginToolWrapper::init(MOBase::IOrganizer *moInfo) +{ + try { + return this->get_override("init")(bpy::ptr(moInfo)); + } PYCATCH; +} + +QString IPluginToolWrapper::name() const +{ + try { + return this->get_override("name")(); + } PYCATCH; +} + +QString IPluginToolWrapper::author() const +{ + try { + return this->get_override("author")(); + } PYCATCH; +} + +QString IPluginToolWrapper::description() const +{ + try { + return this->get_override("description")(); + } PYCATCH; +} + +MOBase::VersionInfo IPluginToolWrapper::version() const +{ + try { + return this->get_override("version")(); + } PYCATCH; +} + +bool IPluginToolWrapper::isActive() const +{ + try { + return this->get_override("isActive")(); + } PYCATCH; +} + +QList IPluginToolWrapper::settings() const +{ + try { + return this->get_override("settings")(); + } PYCATCH; +} + +QString IPluginToolWrapper::displayName() const +{ + try { + return this->get_override("displayName")(); + } PYCATCH; +} + +QString IPluginToolWrapper::tooltip() const +{ + try { + return this->get_override("tooltip")(); + } PYCATCH; +} + +QIcon IPluginToolWrapper::icon() const +{ + try { + return this->get_override("icon")(); + } PYCATCH; +} + +void IPluginToolWrapper::setParentWidget(QWidget *parent) +{ + try { + this->get_override("setParentWidget")(parent); + } PYCATCH; +} + +void IPluginToolWrapper::display() const +{ + try { + GILock lock; + + this->get_override("display")(); + } PYCATCH; +} + +/// end IPluginTool Wrapper +///////////////////////////////////// +/// IPluginInstallerCustom Wrapper + + +bool IPluginInstallerCustomWrapper::init(MOBase::IOrganizer *moInfo) +{ + try { + return this->get_override("init")(bpy::ptr(moInfo)); + } PYCATCH; +} + +QString IPluginInstallerCustomWrapper::name() const +{ + try { + return this->get_override("name")(); + } PYCATCH; +} + +QString IPluginInstallerCustomWrapper::author() const +{ + try { + return this->get_override("author")(); + } PYCATCH; +} + +QString IPluginInstallerCustomWrapper::description() const +{ + try { + return this->get_override("description")(); + } PYCATCH; +} + +MOBase::VersionInfo IPluginInstallerCustomWrapper::version() const +{ + try { + return this->get_override("version")(); + } PYCATCH; +} + +bool IPluginInstallerCustomWrapper::isActive() const +{ + try { + return this->get_override("isActive")(); + } PYCATCH; +} + +QList IPluginInstallerCustomWrapper::settings() const +{ + try { + return this->get_override("settings")(); + } PYCATCH; +} + +unsigned int IPluginInstallerCustomWrapper::priority() const +{ + try { + return this->get_override("priority")(); + } PYCATCH; +} + +bool IPluginInstallerCustomWrapper::isManualInstaller() const +{ + try { + return this->get_override("isManualInstaller")(); + } PYCATCH; +} + +bool IPluginInstallerCustomWrapper::isArchiveSupported(const DirectoryTree &tree) const +{ + try { + //return this->get_override("isArchiveSupported")(tree); + return false; + } PYCATCH; +} + +bool IPluginInstallerCustomWrapper::isArchiveSupported(const QString &archiveName) const +{ + try { + return this->get_override("isArchiveSupported")(archiveName); + } PYCATCH; +} + +std::set IPluginInstallerCustomWrapper::supportedExtensions() const +{ + try { + return this->get_override("supportedExtensions")(); + } PYCATCH; +} + + +IPluginInstaller::EInstallResult IPluginInstallerCustomWrapper::install(GuessedValue &modName, const QString &archiveName, + const QString &version, int modID) +{ + try { + return this->get_override("install")(modName, archiveName, version, modID); + } PYCATCH; +} + + +void IPluginInstallerCustomWrapper::setParentWidget(QWidget *parent) +{ + try { + this->get_override("setParentWidget")(parent); + } PYCATCH; +} + + diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h new file mode 100644 index 0000000..49e87df --- /dev/null +++ b/src/runner/proxypluginwrappers.h @@ -0,0 +1,80 @@ +#ifndef PROXYPLUGINWRAPPERS_H +#define PROXYPLUGINWRAPPERS_H + + +#include +#include +#include +#include + +#ifndef Q_MOC_RUN +#include +#endif + + +class IPluginWrapper : public boost::python::wrapper +{ +public: + virtual bool init(MOBase::IOrganizer *moInfo); + virtual QString name() const; + virtual QString author() const; + virtual QString description() const; + virtual MOBase::VersionInfo version() const; + virtual bool isActive() const; + virtual QList settings() const; +}; + + +class IPluginToolWrapper: public MOBase::IPluginTool, public boost::python::wrapper +{ + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool) + +public: + virtual bool init(MOBase::IOrganizer *moInfo); + virtual QString name() const; + virtual QString author() const; + virtual QString description() const; + virtual MOBase::VersionInfo version() const; + virtual bool isActive() const; + virtual QList settings() const; + + virtual QString displayName() const; + virtual QString tooltip() const; + virtual QIcon icon() const; + virtual void setParentWidget(QWidget *parent); + +public slots: + virtual void display() const; +}; + + +class IPluginInstallerCustomWrapper: public MOBase::IPluginInstallerCustom, public boost::python::wrapper +{ + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom) + +public: + virtual bool init(MOBase::IOrganizer *moInfo); + virtual QString name() const; + virtual QString author() const; + virtual QString description() const; + virtual MOBase::VersionInfo version() const; + virtual bool isActive() const; + virtual QList settings() const; + + virtual unsigned int priority() const; + virtual bool isManualInstaller() const; + virtual bool isArchiveSupported(const MOBase::DirectoryTree &tree) const; + virtual bool isArchiveSupported(const QString &archiveName) const; + virtual std::set supportedExtensions() const; + virtual EInstallResult install(MOBase::GuessedValue &modName, const QString &archiveName, + const QString &version, int modID); + virtual void setParentWidget(QWidget *parent); + +}; + + + + +#endif // PROXYPLUGINWRAPPERS_H diff --git a/src/runner/pythonRunner.pro b/src/runner/pythonRunner.pro new file mode 100644 index 0000000..2074093 --- /dev/null +++ b/src/runner/pythonRunner.pro @@ -0,0 +1,42 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2013-09-01T15:55:01 +# +#------------------------------------------------- + +TARGET = pythonRunner +TEMPLATE = lib + +CONFIG += dll +CONFIG += warn_on + +DEFINES += PYTHONRUNNER_LIBRARY + +SOURCES += pythonrunner.cpp \ + gilock.cpp \ + error.cpp \ + pythonpluginwrapper.cpp \ + proxypluginwrappers.cpp + +HEADERS += pythonrunner.h \ + gilock.h \ + error.h \ + uibasewrappers.h \ + pythonpluginwrapper.h \ + proxypluginwrappers.h + + + +CONFIG(debug, debug|release) { + LIBS += -L$$OUT_PWD/../uibase/debug +} else { + LIBS += -L$$OUT_PWD/../uibase/release +} + + +INCLUDEPATH += "$(BOOSTPATH)" "$$(PYTHONPATH)/include" "$$(PYTHONPATH)/Lib/site-packages/PyQt4/include" +LIBS += -L"$$(PYTHONPATH)/libs" -L"$(BOOSTPATH)/stage/lib" +LIBS += -lpython27 + +INCLUDEPATH += ../uibase +LIBS += -luibase diff --git a/src/runner/pythonpluginwrapper.cpp b/src/runner/pythonpluginwrapper.cpp new file mode 100644 index 0000000..2208f46 --- /dev/null +++ b/src/runner/pythonpluginwrapper.cpp @@ -0,0 +1,112 @@ +#include "pythonpluginwrapper.h" + + +using boost::python::extract; +using boost::python::handle_exception; + + +PythonPluginWrapper::PythonPluginWrapper(const boost::python::object &initFunction, + const boost::python::object &nameFunction, + const boost::python::object &authorFunction, + const boost::python::object &descriptionFunction, + const boost::python::object &versionFunction, + const boost::python::object &isActiveFunction, + const boost::python::object &settingsFunction) + : m_InitFunction(initFunction) + , m_NameFunction(nameFunction) + , m_AuthorFunction(authorFunction) + , m_DescriptionFunction(descriptionFunction) + , m_VersionFunction(versionFunction) + , m_IsActiveFunction(isActiveFunction) + , m_SettingsFunction(settingsFunction) +{ +} + + +void PythonPluginWrapper::reportPythonError() const +{ + if (PyErr_Occurred()) { + PyErr_Print(); + } else { + qCritical("An unexpected C++ exception was thrown in python code"); + } +} + + +bool PythonPluginWrapper::init(MOBase::IOrganizer *moInfo) +{ + try { + return boost::python::extract(m_InitFunction(boost::python::ptr(moInfo))); + } catch (const boost::python::error_already_set&) { + reportPythonError(); + return false; + } +} + +QString PythonPluginWrapper::name() const +{ + try { + return boost::python::extract(m_NameFunction()); + } catch (const boost::python::error_already_set&) { + reportPythonError(); + return QString(); + } +} + +QString PythonPluginWrapper::author() const +{ + try { + return boost::python::extract(m_AuthorFunction()); + } catch (const boost::python::error_already_set&) { + reportPythonError(); + return QString(); + } +} + +QString PythonPluginWrapper::description() const +{ + try { + return boost::python::extract(m_DescriptionFunction()); + } catch (const boost::python::error_already_set&) { + reportPythonError(); + return QString(); + } +} + +MOBase::VersionInfo PythonPluginWrapper::version() const +{ + try { + return boost::python::extract(m_VersionFunction()); + } catch (const boost::python::error_already_set&) { + reportPythonError(); + return MOBase::VersionInfo(); + } +} + +bool PythonPluginWrapper::isActive() const +{ + try { + return boost::python::extract(m_IsActiveFunction()); + } catch (const boost::python::error_already_set&) { + reportPythonError(); + return false; + } +} + +QList PythonPluginWrapper::settings() const +{ + QList result; + try { + boost::python::object l = m_SettingsFunction(); + if (!l.is_none()) { +// boost::python::list l = extract(temp); + for (int i = 0; i < boost::python::len(l); ++i) { + result.append(extract(l[i])); + } + } + } catch (const boost::python::error_already_set&) { + reportPythonError(); + } + + return result; +} diff --git a/src/runner/pythonpluginwrapper.h b/src/runner/pythonpluginwrapper.h new file mode 100644 index 0000000..19c93a9 --- /dev/null +++ b/src/runner/pythonpluginwrapper.h @@ -0,0 +1,44 @@ +#ifndef PYTHONPLUGINWRAPPER_H +#define PYTHONPLUGINWRAPPER_H + +#include +#ifndef Q_MOC_RUN +#include +#endif + +class PythonPluginWrapper : public virtual MOBase::IPlugin +{ + +public: + PythonPluginWrapper(const boost::python::object &initFunction, + const boost::python::object &nameFunction, + const boost::python::object &authorFunction, + const boost::python::object &descriptionFunction, + const boost::python::object &versionFunction, + const boost::python::object &isActiveFunction, + const boost::python::object &settingsFunction); + + virtual bool init(MOBase::IOrganizer *moInfo); + virtual QString name() const; + virtual QString author() const; + virtual QString description() const; + virtual MOBase::VersionInfo version() const; + virtual bool isActive() const; + virtual QList settings() const; + +protected: + void reportPythonError() const; + +private: + + boost::python::object m_InitFunction; + boost::python::object m_NameFunction; + boost::python::object m_AuthorFunction; + boost::python::object m_DescriptionFunction; + boost::python::object m_VersionFunction; + boost::python::object m_IsActiveFunction; + boost::python::object m_SettingsFunction; + +}; + +#endif // PYTHONPLUGINWRAPPER_H diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp new file mode 100644 index 0000000..982b6e4 --- /dev/null +++ b/src/runner/pythonrunner.cpp @@ -0,0 +1,719 @@ +#include "pythonrunner.h" + + +#include +#include +#include "uibasewrappers.h" +#include "pythonpluginwrapper.h" +#include "proxypluginwrappers.h" +#include +#include + +// sip and qt slots seems to conflict +#include + +#ifndef Q_MOC_RUN +#include +#endif + +MOBase::IOrganizer *s_Organizer = NULL; + + + +class PythonRunner : public IPythonRunner +{ + +public: + PythonRunner(const MOBase::IOrganizer *moInfo); + bool initPython(const QString &pythonDir); + QObject *instantiate(const QString &pluginName); + bool isPythonInstalled() const; + bool isPythonVersionSupported() const; + +private: + + std::map m_PythonObjects; + const MOBase::IOrganizer *m_MOInfo; + char *m_PythonHome; +}; + + + + + +IPythonRunner *CreatePythonRunner(const MOBase::IOrganizer *moInfo, const QString &pythonDir) +{ + PythonRunner *result = new PythonRunner(moInfo); + result->initPython(pythonDir); + return result; +} + + +using namespace MOBase; + +namespace bpy = boost::python; + +struct ModRepositoryFileInfo_to_python_dict +{ + static PyObject *convert(const ModRepositoryFileInfo &info) { + PyObject *res = PyDict_New(); + PyDict_SetItemString(res, "uri", bpy::incref(bpy::object(info.uri).ptr())); + PyDict_SetItemString(res, "name", bpy::incref(bpy::object(info.name).ptr())); + PyDict_SetItemString(res, "description", bpy::incref(bpy::object(info.description.toUtf8().constData()).ptr())); + PyDict_SetItemString(res, "categoryID", PyLong_FromLong(info.categoryID)); + PyDict_SetItemString(res, "fileID", PyLong_FromLong(info.fileID)); + PyDict_SetItemString(res, "fileSize", PyLong_FromLong(info.fileSize)); + PyDict_SetItemString(res, "version", bpy::incref(bpy::object(info.version).ptr())); + return bpy::incref(res); + } +}; + + +struct QString_to_python_str +{ + static PyObject *convert(const QString &str) { + return bpy::incref(bpy::object(str.toUtf8().constData()).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 PyString_Check(objPtr) ? objPtr : NULL; + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { + // Extract the character data from the python string + const char* value = PyString_AsString(objPtr); + assert(value != NULL); + + // 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 NULL; + } + } + + + 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>(); + } +}; + + +struct QVariant_to_python_obj +{ + static PyObject *convert(const QVariant &var) { + switch (var.type()) { + case QVariant::Int: return PyLong_FromLong(var.toInt()); + case QVariant::Bool: return PyBool_FromLong(var.toBool()); + case QVariant::String: return bpy::incref(bpy::object(var.toString().toUtf8().constData()).ptr()); + case QVariant::List: { + QVariantList list = var.toList(); + PyObject *result = PyList_New(list.count()); + foreach (QVariant var, list) { + PyList_Append(result, convert(var)); + } + return result; + } break; + default: { + PyErr_SetString(PyExc_TypeError, "type unsupported"); + 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 (!PyString_Check(objPtr) && !PyInt_Check(objPtr) && + !PyBool_Check(objPtr) && !PyList_Check(objPtr)) { + return NULL; + } + 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 QVariant variantFromPyObject(PyObject *objPtr) { + QVariant result; + if (PyList_Check(objPtr)) { + QVariantList resultList; + for (int i = 0; i < PyList_Size(objPtr); ++i) { + resultList.append(variantFromPyObject(PyList_GetItem(objPtr, i))); + } + result = resultList; + } else if (PyString_Check(objPtr)) { + result = PyString_AsString(objPtr); + } else if (PyBool_Check(objPtr)) { + result = (objPtr == Py_True); + } else if (PyInt_Check(objPtr)) { + result = PyInt_AsLong(objPtr); + } else { + PyErr_SetString(PyExc_TypeError, "type unsupported"); + throw bpy::error_already_set(); + } + return result; + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { + // PyBools will also return true for PyInt_Check but not the other way around, so the order + // here is relevant + if (PyList_Check(objPtr)) { + QVariantList result; + for (int i = 0; i < PyList_Size(objPtr); ++i) { + result.append(variantFromPyObject(PyList_GetItem(objPtr, i))); + } + constructVariant(result, data); + } else if (PyString_Check(objPtr)) { + const char *value = PyString_AsString(objPtr); + constructVariant(value, data); + } else if (PyBool_Check(objPtr)) { + bool value = (objPtr == Py_True); + constructVariant(value, data); + } else if (PyInt_Check(objPtr)) { + long value = PyInt_AsLong(objPtr); + constructVariant(value, 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 { + foreach (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 NULL; + } + + 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 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 NULL; + } + + 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; + } +}; + + +static const sipAPIDef *sipAPI() +{ + static const sipAPIDef *sipApi = NULL; + if (sipApi == NULL) { + sipApi = (const sipAPIDef *)PyCapsule_Import("sip._C_API", 0); + } + + return sipApi; +} + + +template struct MetaData; + +template <> struct MetaData { static const char *className() { return "MOBase::INexusBridge"; } }; +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 "QIcon"; } }; +template <> struct MetaData { static const char *className() { return "QVariant"; } }; + + +template +PyObject *toPyQt(T *objPtr) +{ + if (objPtr == NULL) { + qDebug("no input object"); + return bpy::incref(Py_None); + } + + const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + + if (type == NULL) { + 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 == NULL) { + qDebug("failed to convert"); + return bpy::incref(Py_None); + } + return bpy::incref(sipObj); +} + +template +struct QClass_converters +{ + struct QClass_to_PyQt + { + static PyObject *convert(const T &object) { + const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + if (type == NULL) { + return bpy::incref(Py_None); + } + + PyObject *sipObj = sipAPI()->api_convert_from_type((void*)(&object), type, 0); + if (sipObj == NULL) { + return bpy::incref(Py_None); + } + + return bpy::incref(sipObj); + } + + static PyObject *convert(T *object) { + if (object == NULL) { + return bpy::incref(Py_None); + } + + const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + if (type == NULL) { + return bpy::incref(Py_None); + } + + PyObject *sipObj = sipAPI()->api_convert_from_type(object, type, 0); + if (sipObj == NULL) { + return bpy::incref(Py_None); + } + + return bpy::incref(sipObj); + } + + static PyObject *convert(const T *object) { + return convert((T*)object); + } + }; + + static void *QClass_from_PyQt(PyObject *objPtr) + { + if (!PyObject_TypeCheck(objPtr, sipAPI()->api_wrapper_type)) { + PyErr_SetString(PyExc_TypeError, "type not wrapped"); + bpy::throw_error_already_set(); + } + + sipAPI()->api_transfer_to(objPtr, 0); + + sipSimpleWrapper *wrapper = reinterpret_cast(objPtr); + return wrapper->data; + } + 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 == NULL) { + return bpy::incref(Py_None); + } + + PyObject *sipObj = sipAPI()->api_convert_from_type((void*)(&object), type, 0); + if (sipObj == NULL) { + return bpy::incref(Py_None); + } + + return bpy::incref(sipObj); + } + + static PyObject *convert(T *object) { + if (object == NULL) { + return bpy::incref(Py_None); + } + + const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + if (type == NULL) { + return bpy::incref(Py_None); + } + + PyObject *sipObj = sipAPI()->api_convert_from_type(object, type, 0); + if (sipObj == NULL) { + 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(); + } + + 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(); + } +}; + + +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(); + QInterface_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_("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::enum_("GameType") + .value("oblivion", MOBase::IGameInfo::TYPE_OBLIVION) + .value("fallout3", MOBase::IGameInfo::TYPE_FALLOUT3) + .value("falloutnv", MOBase::IGameInfo::TYPE_FALLOUTNV) + .value("skyrim", MOBase::IGameInfo::TYPE_SKYRIM); + + bpy::class_("VersionInfo") + .def(bpy::init()) + .def("parse", &MOBase::VersionInfo::parse) + .def("canonicalString", &MOBase::VersionInfo::canonicalString); + + bpy::class_("PluginSetting", bpy::init()); + + bpy::class_("GameInfo") + .def("type", bpy::pure_virtual(&MOBase::IGameInfo::type)) + .def("path", bpy::pure_virtual(&MOBase::IGameInfo::path)) + .def("binaryName", bpy::pure_virtual(&MOBase::IGameInfo::binaryName)); + + bpy::class_("IOrganizer") + .def("gameInfo", bpy::pure_virtual(&MOBase::IOrganizer::gameInfo), bpy::return_value_policy()) + //.def("createNexusBridge", bpy::pure_virtual(&MOBase::IOrganizer::createNexusBridge), bpy::return_value_policy()) + .def("profileName", bpy::pure_virtual(&MOBase::IOrganizer::profileName)) + .def("profilePath", bpy::pure_virtual(&MOBase::IOrganizer::profilePath)) + .def("downloadsPath", bpy::pure_virtual(&MOBase::IOrganizer::downloadsPath)) + .def("appVersion", bpy::pure_virtual(&MOBase::IOrganizer::appVersion)) + .def("getMod", bpy::pure_virtual(&MOBase::IOrganizer::getMod), bpy::return_value_policy()) + .def("createMod", bpy::pure_virtual(&MOBase::IOrganizer::createMod), bpy::return_value_policy()) + .def("removeMod", bpy::pure_virtual(&MOBase::IOrganizer::removeMod)) + .def("modDataChanged", bpy::pure_virtual(&MOBase::IOrganizer::modDataChanged)) + .def("pluginSetting", bpy::pure_virtual(&IOrganizer::pluginSetting)) + .def("pluginDataPath", bpy::pure_virtual(&IOrganizer::pluginDataPath)) + .def("installMod", bpy::pure_virtual(&IOrganizer::installMod)) + .def("downloadManager", bpy::pure_virtual(&IOrganizer::downloadManager), bpy::return_value_policy()); + + bpy::class_("ModRepositoryBridge") + .def("requestDescription", &ModRepositoryBridgeWrapper::requestDescription) + .def("requestFiles", &ModRepositoryBridgeWrapper::requestFiles) + .def("requestFileInfo", &ModRepositoryBridgeWrapper::requestFileInfo) + .def("requestDownloadURL", &ModRepositoryBridgeWrapper::requestDownloadURL) + .def("requestToggleEndorsement", &ModRepositoryBridgeWrapper::requestToggleEndorsement) + .def("onFilesAvailable", &ModRepositoryBridgeWrapper::onFilesAvailable) + .def("onRequestFailed", &ModRepositoryBridgeWrapper::onRequestFailed); + + 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::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::class_("IPluginTool") + .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginTool::setParentWidget)); + bpy::class_("IPluginInstallerCustom") + .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginInstallerCustom::setParentWidget)); + + GuessedValue_converters(); + + bpy::to_python_converter(); + + QList_from_python_obj(); + bpy::to_python_converter, + QList_to_python_list >(); + + stdset_from_python_list(); +} + + +PythonRunner::PythonRunner(const MOBase::IOrganizer *moInfo) + : m_MOInfo(moInfo) +{ + m_PythonHome = new char[MAX_PATH + 1]; +} + +static char* argv0 = "ModOrganizer.exe"; + +bool PythonRunner::initPython(const QString &pythonPath) +{ + try { + //QString pythonPath = m_MOInfo->pluginSetting(name(), "python_dir").toString(); + strncpy(m_PythonHome, pythonPath.toUtf8().constData(), MAX_PATH); + if (!pythonPath.isEmpty()) { + Py_SetPythonHome(m_PythonHome); + } + + Py_SetProgramName(argv0); + PyImport_AppendInittab("mobase", &initmobase); + Py_Initialize(); + if (!Py_IsInitialized()) { + return false; + } + PySys_SetArgv(0, &argv0); + + bpy::object main_module = bpy::import("__main__"); + bpy::object main_namespace = main_module.attr("__dict__"); + main_namespace["cStringIO"] = bpy::import("cStringIO"); + main_namespace["sys"] = bpy::import("sys"); + bpy::exec("s_ErrIO = cStringIO.StringIO()\n" + "sys.stderr = s_ErrIO", + main_namespace); + 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(boost::bind(bpy::exec_file, filename, globals, locals)); +} + + +#define TRY_PLUGIN_TYPE(type, var) do { \ + bpy::extract extr(var); \ + if (extr.check()) { \ + QObject *res = extr; \ + return res; \ + }\ + } while (false) + + +QObject *PythonRunner::instantiate(const QString &pluginName) +{ + try { + GILock lock; + bpy::object main_module = bpy::import("__main__"); + bpy::object main_namespace = main_module.attr("__dict__"); + + bpy::object mobase_module((bpy::handle<>(PyImport_ImportModule("mobase")))); + main_namespace["sys"] = bpy::import("sys"); + main_namespace["mobase"] = mobase_module; + QString appendDataPath = QString("sys.path.insert(0, \"%1\")").arg(m_MOInfo->pluginDataPath()); + + bpy::eval(appendDataPath.toUtf8().constData(), main_namespace); + + std::string temp = ToString(pluginName); + if (handled_exec_file(temp.c_str(), main_namespace)) { + reportPythonError(); + return NULL; + } + m_PythonObjects[pluginName] = main_namespace["createPlugin"](); + + bpy::object pluginObj = m_PythonObjects[pluginName]; + TRY_PLUGIN_TYPE(IPluginInstallerCustom, pluginObj); + TRY_PLUGIN_TYPE(IPluginTool, pluginObj); + } catch (const bpy::error_already_set&) { + qWarning("failed to run python script \"%s\"", qPrintable(pluginName)); + reportPythonError(); + } + return NULL; +} + +bool PythonRunner::isPythonInstalled() const +{ + return Py_IsInitialized() != 0; +} + + +bool PythonRunner::isPythonVersionSupported() const +{ + const char *version = Py_GetVersion(); + return strstr(version, "2.7") == version; +} + diff --git a/src/runner/pythonrunner.h b/src/runner/pythonrunner.h new file mode 100644 index 0000000..b6435fc --- /dev/null +++ b/src/runner/pythonrunner.h @@ -0,0 +1,21 @@ +#ifndef PYTHONRUNNER_H +#define PYTHONRUNNER_H + +#include +#include +#include +#include +#include + + +class IPythonRunner { +public: + virtual QObject *instantiate(const QString &pluginName) = 0; + virtual bool isPythonInstalled() const = 0; + virtual bool isPythonVersionSupported() const = 0; +}; + +extern "C" QDLLEXPORT IPythonRunner *CreatePythonRunner(const MOBase::IOrganizer *moInfo, const QString &pythonDir); + + +#endif // PYTHONRUNNER_H diff --git a/src/runner/pythonrunner_global.h b/src/runner/pythonrunner_global.h new file mode 100644 index 0000000..4f6b292 --- /dev/null +++ b/src/runner/pythonrunner_global.h @@ -0,0 +1,12 @@ +#ifndef PYTHONRUNNER_GLOBAL_H +#define PYTHONRUNNER_GLOBAL_H + +#include + +#if defined(PYTHONRUNNER_LIBRARY) +# define PYTHONRUNNERSHARED_EXPORT Q_DECL_EXPORT +#else +# define PYTHONRUNNERSHARED_EXPORT Q_DECL_IMPORT +#endif + +#endif // PYTHONRUNNER_GLOBAL_H diff --git a/src/runner/pythontoolwrapper.cpp b/src/runner/pythontoolwrapper.cpp new file mode 100644 index 0000000..8690dff --- /dev/null +++ b/src/runner/pythontoolwrapper.cpp @@ -0,0 +1,80 @@ +#include "pythontoolwrapper.h" +#include +#include + +using boost::python::extract; +using boost::python::handle_exception; + +PythonToolWrapper::PythonToolWrapper(const boost::python::object &initFunction, + const boost::python::object &nameFunction, + const boost::python::object &authorFunction, + const boost::python::object &descriptionFunction, + const boost::python::object &versionFunction, + const boost::python::object &isActiveFunction, + const boost::python::object &settingsFunction, + const boost::python::object &displayNameFunction, + const boost::python::object &tooltipFunction, + const boost::python::object &iconFunction, + const boost::python::object &displayFunction, + const boost::python::object &setParentWidget) + : PythonPluginWrapper(initFunction, nameFunction, authorFunction, descriptionFunction, + versionFunction, isActiveFunction, settingsFunction) + , m_DisplayNameFunction(displayNameFunction) + , m_ToolTipFunction(tooltipFunction) + , m_IconFunction(iconFunction) + , m_DisplayFunction(displayFunction) + , m_SetParentWidget(setParentWidget) +{ +} + + + +QString PythonToolWrapper::displayName() const +{ + try { + return boost::python::extract(m_DisplayNameFunction()); + } catch (const boost::python::error_already_set&) { + reportPythonError(); + return QString(); + } +} + +QString PythonToolWrapper::tooltip() const +{ + try { + return boost::python::extract(m_ToolTipFunction()); + } catch (const boost::python::error_already_set&) { + reportPythonError(); + return QString(); + } +} + +QIcon PythonToolWrapper::icon() const +{ + try { + std::vector rows; + boost::python::object l = m_IconFunction(); + if (!l.is_none()) { + for (int i = 0; i < boost::python::len(l); ++i) { + rows.push_back(extract(l[i])); + } + return QIcon(QPixmap(&rows[0])); + } else { + return QIcon(); + } + } catch (const boost::python::error_already_set&) { + reportPythonError(); + return QIcon(); + } +} + +void PythonToolWrapper::display() const +{ + try { + m_SetParentWidget(parentWidget()); + + m_DisplayFunction(); + } catch (const boost::python::error_already_set&) { + reportPythonError(); + } +} diff --git a/src/runner/pythontoolwrapper.h b/src/runner/pythontoolwrapper.h new file mode 100644 index 0000000..0e07c13 --- /dev/null +++ b/src/runner/pythontoolwrapper.h @@ -0,0 +1,55 @@ +#ifndef PYTHONTOOLWRAPPER_H +#define PYTHONTOOLWRAPPER_H + +#include +#ifndef Q_MOC_RUN +#include +#endif + +#include "pythonpluginwrapper.h" + + +class PythonToolWrapper : public MOBase::IPluginTool, public PythonPluginWrapper +{ + + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool) + +public: + PythonToolWrapper(const boost::python::object &initFunction, + const boost::python::object &nameFunction, + const boost::python::object &authorFunction, + const boost::python::object &descriptionFunction, + const boost::python::object &versionFunction, + const boost::python::object &isActiveFunction, + const boost::python::object &settingsFunction, + const boost::python::object &displayNameFunction, + const boost::python::object &tooltipFunction, + const boost::python::object &iconFunction, + const boost::python::object &displayFunction, + const boost::python::object &setParentWidget); + + using PythonPluginWrapper::init; + using PythonPluginWrapper::name; + + virtual QString displayName() const; + virtual QString tooltip() const; + virtual QIcon icon() const; + +public slots: + + virtual void display() const; + +private: + + boost::python::object m_DisplayNameFunction; + boost::python::object m_ToolTipFunction; + boost::python::object m_IconFunction; + boost::python::object m_DisplayFunction; + + boost::python::object m_SetParentWidget; +}; + + + +#endif // PYTHONTOOLWRAPPER_H diff --git a/src/proxy/uibasewrappers.h b/src/runner/uibasewrappers.h similarity index 96% rename from src/proxy/uibasewrappers.h rename to src/runner/uibasewrappers.h index c4f4c4f..828a3a5 100644 --- a/src/proxy/uibasewrappers.h +++ b/src/runner/uibasewrappers.h @@ -31,7 +31,7 @@ class ModRepositoryBridgeWrapper : public QObject public: ModRepositoryBridgeWrapper() - : m_Wrapped(s_Organizer->createNexusBridge()) + : m_Wrapped(s_Organizer->createNexusBridge()) { }