From 617c76e9f8c333b329cd76fff5f6321575ded050 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sun, 4 May 2014 14:50:01 +0200 Subject: [PATCH] - main window now has a small view displaying log messages - mod list will now be highlighted when grouping is active is active - download tooltip now supports bbcode markup in the description - bbcode translator will now translate some named colors - algorithm for detection of mod order problems is now more sophisticated - exposed more functionality to python plugins - updated to qt 4.8.6 dlls - bugfix: plugin list wasn't - bugfix: state changes in mod list wasn't always reported - bugfix: loot client will now create necessary directory - bugfix: NCC sometimes used wrong source path for extracting - bugfix: removed noisy debug message --- src/proxy/proxypython.cpp | 2 +- src/runner/pythonRunner.pro | 7 ++ src/runner/pythonpluginwrapper.h | 1 + src/runner/pythonrunner.cpp | 152 ++++++++++++++++++++++++++++++- src/runner/uibasewrappers.h | 43 ++++++++- 5 files changed, 200 insertions(+), 5 deletions(-) diff --git a/src/proxy/proxypython.cpp b/src/proxy/proxypython.cpp index 08d7c5b..8fc330d 100644 --- a/src/proxy/proxypython.cpp +++ b/src/proxy/proxypython.cpp @@ -178,7 +178,7 @@ QString ProxyPython::description() const VersionInfo ProxyPython::version() const { - return VersionInfo(1, 2, 1, VersionInfo::RELEASE_FINAL); + return VersionInfo(1, 3, 0, VersionInfo::RELEASE_FINAL); } bool ProxyPython::isActive() const diff --git a/src/runner/pythonRunner.pro b/src/runner/pythonRunner.pro index 83341cd..1aee80a 100644 --- a/src/runner/pythonRunner.pro +++ b/src/runner/pythonRunner.pro @@ -31,14 +31,21 @@ HEADERS += pythonrunner.h \ CONFIG(debug, debug|release) { + SRCDIR = $$OUT_PWD/debug + DSTDIR = $$PWD/../../outputd LIBS += -L$$OUT_PWD/../uibase/debug } else { + SRCDIR = $$OUT_PWD/release + DSTDIR = $$PWD/../../output LIBS += -L$$OUT_PWD/../uibase/release QMAKE_CXXFLAGS += /Zi QMAKE_LFLAGS += /DEBUG } +SRCDIR ~= s,/,$$QMAKE_DIR_SEP,g +DSTDIR ~= s,/,$$QMAKE_DIR_SEP,g + INCLUDEPATH += "$(BOOSTPATH)" "$$(PYTHONPATH)/include" "$$(PYTHONPATH)/Lib/site-packages/PyQt4/include" LIBS += -L"$$(PYTHONPATH)/libs" -L"$(BOOSTPATH)/stage/lib" LIBS += -lpython27 diff --git a/src/runner/pythonpluginwrapper.h b/src/runner/pythonpluginwrapper.h index 19c93a9..f797d90 100644 --- a/src/runner/pythonpluginwrapper.h +++ b/src/runner/pythonpluginwrapper.h @@ -27,6 +27,7 @@ public: virtual QList settings() const; protected: + void reportPythonError() const; private: diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 6dc90f2..e4c6a5b 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -86,6 +86,15 @@ struct QString_to_python_str } }; +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() { @@ -111,7 +120,6 @@ struct QString_from_python_str }; - template struct GuessedValue_converters { @@ -141,7 +149,6 @@ struct GuessedValue_converters } } - 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(); @@ -258,6 +265,7 @@ struct QVariant_from_python_obj } }; + template struct QList_to_python_list { @@ -381,6 +389,7 @@ PyObject *toPyQt(T *objPtr) return bpy::incref(sipObj); } + template struct QClass_converters { @@ -508,6 +517,105 @@ struct QInterface_converters }; +int getArgCount(PyObject *object) { + int result = 0; + PyObject *funcCode = PyObject_GetAttrString(object, "func_code"); + if (funcCode) { + PyObject *argCount = PyObject_GetAttrString(funcCode, "co_argcount"); + if(argCount) { + result = PyInt_AsLong(argCount); + Py_DECREF(argCount); + } + Py_DECREF(funcCode); + } + return result; +} + +struct Functor0_converter +{ + + struct FunctorWrapper + { + FunctorWrapper(boost::python::object callable) : m_Callable(callable) { + } + + void operator()() { + // These GIL calls make it thread safe, may or may not be needed depending on your use case + PyGILState_STATE gstate = PyGILState_Ensure(); + m_Callable(); + PyGILState_Release(gstate); + } + + 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 NULL; + } + 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) { + } + + void operator()(const PAR1 ¶m1, const PAR2 ¶m2) { + // These GIL calls make it thread safe, may or may not be needed depending on your use case + PyGILState_STATE gstate = PyGILState_Ensure(); + m_Callable(param1, param2); + PyGILState_Release(gstate); + } + + 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 NULL; + } + 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) @@ -578,9 +686,10 @@ BOOST_PYTHON_MODULE(mobase) .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)) + .def("installMod", bpy::pure_virtual(&IOrganizer::installMod), bpy::return_value_policy()) .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("startApplication", bpy::pure_virtual(&IOrganizer::startApplication), bpy::return_value_policy()) .def("onAboutToRun", bpy::pure_virtual(&IOrganizer::onAboutToRun)) .def("refreshModList", bpy::pure_virtual(&IOrganizer::refreshModList)) @@ -600,6 +709,24 @@ BOOST_PYTHON_MODULE(mobase) .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)) + ; + + 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("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) @@ -619,6 +746,25 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_("IPluginInstallerCustom") .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginInstallerCustom::setParentWidget)); + 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("origin", bpy::pure_virtual(&MOBase::IPluginList::origin)) + .def("onRefreshed", bpy::pure_virtual(&MOBase::IPluginList::onRefreshed)) + ; + + bpy::to_python_converter>(); + Functor2_converter(); // converter for the onModStateChanged-callback + bpy::class_("IModList") + .def("state", bpy::pure_virtual(&MOBase::IModList::state)) + .def("priority", bpy::pure_virtual(&MOBase::IModList::priority)) + .def("setPriority", bpy::pure_virtual(&MOBase::IModList::setPriority)) + .def("onModStateChanged", bpy::pure_virtual(&MOBase::IModList::onModStateChanged)) + ; + GuessedValue_converters(); bpy::to_python_converter(); diff --git a/src/runner/uibasewrappers.h b/src/runner/uibasewrappers.h index 882272e..dac1a44 100644 --- a/src/runner/uibasewrappers.h +++ b/src/runner/uibasewrappers.h @@ -3,7 +3,9 @@ #ifndef Q_MOC_RUN +#pragma warning (push, 0) #include +#pragma warning (pop) #endif #include @@ -11,6 +13,8 @@ #include #include #include +#include +#include #include "error.h" #include "gilock.h" @@ -133,7 +137,7 @@ struct IOrganizerWrapper: MOBase::IOrganizer, boost::python::wrapperget_override("persistent")(pluginName, key, def); } virtual void setPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync = true) { this->get_override("setPersistent")(pluginName, key, value, sync); } virtual QString pluginDataPath() const { return this->get_override("pluginDataPath")(); } - virtual void installMod(const QString &fileName) { this->get_override("installMod")(fileName); } + virtual MOBase::IModInterface *installMod(const QString &fileName) { return this->get_override("installMod")(fileName); } virtual MOBase::IDownloadManager *downloadManager() { return this->get_override("downloadManager")(); } virtual MOBase::IPluginList *pluginList() { return this->get_override("pluginList")(); } virtual MOBase::IModList *modList() { return this->get_override("modList")(); } @@ -155,6 +159,13 @@ private: boost::python::object m_DownloadCompleteHandler; }; +struct IInstallationManagerWrapper: MOBase::IInstallationManager, boost::python::wrapper +{ + virtual QString extractFile(const QString &fileName) { return this->get_override("extractFile")(fileName); } + virtual QStringList extractFiles(const QStringList &files, bool flatten) { return this->get_override("extractFiles")(files, flatten); } + virtual MOBase::IPluginInstaller::EInstallResult installArchive(MOBase::GuessedValue &modName, const QString &archiveFile) { return this->get_override("installArchive")(modName, archiveFile); } +}; + struct IGameInfoWrapper: MOBase::IGameInfo, boost::python::wrapper { virtual Type type() const { return this->get_override("type")(); } @@ -162,5 +173,35 @@ struct IGameInfoWrapper: MOBase::IGameInfo, boost::python::wrapperget_override("binaryName")(); } }; +struct IModInterfaceWrapper: MOBase::IModInterface, boost::python::wrapper +{ + virtual QString name() const { return this->get_override("name")(); } + virtual QString absolutePath() const { return this->get_override("absolutePath")(); } + virtual void setVersion(const MOBase::VersionInfo &version) { this->get_override("setVersion")(version); } + virtual void setNewestVersion(const MOBase::VersionInfo &version) { this->get_override("setNewestVersion")(version); } + virtual void setIsEndorsed(bool endorsed) { this->get_override("setIsEndorsed")(endorsed); } + virtual void setNexusID(int nexusID) { this->get_override("setNexusID")(nexusID); } + virtual void addNexusCategory(int categoryID) { this->get_override("addNexusCategory")(categoryID); } + virtual bool setName(const QString &name) { return this->get_override("setName")(name); } + virtual bool remove() { return this->get_override("remove")(); } +}; + + +struct IPluginListWrapper: MOBase::IPluginList, boost::python::wrapper { + virtual PluginState state(const QString &name) const { return this->get_override("state")(name); } + virtual int priority(const QString &name) const { return this->get_override("priority")(name); } + virtual int loadOrder(const QString &name) const { return this->get_override("loadOrder")(name); } + virtual bool isMaster(const QString &name) const { return this->get_override("isMaster")(name); } + virtual QString origin(const QString &name) const { return this->get_override("origin")(name); } + virtual bool onRefreshed(const std::function &callback) { return this->get_override("onRefreshed")(callback); } +}; + + +struct IModListWrapper: MOBase::IModList, boost::python::wrapper { + virtual ModStates state(const QString &name) const{ return this->get_override("state")(name); } + virtual int priority(const QString &name) const{ return this->get_override("priority")(name); } + virtual bool setPriority(const QString &name, int newPriority){ return this->get_override("setPriority")(name, newPriority); } + virtual bool onModStateChanged(const std::function &func) { return this->get_override("onModStateChanged")(func); } +}; #endif // UIBASEWRAPPERS_H