From 8fc33485df9073012c8a7e754163409017f3b3c6 Mon Sep 17 00:00:00 2001 From: QianMoth <1306057656@qq.com> Date: Fri, 16 Dec 2022 14:44:44 +0800 Subject: [PATCH 1/5] plugin system initialisation --- CMakeLists.txt | 3 + examples/CMakeLists.txt | 4 + examples/plugin_text/CMakeLists.txt | 11 ++ examples/plugin_text/PluginDefinition.cpp | 14 +++ examples/plugin_text/PluginDefinition.hpp | 36 ++++++ examples/plugin_text/TextData.hpp | 28 +++++ examples/plugin_text/TextSourceDataModel.cpp | 78 ++++++++++++ examples/plugin_text/TextSourceDataModel.hpp | 66 ++++++++++ examples/plugins_load/CMakeLists.txt | 6 + examples/plugins_load/main.cpp | 58 +++++++++ include/QtNodes/PluginInterface | 1 + include/QtNodes/PluginsManager | 1 + include/QtNodes/internal/PluginInterface.hpp | 28 +++++ include/QtNodes/internal/PluginsManager.hpp | 102 ++++++++++++++++ src/PluginsManager.cpp | 121 +++++++++++++++++++ 15 files changed, 557 insertions(+) create mode 100644 examples/plugin_text/CMakeLists.txt create mode 100644 examples/plugin_text/PluginDefinition.cpp create mode 100644 examples/plugin_text/PluginDefinition.hpp create mode 100644 examples/plugin_text/TextData.hpp create mode 100644 examples/plugin_text/TextSourceDataModel.cpp create mode 100644 examples/plugin_text/TextSourceDataModel.hpp create mode 100644 examples/plugins_load/CMakeLists.txt create mode 100644 examples/plugins_load/main.cpp create mode 100644 include/QtNodes/PluginInterface create mode 100644 include/QtNodes/PluginsManager create mode 100644 include/QtNodes/internal/PluginInterface.hpp create mode 100644 include/QtNodes/internal/PluginsManager.hpp create mode 100644 src/PluginsManager.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 76ab0c140..e1d4ab173 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,7 @@ set(CPP_SOURCE_FILES src/StyleCollection.cpp src/UndoCommands.cpp src/locateNode.cpp + src/PluginsManager.cpp ) set(HPP_HEADER_FILES @@ -136,6 +137,8 @@ set(HPP_HEADER_FILES include/QtNodes/internal/Serializable.hpp include/QtNodes/internal/Style.hpp include/QtNodes/internal/StyleCollection.hpp + include/QtNodes/internal/PluginInterface.hpp + include/QtNodes/internal/PluginsManager.hpp src/ConnectionPainter.hpp src/DefaultHorizontalNodeGeometry.hpp src/DefaultVerticalNodeGeometry.hpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 49494da2e..067bf9517 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -16,3 +16,7 @@ add_subdirectory(dynamic_ports) add_subdirectory(lock_nodes_and_connections) +add_subdirectory(plugin_text) + +add_subdirectory(plugins_load) + diff --git a/examples/plugin_text/CMakeLists.txt b/examples/plugin_text/CMakeLists.txt new file mode 100644 index 000000000..3109360a3 --- /dev/null +++ b/examples/plugin_text/CMakeLists.txt @@ -0,0 +1,11 @@ +file(GLOB_RECURSE CPPS ./*.cpp) +file(GLOB_RECURSE HPPS ./*.hpp) + +add_library(plugin_text SHARED ${CPPS} ${HPPS}) + +target_link_libraries(plugin_text QtNodes) + +target_compile_definitions(plugin_text + PUBLIC + NODE_EDITOR_SHARED +) diff --git a/examples/plugin_text/PluginDefinition.cpp b/examples/plugin_text/PluginDefinition.cpp new file mode 100644 index 000000000..ee4b0a951 --- /dev/null +++ b/examples/plugin_text/PluginDefinition.cpp @@ -0,0 +1,14 @@ +#include "PluginDefinition.hpp" + +#include "TextSourceDataModel.hpp" + +Plugin *Plugin::_this_plugin = nullptr; + +Plugin::Plugin() { _this_plugin = this; } + +void Plugin::registerDataModels(std::shared_ptr ®) +{ + assert(reg); + + reg->registerModel(); +} diff --git a/examples/plugin_text/PluginDefinition.hpp b/examples/plugin_text/PluginDefinition.hpp new file mode 100644 index 000000000..20a39ad22 --- /dev/null +++ b/examples/plugin_text/PluginDefinition.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +#if defined(plugin_text_EXPORTS) // 这里需要和您的工程文件名一致${PROJECT_NAME} +#define DLL_EXPORT Q_DECL_EXPORT +#else +#define DLL_EXPORT Q_DECL_IMPORT +#endif + +using QtNodes::NodeDelegateModelRegistry; +using QtNodes::PluginInterface; + +/* 插件名称,一个插件可以存在多个节点 */ +#define PLUGIN_NAME "plugin_text" + +class DLL_EXPORT Plugin + : public QObject + , public QtNodes::PluginInterface +{ + Q_OBJECT + Q_INTERFACES(QtNodes::PluginInterface) + Q_PLUGIN_METADATA(IID PLUGIN_NAME) + + public: + Plugin(); + + QString name() const override { return PLUGIN_NAME; }; + + void registerDataModels(std::shared_ptr ®) override; + + private: + static Plugin *_this_plugin; +}; \ No newline at end of file diff --git a/examples/plugin_text/TextData.hpp b/examples/plugin_text/TextData.hpp new file mode 100644 index 000000000..c417c3249 --- /dev/null +++ b/examples/plugin_text/TextData.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +using QtNodes::NodeData; +using QtNodes::NodeDataType; + +/// The class can potentially incapsulate any user data which +/// need to be transferred within the Node Editor graph +class TextData : public NodeData +{ +public: + + TextData() {} + + TextData(QString const &text) + : _text(text) + {} + + NodeDataType type() const override + { return NodeDataType {"text", "Text"}; } + + QString text() const { return _text; } + +private: + + QString _text; +}; diff --git a/examples/plugin_text/TextSourceDataModel.cpp b/examples/plugin_text/TextSourceDataModel.cpp new file mode 100644 index 000000000..e90d7afeb --- /dev/null +++ b/examples/plugin_text/TextSourceDataModel.cpp @@ -0,0 +1,78 @@ +#include "TextSourceDataModel.hpp" + +#include + +TextSourceDataModel:: +TextSourceDataModel() + : _lineEdit{nullptr} +{ + // +} + + +unsigned int +TextSourceDataModel:: +nPorts(PortType portType) const +{ + unsigned int result = 1; + + switch (portType) + { + case PortType::In: + result = 0; + break; + + case PortType::Out: + result = 1; + + default: + break; + } + + return result; +} + + +void +TextSourceDataModel:: +onTextEdited(QString const & string) +{ + Q_UNUSED(string); + + Q_EMIT dataUpdated(0); +} + + +NodeDataType +TextSourceDataModel:: +dataType(PortType, PortIndex) const +{ + return TextData().type(); +} + + +std::shared_ptr +TextSourceDataModel:: +outData(PortIndex const portIndex) +{ + Q_UNUSED(portIndex); + return std::make_shared(_lineEdit->text()); +} + + +QWidget * +TextSourceDataModel:: +embeddedWidget() +{ + if (!_lineEdit) + { + _lineEdit = new QLineEdit("Default Text"), + + connect(_lineEdit, &QLineEdit::textEdited, + this, &TextSourceDataModel::onTextEdited); + + } + + return _lineEdit; +} + diff --git a/examples/plugin_text/TextSourceDataModel.hpp b/examples/plugin_text/TextSourceDataModel.hpp new file mode 100644 index 000000000..fc481af34 --- /dev/null +++ b/examples/plugin_text/TextSourceDataModel.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include + +#include "TextData.hpp" + +#include + +#include + +using QtNodes::PortType; +using QtNodes::PortIndex; +using QtNodes::NodeData; +using QtNodes::NodeDelegateModel; + +class QLineEdit; + +/// The model dictates the number of inputs and outputs for the Node. +/// In this example it has no logic. +class TextSourceDataModel : public NodeDelegateModel +{ + Q_OBJECT + +public: + TextSourceDataModel(); + +public: + QString + caption() const override + { return QString("Text Source"); } + + bool + captionVisible() const override { return false; } + + static QString + Name() + { return QString("TextSourceDataModel"); } + + QString + name() const override + { return TextSourceDataModel::Name(); } + +public: + unsigned int + nPorts(PortType portType) const override; + + NodeDataType + dataType(PortType portType, PortIndex portIndex) const override; + + std::shared_ptr + outData(PortIndex const portIndex) override; + + void + setInData(std::shared_ptr, PortIndex const) override { } + + QWidget * + embeddedWidget() override; + +private Q_SLOTS: + + void + onTextEdited(QString const & string); + +private: + QLineEdit * _lineEdit; +}; diff --git a/examples/plugins_load/CMakeLists.txt b/examples/plugins_load/CMakeLists.txt new file mode 100644 index 000000000..7a7a7d3ff --- /dev/null +++ b/examples/plugins_load/CMakeLists.txt @@ -0,0 +1,6 @@ +file(GLOB_RECURSE CPPS ./*.cpp) +file(GLOB_RECURSE HPPS ./*.hpp) + +add_executable(plugins_load ${CPPS} ${HPPS}) + +target_link_libraries(plugins_load QtNodes) diff --git a/examples/plugins_load/main.cpp b/examples/plugins_load/main.cpp new file mode 100644 index 000000000..a5b65b4f0 --- /dev/null +++ b/examples/plugins_load/main.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using QtNodes::DataFlowGraphicsScene; +using QtNodes::DataFlowGraphModel; +using QtNodes::GraphicsView; +using QtNodes::NodeDelegateModelRegistry; +using QtNodes::PluginsManager; + +int main(int argc, char *argv[]) +{ + qSetMessagePattern( + "[%{time yyyyMMdd h:mm:ss.zzz}] [%{time process}] " + "[%{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-" + "fatal}F%{endif}]: %{message}\t| (%{function}) [%{file}:%{line}]"); + + QApplication app(argc, argv); + + PluginsManager *pluginsManager = PluginsManager::instance(); + std::shared_ptr registry = pluginsManager->registry(); + pluginsManager->loadPlugins(R"(./nodes)"); + for (auto plugin : pluginsManager->pluginList()) + { + plugin->registerDataModels(registry); + } + + QWidget mainWidget; + + auto menuBar = new QMenuBar(); + QMenu *menu = menuBar->addMenu("File"); + auto saveAction = menu->addAction("Save Scene"); + auto loadAction = menu->addAction("Load Scene"); + + QVBoxLayout *l = new QVBoxLayout(&mainWidget); + + DataFlowGraphModel dataFlowGraphModel(registry); + + l->addWidget(menuBar); + auto scene = new DataFlowGraphicsScene(dataFlowGraphModel, &mainWidget); + l->addWidget(new GraphicsView(scene)); + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + QObject::connect(saveAction, &QAction::triggered, scene, &DataFlowGraphicsScene::save); + QObject::connect(loadAction, &QAction::triggered, scene, &DataFlowGraphicsScene::load); + + mainWidget.setWindowTitle("Data Flow: Plugins Load"); + mainWidget.resize(800, 600); + mainWidget.show(); + + return app.exec(); +} diff --git a/include/QtNodes/PluginInterface b/include/QtNodes/PluginInterface new file mode 100644 index 000000000..d93896bd1 --- /dev/null +++ b/include/QtNodes/PluginInterface @@ -0,0 +1 @@ +#include "internal/PluginInterface.hpp" \ No newline at end of file diff --git a/include/QtNodes/PluginsManager b/include/QtNodes/PluginsManager new file mode 100644 index 000000000..e1a496e37 --- /dev/null +++ b/include/QtNodes/PluginsManager @@ -0,0 +1 @@ +#include "internal/PluginsManager.hpp" \ No newline at end of file diff --git a/include/QtNodes/internal/PluginInterface.hpp b/include/QtNodes/internal/PluginInterface.hpp new file mode 100644 index 000000000..e2d46ea03 --- /dev/null +++ b/include/QtNodes/internal/PluginInterface.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "NodeDelegateModelRegistry.hpp" + +#include + +namespace QtNodes +{ + +class PluginInterface +{ +public: + virtual + ~PluginInterface() = default; + + virtual + QString + name() const = 0; + + virtual + void + registerDataModels(std::shared_ptr ®) = 0; + +}; + +} // namespace QtNodes + +Q_DECLARE_INTERFACE(QtNodes::PluginInterface, "QtNodes.PluginInterface/1.0") diff --git a/include/QtNodes/internal/PluginsManager.hpp b/include/QtNodes/internal/PluginsManager.hpp new file mode 100644 index 000000000..b458c3aa4 --- /dev/null +++ b/include/QtNodes/internal/PluginsManager.hpp @@ -0,0 +1,102 @@ +#pragma once + +#include "Export.hpp" +#include "PluginInterface.hpp" + +#include +#include +#include +#include + +namespace QtNodes +{ + +class NodeDelegateModelRegistry; + +/** + * @brief PluginsManager + * + * @code {.cpp} + * PluginsManager *pluginsManager = PluginsManager::instance(); + * std::shared_ptr registry = pluginsManager->createRegistry(); + * pluginsManager->loadPlugins(R"(./nodes)"); + * for (auto plugin : pluginsManager->pluginList()) + * { + * plugin->registerDataModels(registry); + * } + * @endcode + */ +class NODE_EDITOR_PUBLIC PluginsManager +{ + PluginsManager(); + + ~PluginsManager(); + + PluginsManager(const PluginsManager &rhs); + + const PluginsManager &operator=(const PluginsManager &rhs); + +public: + static PluginsManager *instance(); + + std::shared_ptr registry(); + + /** + * @~Chinese + * @brief 在指定文件夹下加载所有后缀为fileExtension的文件 + * + * @param[in] folderPath 文件夹路径 + * @param[in] fileExtension 文件后缀 + * + * @return int + * @retval 0 程序执行成功 + * @retval -1 程序执行失败 + */ + int loadPlugins(const QString &folderPath, const QString /*fileExtension*/); + + int loadPlugins(const QString &folderPath = "./plugins"); + + /** + * @~Chinese + * @brief 卸载所有插件 + */ + void unloadPlugins(); + + /** + * @~Chinese + * @brief 根据完整的文件路径(包括后缀名)加载插件 + * + * @param[in] filePath 完整的文件路径 "./plugins/demo.dll" + * + * @return int + * @retval 0 程序执行成功 + * @retval <0 程序执行失败 + */ + int loadPluginFromPath(const QString &filePath); + + /** + * @~Chinese + * @brief 根据完整的文件路径(包括后缀名)卸载插件 + * + * @param[in] filePath 完整的文件路径 "./plugins/demo.dll" + * + * @return int + * @retval 0 程序执行成功 + * @retval <0 程序执行失败 + */ + int unloadPluginFromPath(const QString &filePath); + + inline std::vector pluginList() { return _plugins; }; + + inline QHash loaderList() { return _loaders; }; + +private: + static PluginsManager *_instance; + + std::vector _plugins; // 插件 + QHash _loaders; // 插件路径--QPluginLoader实例 + + std::shared_ptr _register; +}; + +} // namespace QtNodes \ No newline at end of file diff --git a/src/PluginsManager.cpp b/src/PluginsManager.cpp new file mode 100644 index 000000000..0c07650e1 --- /dev/null +++ b/src/PluginsManager.cpp @@ -0,0 +1,121 @@ +#include "PluginsManager.hpp" + +#include "NodeDelegateModelRegistry.hpp" + +#include +#include +#include +#include + +namespace QtNodes +{ + +PluginsManager* PluginsManager::_instance = nullptr; + +PluginsManager::PluginsManager() +{ + if (!_register) + _register = std::make_shared(); +} + +PluginsManager::~PluginsManager() +{ + unloadPlugins(); + + if (PluginsManager::instance()) + { + delete PluginsManager::instance(); + PluginsManager::_instance = nullptr; + } +} + +PluginsManager *PluginsManager::instance() // 创建插件管理器 +{ + if (_instance == nullptr) + _instance = new PluginsManager(); + return _instance; +} + +std::shared_ptr PluginsManager::registry() { return _register; }; + +int PluginsManager::loadPlugins(const QString &folderPath) { return loadPlugins(folderPath, ""); } + +int PluginsManager::loadPlugins(const QString &folderPath, const QString /*fileExtension*/) +{ + // TODO: 未来会获得当前文件夹及其子文件夹下的所有dll文件, + // 当前只能识别当前文件夹下的dll + QDir pluginsDir; + if (!pluginsDir.exists(folderPath)) + { + pluginsDir.mkpath(folderPath); + } + pluginsDir.cd(folderPath); + + QFileInfoList pluginsInfo = pluginsDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot); + + // 加载插件 + for (QFileInfo fileInfo : pluginsInfo) + { + qDebug() << "plugin path: " << fileInfo.absoluteFilePath(); + loadPluginFromPath(fileInfo.absoluteFilePath()); + } + return 0; +} + +void PluginsManager::unloadPlugins() +{ + for (QString filePath : _loaders.keys()) + unloadPluginFromPath(filePath); +} + +int PluginsManager::loadPluginFromPath(const QString &filePath) +{ + if (!QLibrary::isLibrary(filePath)) + return -1; + + // 加载插件 + QPluginLoader *loader = new QPluginLoader(filePath); + if (loader->load()) + { + PluginInterface *plugin = qobject_cast(loader->instance()); + if (plugin) + { + _plugins.push_back(plugin); + _loaders.insert(filePath, loader); + qDebug() << "add plugin: " << plugin->name(); + return 0; + } + else + { + delete loader; + loader = nullptr; + } + } + else + { + qDebug() << "loadPlugin:" << filePath << loader->errorString(); + } + return -1; +} + +int PluginsManager::unloadPluginFromPath(const QString &filePath) +{ + QPluginLoader *loader = _loaders.value(filePath); + PluginInterface *plugin = qobject_cast(loader->instance()); + if (plugin) + { + // TODO: 待验证 + _plugins.erase(std::remove(_plugins.begin(), _plugins.end(), plugin), _plugins.end()); + } + + // 卸载插件,并从内部数据结构中移除 + if (loader->unload()) + { + _loaders.remove(filePath); + delete loader; + loader = nullptr; + } + return 0; +} + +} // namespace QtNodes \ No newline at end of file From a772f517d472557c79f317476ad0b687eeecc987 Mon Sep 17 00:00:00 2001 From: QianMoth <1306057656@qq.com> Date: Fri, 16 Dec 2022 15:51:47 +0800 Subject: [PATCH 2/5] Modified to `TextModel`, format --- examples/plugin_text/PluginDefinition.cpp | 14 +- examples/plugin_text/PluginDefinition.hpp | 26 ++-- examples/plugin_text/TextModel.cpp | 97 ++++++++++++ ...{TextSourceDataModel.hpp => TextModel.hpp} | 23 +-- examples/plugin_text/TextSourceDataModel.cpp | 78 ---------- examples/plugins_load/main.cpp | 92 +++++++---- include/QtNodes/internal/PluginsManager.hpp | 87 ++++------- src/PluginsManager.cpp | 144 ++++++++++-------- 8 files changed, 307 insertions(+), 254 deletions(-) create mode 100644 examples/plugin_text/TextModel.cpp rename examples/plugin_text/{TextSourceDataModel.hpp => TextModel.hpp} (71%) delete mode 100644 examples/plugin_text/TextSourceDataModel.cpp diff --git a/examples/plugin_text/PluginDefinition.cpp b/examples/plugin_text/PluginDefinition.cpp index ee4b0a951..df09e631d 100644 --- a/examples/plugin_text/PluginDefinition.cpp +++ b/examples/plugin_text/PluginDefinition.cpp @@ -1,14 +1,18 @@ #include "PluginDefinition.hpp" -#include "TextSourceDataModel.hpp" +#include "TextModel.hpp" Plugin *Plugin::_this_plugin = nullptr; -Plugin::Plugin() { _this_plugin = this; } +Plugin:: +Plugin() +{ _this_plugin = this; } -void Plugin::registerDataModels(std::shared_ptr ®) +void +Plugin:: +registerDataModels(std::shared_ptr ®) { - assert(reg); + assert(reg); - reg->registerModel(); + reg->registerModel(); } diff --git a/examples/plugin_text/PluginDefinition.hpp b/examples/plugin_text/PluginDefinition.hpp index 20a39ad22..4c065774d 100644 --- a/examples/plugin_text/PluginDefinition.hpp +++ b/examples/plugin_text/PluginDefinition.hpp @@ -4,7 +4,9 @@ #include #include -#if defined(plugin_text_EXPORTS) // 这里需要和您的工程文件名一致${PROJECT_NAME} +// This needs to be the same as the name of your project file ${PROJECT_NAME} +// 这里需要和您的工程文件名一致 ${PROJECT_NAME} +#if defined(plugin_text_EXPORTS) #define DLL_EXPORT Q_DECL_EXPORT #else #define DLL_EXPORT Q_DECL_IMPORT @@ -13,24 +15,26 @@ using QtNodes::NodeDelegateModelRegistry; using QtNodes::PluginInterface; -/* 插件名称,一个插件可以存在多个节点 */ #define PLUGIN_NAME "plugin_text" class DLL_EXPORT Plugin : public QObject , public QtNodes::PluginInterface { - Q_OBJECT - Q_INTERFACES(QtNodes::PluginInterface) - Q_PLUGIN_METADATA(IID PLUGIN_NAME) + Q_OBJECT + Q_INTERFACES(QtNodes::PluginInterface) + Q_PLUGIN_METADATA(IID PLUGIN_NAME) - public: - Plugin(); +public: + Plugin(); - QString name() const override { return PLUGIN_NAME; }; + QString + name() const override + { return PLUGIN_NAME; }; - void registerDataModels(std::shared_ptr ®) override; + void + registerDataModels(std::shared_ptr ®) override; - private: - static Plugin *_this_plugin; +private: + static Plugin *_this_plugin; }; \ No newline at end of file diff --git a/examples/plugin_text/TextModel.cpp b/examples/plugin_text/TextModel.cpp new file mode 100644 index 000000000..fe0a2be34 --- /dev/null +++ b/examples/plugin_text/TextModel.cpp @@ -0,0 +1,97 @@ +#include "TextModel.hpp" + +#include + +TextModel:: +TextModel() + : _lineEdit{nullptr} +{ + // +} + + +unsigned int +TextModel:: +nPorts(PortType portType) const +{ + unsigned int result = 1; + + switch (portType) + { + case PortType::In: + result = 1; + break; + + case PortType::Out: + result = 1; + + default: + break; + } + + return result; +} + + +void +TextModel:: +onTextEdited() +{ + Q_EMIT dataUpdated(0); +} + + +NodeDataType +TextModel:: +dataType(PortType, PortIndex) const +{ + return TextData().type(); +} + + +std::shared_ptr +TextModel:: +outData(PortIndex const portIndex) +{ + Q_UNUSED(portIndex); + return std::make_shared(_lineEdit->toPlainText()); +} + + +QWidget * +TextModel:: +embeddedWidget() +{ + if (!_lineEdit) + { + _lineEdit = new QTextEdit(); + + connect(_lineEdit, &QTextEdit::textChanged, + this, &TextModel::onTextEdited); + + } + + return _lineEdit; +} + + +void +TextModel:: +setInData(std::shared_ptr data, PortIndex const) +{ + auto textData = std::dynamic_pointer_cast(data); + + QString inputText; + + if (textData) + { + inputText = textData->text(); + } + else + { + inputText = ""; + } + + _lineEdit->setText(inputText); +} + diff --git a/examples/plugin_text/TextSourceDataModel.hpp b/examples/plugin_text/TextModel.hpp similarity index 71% rename from examples/plugin_text/TextSourceDataModel.hpp rename to examples/plugin_text/TextModel.hpp index fc481af34..5fa1b9ff6 100644 --- a/examples/plugin_text/TextSourceDataModel.hpp +++ b/examples/plugin_text/TextModel.hpp @@ -13,32 +13,32 @@ using QtNodes::PortIndex; using QtNodes::NodeData; using QtNodes::NodeDelegateModel; -class QLineEdit; +class QTextEdit; /// The model dictates the number of inputs and outputs for the Node. /// In this example it has no logic. -class TextSourceDataModel : public NodeDelegateModel +class TextModel : public NodeDelegateModel { Q_OBJECT public: - TextSourceDataModel(); + TextModel(); public: QString caption() const override - { return QString("Text Source"); } + { return QString("Text"); } bool - captionVisible() const override { return false; } + captionVisible() const override { return true; } static QString Name() - { return QString("TextSourceDataModel"); } + { return QString("TextModel"); } QString name() const override - { return TextSourceDataModel::Name(); } + { return TextModel::Name(); } public: unsigned int @@ -51,16 +51,19 @@ class TextSourceDataModel : public NodeDelegateModel outData(PortIndex const portIndex) override; void - setInData(std::shared_ptr, PortIndex const) override { } + setInData(std::shared_ptr, PortIndex const) override; QWidget * embeddedWidget() override; + bool + resizable() const override { return true; } + private Q_SLOTS: void - onTextEdited(QString const & string); + onTextEdited(); private: - QLineEdit * _lineEdit; + QTextEdit * _lineEdit; }; diff --git a/examples/plugin_text/TextSourceDataModel.cpp b/examples/plugin_text/TextSourceDataModel.cpp deleted file mode 100644 index e90d7afeb..000000000 --- a/examples/plugin_text/TextSourceDataModel.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "TextSourceDataModel.hpp" - -#include - -TextSourceDataModel:: -TextSourceDataModel() - : _lineEdit{nullptr} -{ - // -} - - -unsigned int -TextSourceDataModel:: -nPorts(PortType portType) const -{ - unsigned int result = 1; - - switch (portType) - { - case PortType::In: - result = 0; - break; - - case PortType::Out: - result = 1; - - default: - break; - } - - return result; -} - - -void -TextSourceDataModel:: -onTextEdited(QString const & string) -{ - Q_UNUSED(string); - - Q_EMIT dataUpdated(0); -} - - -NodeDataType -TextSourceDataModel:: -dataType(PortType, PortIndex) const -{ - return TextData().type(); -} - - -std::shared_ptr -TextSourceDataModel:: -outData(PortIndex const portIndex) -{ - Q_UNUSED(portIndex); - return std::make_shared(_lineEdit->text()); -} - - -QWidget * -TextSourceDataModel:: -embeddedWidget() -{ - if (!_lineEdit) - { - _lineEdit = new QLineEdit("Default Text"), - - connect(_lineEdit, &QLineEdit::textEdited, - this, &TextSourceDataModel::onTextEdited); - - } - - return _lineEdit; -} - diff --git a/examples/plugins_load/main.cpp b/examples/plugins_load/main.cpp index a5b65b4f0..1db8ac0bb 100644 --- a/examples/plugins_load/main.cpp +++ b/examples/plugins_load/main.cpp @@ -11,48 +11,76 @@ using QtNodes::DataFlowGraphicsScene; using QtNodes::DataFlowGraphModel; using QtNodes::GraphicsView; using QtNodes::NodeDelegateModelRegistry; +using QtNodes::ConnectionStyle; using QtNodes::PluginsManager; -int main(int argc, char *argv[]) +static +void +setStyle() { - qSetMessagePattern( - "[%{time yyyyMMdd h:mm:ss.zzz}] [%{time process}] " - "[%{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-" - "fatal}F%{endif}]: %{message}\t| (%{function}) [%{file}:%{line}]"); - - QApplication app(argc, argv); - - PluginsManager *pluginsManager = PluginsManager::instance(); - std::shared_ptr registry = pluginsManager->registry(); - pluginsManager->loadPlugins(R"(./nodes)"); - for (auto plugin : pluginsManager->pluginList()) - { - plugin->registerDataModels(registry); + ConnectionStyle::setConnectionStyle( + R"( + { + "ConnectionStyle": { + "UseDataDefinedColors": true } + } + )"); +} + + +int +main(int argc, char* argv[]) +{ + qSetMessagePattern( + "[%{time yyyyMMdd h:mm:ss.zzz}] [%{time process}] " + "[%{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-" + "fatal}F%{endif}]: %{message}\t| (%{function}) [%{file}:%{line}]"); + + QApplication app(argc, argv); + + setStyle(); + + PluginsManager* pluginsManager = PluginsManager::instance(); + std::shared_ptr registry = pluginsManager->registry(); + pluginsManager->loadPlugins(R"(./nodes)"); + for (auto plugin : pluginsManager->pluginList()) + { + plugin->registerDataModels(registry); + } + + QWidget mainWidget; + + auto menuBar = new QMenuBar(); + QMenu* menu = menuBar->addMenu("File"); + auto saveAction = menu->addAction("Save Scene"); + auto loadAction = menu->addAction("Load Scene"); + + QVBoxLayout* l = new QVBoxLayout(&mainWidget); - QWidget mainWidget; + DataFlowGraphModel dataFlowGraphModel(registry); - auto menuBar = new QMenuBar(); - QMenu *menu = menuBar->addMenu("File"); - auto saveAction = menu->addAction("Save Scene"); - auto loadAction = menu->addAction("Load Scene"); + l->addWidget(menuBar); + auto scene = new DataFlowGraphicsScene(dataFlowGraphModel, + &mainWidget); - QVBoxLayout *l = new QVBoxLayout(&mainWidget); + auto view = new GraphicsView(scene); + l->addWidget(view); + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); - DataFlowGraphModel dataFlowGraphModel(registry); + QObject::connect(saveAction, &QAction::triggered, + scene, &DataFlowGraphicsScene::save); - l->addWidget(menuBar); - auto scene = new DataFlowGraphicsScene(dataFlowGraphModel, &mainWidget); - l->addWidget(new GraphicsView(scene)); - l->setContentsMargins(0, 0, 0, 0); - l->setSpacing(0); + QObject::connect(loadAction, &QAction::triggered, + scene, &DataFlowGraphicsScene::load); - QObject::connect(saveAction, &QAction::triggered, scene, &DataFlowGraphicsScene::save); - QObject::connect(loadAction, &QAction::triggered, scene, &DataFlowGraphicsScene::load); + QObject::connect(scene, &DataFlowGraphicsScene::sceneLoaded, + view, &GraphicsView::centerScene); - mainWidget.setWindowTitle("Data Flow: Plugins Load"); - mainWidget.resize(800, 600); - mainWidget.show(); + mainWidget.setWindowTitle("Data Flow: Plugins Load"); + mainWidget.resize(800, 600); + mainWidget.show(); - return app.exec(); + return app.exec(); } diff --git a/include/QtNodes/internal/PluginsManager.hpp b/include/QtNodes/internal/PluginsManager.hpp index b458c3aa4..ec6a522ca 100644 --- a/include/QtNodes/internal/PluginsManager.hpp +++ b/include/QtNodes/internal/PluginsManager.hpp @@ -37,64 +37,41 @@ class NODE_EDITOR_PUBLIC PluginsManager const PluginsManager &operator=(const PluginsManager &rhs); public: - static PluginsManager *instance(); - - std::shared_ptr registry(); - - /** - * @~Chinese - * @brief 在指定文件夹下加载所有后缀为fileExtension的文件 - * - * @param[in] folderPath 文件夹路径 - * @param[in] fileExtension 文件后缀 - * - * @return int - * @retval 0 程序执行成功 - * @retval -1 程序执行失败 - */ - int loadPlugins(const QString &folderPath, const QString /*fileExtension*/); - - int loadPlugins(const QString &folderPath = "./plugins"); - - /** - * @~Chinese - * @brief 卸载所有插件 - */ - void unloadPlugins(); - - /** - * @~Chinese - * @brief 根据完整的文件路径(包括后缀名)加载插件 - * - * @param[in] filePath 完整的文件路径 "./plugins/demo.dll" - * - * @return int - * @retval 0 程序执行成功 - * @retval <0 程序执行失败 - */ - int loadPluginFromPath(const QString &filePath); - - /** - * @~Chinese - * @brief 根据完整的文件路径(包括后缀名)卸载插件 - * - * @param[in] filePath 完整的文件路径 "./plugins/demo.dll" - * - * @return int - * @retval 0 程序执行成功 - * @retval <0 程序执行失败 - */ - int unloadPluginFromPath(const QString &filePath); - - inline std::vector pluginList() { return _plugins; }; - - inline QHash loaderList() { return _loaders; }; + + static + PluginsManager* + instance(); + + std::shared_ptr + registry(); + + int + loadPlugins(const QString &folderPath = "./plugins"); + + void + unloadPlugins(); + + int + loadPluginFromPath(const QString &filePath); + + int + unloadPluginFromPath(const QString &filePath); + + inline + std::vector + pluginList() + { return _plugins; }; + + inline + QHash + loaderList() + { return _loaders; }; private: - static PluginsManager *_instance; + static PluginsManager* _instance; - std::vector _plugins; // 插件 - QHash _loaders; // 插件路径--QPluginLoader实例 + std::vector _plugins; + QHash _loaders; ///< plugin path std::shared_ptr _register; }; diff --git a/src/PluginsManager.cpp b/src/PluginsManager.cpp index 0c07650e1..48599d6f8 100644 --- a/src/PluginsManager.cpp +++ b/src/PluginsManager.cpp @@ -12,13 +12,16 @@ namespace QtNodes PluginsManager* PluginsManager::_instance = nullptr; -PluginsManager::PluginsManager() +PluginsManager:: +PluginsManager() { if (!_register) _register = std::make_shared(); } -PluginsManager::~PluginsManager() + +PluginsManager:: +~PluginsManager() { unloadPlugins(); @@ -29,93 +32,108 @@ PluginsManager::~PluginsManager() } } -PluginsManager *PluginsManager::instance() // 创建插件管理器 + +PluginsManager* +PluginsManager:: +instance() { if (_instance == nullptr) _instance = new PluginsManager(); return _instance; } -std::shared_ptr PluginsManager::registry() { return _register; }; -int PluginsManager::loadPlugins(const QString &folderPath) { return loadPlugins(folderPath, ""); } +std::shared_ptr +PluginsManager:: +registry() +{ return _register; }; -int PluginsManager::loadPlugins(const QString &folderPath, const QString /*fileExtension*/) + +int +PluginsManager:: +loadPlugins(const QString &folderPath) { - // TODO: 未来会获得当前文件夹及其子文件夹下的所有dll文件, - // 当前只能识别当前文件夹下的dll - QDir pluginsDir; - if (!pluginsDir.exists(folderPath)) - { - pluginsDir.mkpath(folderPath); - } - pluginsDir.cd(folderPath); + QDir pluginsDir; + if (!pluginsDir.exists(folderPath)) + { + // Created if folderPath does not exist + pluginsDir.mkpath(folderPath); + } + pluginsDir.cd(folderPath); - QFileInfoList pluginsInfo = pluginsDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot); + // TODO: Need recursive folder to get all plugins + + QFileInfoList pluginsInfo = pluginsDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot); - // 加载插件 - for (QFileInfo fileInfo : pluginsInfo) - { - qDebug() << "plugin path: " << fileInfo.absoluteFilePath(); - loadPluginFromPath(fileInfo.absoluteFilePath()); - } - return 0; + for (QFileInfo fileInfo : pluginsInfo) + { + qDebug() << "plugin path: " << fileInfo.absoluteFilePath(); + loadPluginFromPath(fileInfo.absoluteFilePath()); + } + return 0; } -void PluginsManager::unloadPlugins() + +void +PluginsManager:: +unloadPlugins() { - for (QString filePath : _loaders.keys()) - unloadPluginFromPath(filePath); + for (QString filePath : _loaders.keys()) + unloadPluginFromPath(filePath); } -int PluginsManager::loadPluginFromPath(const QString &filePath) + +int +PluginsManager:: +loadPluginFromPath(const QString & filePath) { - if (!QLibrary::isLibrary(filePath)) - return -1; + if (!QLibrary::isLibrary(filePath)) + return -1; - // 加载插件 - QPluginLoader *loader = new QPluginLoader(filePath); - if (loader->load()) + QPluginLoader* loader = new QPluginLoader(filePath); + if (loader->load()) + { + PluginInterface* plugin = qobject_cast(loader->instance()); + if (plugin) { - PluginInterface *plugin = qobject_cast(loader->instance()); - if (plugin) - { - _plugins.push_back(plugin); - _loaders.insert(filePath, loader); - qDebug() << "add plugin: " << plugin->name(); - return 0; - } - else - { - delete loader; - loader = nullptr; - } + _plugins.push_back(plugin); + _loaders.insert(filePath, loader); + qDebug() << "add plugin: " << plugin->name(); + return 0; } else { - qDebug() << "loadPlugin:" << filePath << loader->errorString(); + delete loader; + loader = nullptr; } - return -1; + } + else + { + qDebug() << "loadPlugin:" << filePath << loader->errorString(); + } + return -1; } -int PluginsManager::unloadPluginFromPath(const QString &filePath) + +int +PluginsManager:: +unloadPluginFromPath(const QString & filePath) { - QPluginLoader *loader = _loaders.value(filePath); - PluginInterface *plugin = qobject_cast(loader->instance()); - if (plugin) - { - // TODO: 待验证 - _plugins.erase(std::remove(_plugins.begin(), _plugins.end(), plugin), _plugins.end()); - } + QPluginLoader *loader = _loaders.value(filePath); + PluginInterface *plugin = qobject_cast(loader->instance()); + if (plugin) + { + // TODO: 待验证 + _plugins.erase(std::remove(_plugins.begin(), _plugins.end(), plugin), _plugins.end()); + } - // 卸载插件,并从内部数据结构中移除 - if (loader->unload()) - { - _loaders.remove(filePath); - delete loader; - loader = nullptr; - } - return 0; + if (loader->unload()) + { + _loaders.remove(filePath); + delete loader; + loader = nullptr; + } + return 0; } -} // namespace QtNodes \ No newline at end of file +} // namespace QtNodes From e48b8baa3128cfb8212c5263afd1d67414614668 Mon Sep 17 00:00:00 2001 From: QianMoth <1306057656@qq.com> Date: Fri, 16 Dec 2022 16:11:08 +0800 Subject: [PATCH 3/5] Recursive loading plugins --- examples/plugin_text/TextModel.cpp | 13 ++++++------- examples/plugin_text/TextModel.hpp | 2 +- include/QtNodes/internal/PluginInterface.hpp | 4 ++-- src/PluginsManager.cpp | 15 ++++++++++----- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/examples/plugin_text/TextModel.cpp b/examples/plugin_text/TextModel.cpp index fe0a2be34..e39182e23 100644 --- a/examples/plugin_text/TextModel.cpp +++ b/examples/plugin_text/TextModel.cpp @@ -4,7 +4,6 @@ TextModel:: TextModel() - : _lineEdit{nullptr} { // } @@ -54,7 +53,7 @@ TextModel:: outData(PortIndex const portIndex) { Q_UNUSED(portIndex); - return std::make_shared(_lineEdit->toPlainText()); + return std::make_shared(_textEdit->toPlainText()); } @@ -62,16 +61,16 @@ QWidget * TextModel:: embeddedWidget() { - if (!_lineEdit) + if (!_textEdit) { - _lineEdit = new QTextEdit(); + _textEdit = new QTextEdit(); - connect(_lineEdit, &QTextEdit::textChanged, + connect(_textEdit, &QTextEdit::textChanged, this, &TextModel::onTextEdited); } - return _lineEdit; + return _textEdit; } @@ -92,6 +91,6 @@ setInData(std::shared_ptr data, PortIndex const) inputText = ""; } - _lineEdit->setText(inputText); + _textEdit->setText(inputText); } diff --git a/examples/plugin_text/TextModel.hpp b/examples/plugin_text/TextModel.hpp index 5fa1b9ff6..f71177961 100644 --- a/examples/plugin_text/TextModel.hpp +++ b/examples/plugin_text/TextModel.hpp @@ -65,5 +65,5 @@ private Q_SLOTS: onTextEdited(); private: - QTextEdit * _lineEdit; + QTextEdit * _textEdit = nullptr; }; diff --git a/include/QtNodes/internal/PluginInterface.hpp b/include/QtNodes/internal/PluginInterface.hpp index e2d46ea03..900abfe54 100644 --- a/include/QtNodes/internal/PluginInterface.hpp +++ b/include/QtNodes/internal/PluginInterface.hpp @@ -1,12 +1,12 @@ #pragma once -#include "NodeDelegateModelRegistry.hpp" - #include namespace QtNodes { +class NodeDelegateModelRegistry; + class PluginInterface { public: diff --git a/src/PluginsManager.cpp b/src/PluginsManager.cpp index 48599d6f8..566bbfe11 100644 --- a/src/PluginsManager.cpp +++ b/src/PluginsManager.cpp @@ -61,14 +61,19 @@ loadPlugins(const QString &folderPath) } pluginsDir.cd(folderPath); - // TODO: Need recursive folder to get all plugins - - QFileInfoList pluginsInfo = pluginsDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot); + QFileInfoList pluginsInfo = pluginsDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden); for (QFileInfo fileInfo : pluginsInfo) { - qDebug() << "plugin path: " << fileInfo.absoluteFilePath(); - loadPluginFromPath(fileInfo.absoluteFilePath()); + if(fileInfo.isFile()) + { + qDebug() << "plugin path: " << fileInfo.absoluteFilePath(); + loadPluginFromPath(fileInfo.absoluteFilePath()); + } + else + { + loadPlugins(fileInfo.absoluteFilePath()); + } } return 0; } From 76b58cad9d6f871fe32e047bda57a64f30f10855 Mon Sep 17 00:00:00 2001 From: QianMoth <1306057656@qq.com> Date: Fri, 16 Dec 2022 16:38:48 +0800 Subject: [PATCH 4/5] Update format and comments --- examples/plugin_text/PluginDefinition.cpp | 4 ++-- examples/plugin_text/PluginDefinition.hpp | 4 ++-- include/QtNodes/internal/PluginInterface.hpp | 2 +- include/QtNodes/internal/PluginsManager.hpp | 2 +- src/PluginsManager.cpp | 13 +++++++------ 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/examples/plugin_text/PluginDefinition.cpp b/examples/plugin_text/PluginDefinition.cpp index df09e631d..28930828b 100644 --- a/examples/plugin_text/PluginDefinition.cpp +++ b/examples/plugin_text/PluginDefinition.cpp @@ -2,7 +2,7 @@ #include "TextModel.hpp" -Plugin *Plugin::_this_plugin = nullptr; +Plugin* Plugin::_this_plugin = nullptr; Plugin:: Plugin() @@ -10,7 +10,7 @@ Plugin() void Plugin:: -registerDataModels(std::shared_ptr ®) +registerDataModels(std::shared_ptr & reg) { assert(reg); diff --git a/examples/plugin_text/PluginDefinition.hpp b/examples/plugin_text/PluginDefinition.hpp index 4c065774d..07d76aed7 100644 --- a/examples/plugin_text/PluginDefinition.hpp +++ b/examples/plugin_text/PluginDefinition.hpp @@ -33,8 +33,8 @@ class DLL_EXPORT Plugin { return PLUGIN_NAME; }; void - registerDataModels(std::shared_ptr ®) override; + registerDataModels(std::shared_ptr & reg) override; private: - static Plugin *_this_plugin; + static Plugin* _this_plugin; }; \ No newline at end of file diff --git a/include/QtNodes/internal/PluginInterface.hpp b/include/QtNodes/internal/PluginInterface.hpp index 900abfe54..ebe2ecf78 100644 --- a/include/QtNodes/internal/PluginInterface.hpp +++ b/include/QtNodes/internal/PluginInterface.hpp @@ -19,7 +19,7 @@ class PluginInterface virtual void - registerDataModels(std::shared_ptr ®) = 0; + registerDataModels(std::shared_ptr & reg) = 0; }; diff --git a/include/QtNodes/internal/PluginsManager.hpp b/include/QtNodes/internal/PluginsManager.hpp index ec6a522ca..10292e05d 100644 --- a/include/QtNodes/internal/PluginsManager.hpp +++ b/include/QtNodes/internal/PluginsManager.hpp @@ -18,7 +18,7 @@ class NodeDelegateModelRegistry; * * @code {.cpp} * PluginsManager *pluginsManager = PluginsManager::instance(); - * std::shared_ptr registry = pluginsManager->createRegistry(); + * std::shared_ptr registry = pluginsManager->registry(); * pluginsManager->loadPlugins(R"(./nodes)"); * for (auto plugin : pluginsManager->pluginList()) * { diff --git a/src/PluginsManager.cpp b/src/PluginsManager.cpp index 566bbfe11..c867f2799 100644 --- a/src/PluginsManager.cpp +++ b/src/PluginsManager.cpp @@ -61,11 +61,12 @@ loadPlugins(const QString &folderPath) } pluginsDir.cd(folderPath); - QFileInfoList pluginsInfo = pluginsDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden); + QFileInfoList pluginsInfo = pluginsDir.entryInfoList( + QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden); for (QFileInfo fileInfo : pluginsInfo) { - if(fileInfo.isFile()) + if (fileInfo.isFile()) { qDebug() << "plugin path: " << fileInfo.absoluteFilePath(); loadPluginFromPath(fileInfo.absoluteFilePath()); @@ -114,7 +115,7 @@ loadPluginFromPath(const QString & filePath) } else { - qDebug() << "loadPlugin:" << filePath << loader->errorString(); + qCritical() << "loadPlugin:" << filePath << loader->errorString(); } return -1; } @@ -124,11 +125,11 @@ int PluginsManager:: unloadPluginFromPath(const QString & filePath) { - QPluginLoader *loader = _loaders.value(filePath); - PluginInterface *plugin = qobject_cast(loader->instance()); + QPluginLoader* loader = _loaders.value(filePath); + PluginInterface* plugin = qobject_cast(loader->instance()); if (plugin) { - // TODO: 待验证 + // TODO: Verification required _plugins.erase(std::remove(_plugins.begin(), _plugins.end(), plugin), _plugins.end()); } From 49779db0f63b7be4ddd53ce888ab8d82f2d8dff7 Mon Sep 17 00:00:00 2001 From: QianMoth <1306057656@qq.com> Date: Sun, 18 Dec 2022 19:39:21 +0800 Subject: [PATCH 5/5] add load plugin action , Currently only one path can be added --- examples/plugin_text/PluginDefinition.hpp | 2 +- examples/plugins_load/main.cpp | 38 +++++++---- include/QtNodes/internal/PluginsManager.hpp | 57 ++++++++-------- src/PluginsManager.cpp | 73 ++++++++++++++------- 4 files changed, 107 insertions(+), 63 deletions(-) diff --git a/examples/plugin_text/PluginDefinition.hpp b/examples/plugin_text/PluginDefinition.hpp index 07d76aed7..ea920ad22 100644 --- a/examples/plugin_text/PluginDefinition.hpp +++ b/examples/plugin_text/PluginDefinition.hpp @@ -15,7 +15,7 @@ using QtNodes::NodeDelegateModelRegistry; using QtNodes::PluginInterface; -#define PLUGIN_NAME "plugin_text" +#define PLUGIN_NAME "pluginText" class DLL_EXPORT Plugin : public QObject diff --git a/examples/plugins_load/main.cpp b/examples/plugins_load/main.cpp index 1db8ac0bb..970f32605 100644 --- a/examples/plugins_load/main.cpp +++ b/examples/plugins_load/main.cpp @@ -1,6 +1,9 @@ #include #include #include +#include +#include + #include #include #include @@ -44,17 +47,36 @@ main(int argc, char* argv[]) PluginsManager* pluginsManager = PluginsManager::instance(); std::shared_ptr registry = pluginsManager->registry(); pluginsManager->loadPlugins(R"(./nodes)"); - for (auto plugin : pluginsManager->pluginList()) + for (auto plugin : pluginsManager->plugins()) { - plugin->registerDataModels(registry); + plugin.second->registerDataModels(registry); } QWidget mainWidget; auto menuBar = new QMenuBar(); - QMenu* menu = menuBar->addMenu("File"); - auto saveAction = menu->addAction("Save Scene"); - auto loadAction = menu->addAction("Load Scene"); + QMenu* menu = menuBar->addMenu("Plugins"); + auto loadAction = menu->addAction("Load Plugin"); + auto unloadAction = menu->addAction("Unload Plugin"); + + QObject::connect(loadAction, &QAction::triggered, + [&]() + { + // TODO: load plugins + QString fileName = + QFileDialog::getOpenFileName(nullptr, + "Load Plugin", + QDir::homePath()); + + if (!QFileInfo::exists(fileName)) + return; + + auto plugin = pluginsManager->loadPluginFromPath(fileName); + if (plugin) + { + plugin->registerDataModels(registry); + } + }); QVBoxLayout* l = new QVBoxLayout(&mainWidget); @@ -69,12 +91,6 @@ main(int argc, char* argv[]) l->setContentsMargins(0, 0, 0, 0); l->setSpacing(0); - QObject::connect(saveAction, &QAction::triggered, - scene, &DataFlowGraphicsScene::save); - - QObject::connect(loadAction, &QAction::triggered, - scene, &DataFlowGraphicsScene::load); - QObject::connect(scene, &DataFlowGraphicsScene::sceneLoaded, view, &GraphicsView::centerScene); diff --git a/include/QtNodes/internal/PluginsManager.hpp b/include/QtNodes/internal/PluginsManager.hpp index 10292e05d..49d70be37 100644 --- a/include/QtNodes/internal/PluginsManager.hpp +++ b/include/QtNodes/internal/PluginsManager.hpp @@ -3,39 +3,22 @@ #include "Export.hpp" #include "PluginInterface.hpp" -#include #include #include #include +#include namespace QtNodes { class NodeDelegateModelRegistry; -/** - * @brief PluginsManager - * - * @code {.cpp} - * PluginsManager *pluginsManager = PluginsManager::instance(); - * std::shared_ptr registry = pluginsManager->registry(); - * pluginsManager->loadPlugins(R"(./nodes)"); - * for (auto plugin : pluginsManager->pluginList()) - * { - * plugin->registerDataModels(registry); - * } - * @endcode - */ class NODE_EDITOR_PUBLIC PluginsManager { PluginsManager(); ~PluginsManager(); - PluginsManager(const PluginsManager &rhs); - - const PluginsManager &operator=(const PluginsManager &rhs); - public: static @@ -45,33 +28,49 @@ class NODE_EDITOR_PUBLIC PluginsManager std::shared_ptr registry(); - int + void loadPlugins(const QString &folderPath = "./plugins"); void unloadPlugins(); - int + /** + * @brief Load the plug-in from the full file path + * + * @param filePath "C:/plugin_text.dll" + * @return PluginInterface* + */ + PluginInterface* loadPluginFromPath(const QString &filePath); + /** + * @brief Unload the plugin from the full file path + * + * @param filePath "C:/plugin_text.dll" + * @return int + */ int unloadPluginFromPath(const QString &filePath); - inline - std::vector - pluginList() - { return _plugins; }; + /** + * @brief Uninstall a plugin by its name, not its file name + * + * @param pluginName "pluginText" + * @return int + */ + int + unloadPluginFromName(const QString &pluginName); inline - QHash - loaderList() - { return _loaders; }; + std::unordered_map + plugins() + { return _plugins; }; private: static PluginsManager* _instance; - std::vector _plugins; - QHash _loaders; ///< plugin path + std::unordered_map _plugins; + std::unordered_map _loaders; ///< plugin path std::shared_ptr _register; }; diff --git a/src/PluginsManager.cpp b/src/PluginsManager.cpp index c867f2799..c7f05d388 100644 --- a/src/PluginsManager.cpp +++ b/src/PluginsManager.cpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace QtNodes { @@ -49,7 +50,7 @@ registry() { return _register; }; -int +void PluginsManager:: loadPlugins(const QString &folderPath) { @@ -68,7 +69,6 @@ loadPlugins(const QString &folderPath) { if (fileInfo.isFile()) { - qDebug() << "plugin path: " << fileInfo.absoluteFilePath(); loadPluginFromPath(fileInfo.absoluteFilePath()); } else @@ -76,7 +76,6 @@ loadPlugins(const QString &folderPath) loadPlugins(fileInfo.absoluteFilePath()); } } - return 0; } @@ -84,17 +83,19 @@ void PluginsManager:: unloadPlugins() { - for (QString filePath : _loaders.keys()) - unloadPluginFromPath(filePath); + for (auto loadMap : _loaders) + { + unloadPluginFromPath(loadMap.second->fileName()); + } } -int +PluginInterface* PluginsManager:: loadPluginFromPath(const QString & filePath) { if (!QLibrary::isLibrary(filePath)) - return -1; + return nullptr; QPluginLoader* loader = new QPluginLoader(filePath); if (loader->load()) @@ -102,10 +103,13 @@ loadPluginFromPath(const QString & filePath) PluginInterface* plugin = qobject_cast(loader->instance()); if (plugin) { - _plugins.push_back(plugin); - _loaders.insert(filePath, loader); - qDebug() << "add plugin: " << plugin->name(); - return 0; + const QString name = plugin->name(); + qDebug() << "add plugin: " << name; + + _loaders[name] = loader; + _plugins[filePath] = plugin; + + return plugin; } else { @@ -117,7 +121,7 @@ loadPluginFromPath(const QString & filePath) { qCritical() << "loadPlugin:" << filePath << loader->errorString(); } - return -1; + return nullptr; } @@ -125,21 +129,46 @@ int PluginsManager:: unloadPluginFromPath(const QString & filePath) { - QPluginLoader* loader = _loaders.value(filePath); - PluginInterface* plugin = qobject_cast(loader->instance()); - if (plugin) + auto pluginIter = _plugins.find(filePath); + if(pluginIter != _plugins.end()) { - // TODO: Verification required - _plugins.erase(std::remove(_plugins.begin(), _plugins.end(), plugin), _plugins.end()); + auto loaderIter = _loaders.find(pluginIter->second->name()); + if(loaderIter != _loaders.end()) + { + // delete loader + loaderIter->second->unload(); + delete loaderIter->second; + _loaders.erase(loaderIter->first); + } + + // delete plugin + _plugins.erase(pluginIter->first); + return 0; } + return -1; +} - if (loader->unload()) +int +PluginsManager:: +unloadPluginFromName(const QString &pluginName) +{ + auto loaderIter = _loaders.find(pluginName); + if(loaderIter != _loaders.end()) { - _loaders.remove(filePath); - delete loader; - loader = nullptr; + auto pluginIter = _plugins.find(loaderIter->second->fileName()); + if(pluginIter != _plugins.end()) + { + // delete plugin + _plugins.erase(pluginIter->first); + } + + // delete loaders + loaderIter->second->unload(); + delete loaderIter->second; + _loaders.erase(loaderIter->first); + return 0; } - return 0; + return -1; } } // namespace QtNodes