当前位置: 首页 > news >正文

CTK的插件框架和QTUI集成方法

将CTK的插件框架和QTUI集成到一起,有两种方法。

方法1: QTUI集成,采取传递context上下文的方法

//在main函数创建插件上下文,传到MainWindow中#include <ctkConfig.h>
#include <ctkPluginFrameworkFactory.h>
#include <ctkPluginFramework.h>
#include <ctkPluginException.h>
#include <ctkPluginContext.h>
#include <ctkPluginFrameworkLauncher.h>#include <service/event/ctkEventAdmin.h>#include <QApplication>
#include <QFormLayout>
#include <QVBoxLayout>#include <ctkCheckablePushButton.h>
#include <ctkCollapsibleButton.h>
#include <ctkColorPickerButton.h>
#include <ctkRangeWidget.h>#include <QApplication>
#include <QDebug>
#include <QPushButton>
#include <QMainWindow>
#include <QMessageBox>
#include <QWidget>
#include <QVBoxLayout>
#include <QElapsedTimer>
#include <QLabel>class MainWindow : public QMainWindow 
{Q_OBJECT
public:MainWindow(ctkPluginFramework* framwork, QWidget* parent = nullptr) : QMainWindow(parent){// 创建中央部件QWidget* centralWidget = new QWidget(this);setCentralWidget(centralWidget);// 创建布局和控件QVBoxLayout* layout = new QVBoxLayout(centralWidget);m_label = new QLabel("Hello QMainWindow!", centralWidget);QPushButton* button = new QPushButton("Click me!", centralWidget);// 设置布局layout->addWidget(m_label);layout->addWidget(button);// 连接信号槽connect(button, &QPushButton::clicked, this, &MainWindow::onButtonClicked);// 窗口设置setWindowTitle("MainWindow Example");resize(400, 300);}private slots:void onButtonClicked() {m_label->setText("Button Clicked!");}private:QLabel* m_label;ctkPluginFrameworkFactory frameWorkFactory;
};int main(int argc, char* argv[])
{QApplication a(argc, argv);a.setApplicationName("CTK_PluginFramework");//给框架创建名称,Linux下没有会报错ctkPluginFrameworkFactory frameWorkFactory;QSharedPointer<ctkPluginFramework> framework = frameWorkFactory.getFramework();framework->init();framework->start();ctkPluginContext* pluginContext = framework->getPluginContext();// 定义插件路径列表QStringList pluginDirs = {"C:/CTK/x64-Debug/lib/ctk-0.1/plugins/liborg_commontk_eventadmin.dll","test_plugins/libpluginSL1_test.dll","test_plugins/libpluginSL3_test.dll","test_plugins/libpluginP1_test.dll",//"test_plugins/libpluginUI_test.dll",};try{// 遍历所有插件路径for (const QString& pluginPath : pluginDirs){// 安装插件QSharedPointer<ctkPlugin> plugin;plugin = pluginContext->installPlugin(QUrl::fromLocalFile(pluginPath));qDebug() << QString("Plugin[%1_%2] installed...").arg(plugin->getSymbolicName()).arg(plugin->getVersion().toString());// 启动插件plugin->start(ctkPlugin::START_TRANSIENT);qDebug() << "Plugin start...";// 提取并打印元数据QHash<QString, QString> headers = plugin->getHeaders();ctkVersion version = ctkVersion::parseVersion(headers.value(ctkPluginConstants::PLUGIN_VERSION));QString name = headers.value(ctkPluginConstants::PLUGIN_SYMBOLICNAME);QString number = headers.value("Plugin-Number");qDebug() << "version: " << version;qDebug() << "name: " << name;qDebug() << "number: " << number;qDebug() << endl;qDebug() << endl;}}catch (const ctkPluginException& e){qDebug() << QString("Failed to install or run plugin: %1").arg(e.what());return -2;}MainWindow w(0);//framework.data()w.show();a.exec();auto plugins = pluginContext->getPlugins();for (auto plugin : plugins){plugin->stop();}framework->stop();framework->waitForStop(0);return 0;
}#include "main.moc"

对应的cmake文件:

# CMakeList.txt: CTKTest 的 CMake 项目,在此处包括源代码并定义
# 项目特定的逻辑。
#
set(TARGET_NAME CTKTestUI)# 设置 UTF-8 编码(现代写法)
if(MSVC)# MSVC 编译器使用 /utf-8 选项add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
else()# GCC/Clang 编译器使用 -finput-charset=UTF-8 -fexec-charset=UTF-8add_compile_options("$<$<OR:$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:Clang>>:-finput-charset=UTF-8;-fexec-charset=UTF-8>")
endif()set(CMAKE_AUTOMOC ON)include_directories(${Qt5Core_INCLUDE_DIRS})
include_directories(${Qt5Gui_INCLUDE_DIRS})
include_directories(${Qt5Widgets_INCLUDE_DIRS})
include_directories(${Qt5Multimedia_INCLUDE_DIRS})
include_directories(${Qt5MultimediaWidgets_INCLUDE_DIRS})include_directories(${CTK_INCLUDE_DIRS})# 将源代码添加到此项目的可执行文件。
add_executable (${TARGET_NAME} main.cpp )target_link_libraries(${TARGET_NAME} Qt${QT_VERSION_MAJOR}::Core)
target_link_libraries(${TARGET_NAME} Qt${QT_VERSION_MAJOR}::Gui)
target_link_libraries(${TARGET_NAME} Qt${QT_VERSION_MAJOR}::Widgets)target_link_libraries(${TARGET_NAME} ${CTK_LIBRARIES})if (CMAKE_VERSION VERSION_GREATER 3.12)set_property(TARGET ${TARGET_NAME} PROPERTY CXX_STANDARD 20)
endif()# TODO: 如有需要,请添加测试并安装目标。

方法2: UI作为一个插件的方式

新建一个插件
在这里插入图片描述

DataWidget.h

// DataWidget.h
#include <QWidget>
#include <QMainWindow>
#include <QPushButton>
#include <QLineEdit>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QDebug>class DataWidget : public QWidget
{Q_OBJECT
public:explicit DataWidget(QWidget* parent = nullptr);~DataWidget(){qDebug() << "~DataWidget";};
signals:void processRequested(const QVector<int>& data); // UI 事件信号private slots:void onButtonClicked();void showResult(const QVector<int>& result);private:QLineEdit* m_inputEdit;QTextEdit* m_outputEdit;QPushButton* m_button;
};

cpp文件:

//DataWidget.cpp
#include "widget.h"DataWidget::DataWidget(QWidget* parent) : QWidget(parent)
{m_inputEdit = new QLineEdit("1,2,3", this);m_outputEdit = new QTextEdit(this);m_button = new QPushButton("处理数据", this);QVBoxLayout* layout = new QVBoxLayout();layout->addWidget(m_inputEdit);layout->addWidget(m_button);layout->addWidget(m_outputEdit);setLayout(layout);connect(m_button, &QPushButton::clicked, this, &DataWidget::onButtonClicked);
}void DataWidget::onButtonClicked()
{QStringList inputStrs = m_inputEdit->text().split(",");QVector<int> input;for (const QString& str : inputStrs) {input.append(str.toInt());}emit processRequested(input); // 触发处理请求
}void DataWidget::showResult(const QVector<int>& result) {QString text;for (int num : result) {text += QString::number(num) + " ";}m_outputEdit->setText(text);
}

激活器:

//Activator.h
#ifndef CTKACTIVATORUI_P_H
#define CTKACTIVATORUI_P_H#include <ctkPluginActivator.h>
#include <QScopedPointer>
#include <QObject>
#include <QPointer>class DataWidget;class ctkActivatorUI : public QObject, public ctkPluginActivator
{Q_OBJECTQ_INTERFACES(ctkPluginActivator)Q_PLUGIN_METADATA(IID "org.example.consumer")public:ctkActivatorUI();~ctkActivatorUI();void start(ctkPluginContext* context) override;void stop(ctkPluginContext* context) override;private:QPointer<DataWidget> m_widget;ctkPluginContext* context;void send(const QVector<int>&);};#endif

cpp文件:

//Activator.cpp#include "ctkActivatorUI_p.h"#include <ctkServiceReference.h>
#include <service/event/ctkEventAdmin.h>#include <QtGlobal>
#include <QStringList>#include "../pluginSL1_test/ctkFooService.h"#include "widget.h"#include <QApplication>
#include <QThread>ctkActivatorUI::ctkActivatorUI() : context(0){}
ctkActivatorUI::~ctkActivatorUI() {}void ctkActivatorUI::start(ctkPluginContext* context)
{this->context = context;m_widget = new DataWidget();m_widget->show();connect(m_widget, &DataWidget::processRequested, this, &ctkActivatorUI::send);
}void ctkActivatorUI::stop(ctkPluginContext* context)
{Q_UNUSED(context);//tracker->close();
}void ctkActivatorUI::send(const QVector<int>& vI)
{ctkServiceReference ref = context->getServiceReference<ctkEventAdmin>();if (ref){ctkEventAdmin* eventAdmin = context->getService<ctkEventAdmin>(ref);if (eventAdmin){ctkDictionary props;props["title"] = "CTK Event Admin";props["content"] = QString::number(vI[0]);props["author"] = "weijingao";ctkEvent event("org/commontk/bloggenerator/published", props);qDebug() << "====================================================";qDebug() << "=== Publisher sends a message ===";qDebug() << "props: " << props;eventAdmin->sendEvent(event);}context->ungetService(ref);}
}

cmake文件:

project(pluginUI_test)set(PLUGIN_export_directive "pluginUI_test_EXPORT")set(PLUGIN_SRCSctkActivatorUI.cppwidget.cpp
)# Files which should be processed by Qts moc
set(PLUGIN_MOC_SRCSctkActivatorUI_p.hwidget.h
)# Qt Designer files which should be processed by Qts uic
set(PLUGIN_UI_FORMS
)# QRC Files which should be compiled into the plugin
set(PLUGIN_resources
)#Compute the plugin dependencies
ctkFunctionGetTargetLibraries(PLUGIN_target_libraries)#Qt5包含路径
include_directories(${Qt5Core_INCLUDE_DIRS})
include_directories(${Qt5Gui_INCLUDE_DIRS})
include_directories(${Qt5Widgets_INCLUDE_DIRS})
include_directories(${Qt5Multimedia_INCLUDE_DIRS})
include_directories(${Qt5MultimediaWidgets_INCLUDE_DIRS})include_directories(${CTK_INCLUDE_DIRS})ctkMacroBuildPlugin(NAME ${PROJECT_NAME}EXPORT_DIRECTIVE ${PLUGIN_export_directive}SRCS ${PLUGIN_SRCS}MOC_SRCS ${PLUGIN_MOC_SRCS}UI_FORMS ${PLUGIN_UI_FORMS}RESOURCES ${PLUGIN_resources}TARGET_LIBRARIES ${PLUGIN_target_libraries}TEST_PLUGIN
)target_link_libraries(${PROJECT_NAME} PRIVATE  Qt${QT_VERSION_MAJOR}::Core)
target_link_libraries(${PROJECT_NAME} PRIVATE  Qt${QT_VERSION_MAJOR}::Gui)
target_link_libraries(${PROJECT_NAME} PRIVATE  Qt${QT_VERSION_MAJOR}::Widgets)

最后,在main函数中做如下调用:

int main(int argc, char* argv[])
{QApplication a(argc, argv);a.setApplicationName("CTK_PluginFramework");//给框架创建名称,Linux下没有会报错ctkPluginFrameworkFactory frameWorkFactory;QSharedPointer<ctkPluginFramework> framework = frameWorkFactory.getFramework();framework->init();framework->start();ctkPluginContext* pluginContext = framework->getPluginContext();try{// 定义插件路径列表QStringList pluginDirs = {"C:/CTK/x64-Debug/lib/ctk-0.1/plugins/liborg_commontk_eventadmin.dll","test_plugins/libpluginSL1_test.dll","test_plugins/libpluginSL3_test.dll","test_plugins/libpluginP1_test.dll","test_plugins/libpluginUI_test.dll"};// 遍历所有插件路径for (const QString& pluginPath : pluginDirs){// 安装插件QSharedPointer<ctkPlugin> plugin;if (pluginPath.contains("liborg_commontk_eventadmin.dll")){//ctkDictionary properties;//properties.insert("org.commontk.eventadmin.LogLevel", 0); // 0=关闭日志,4=DEBUG// 处理ctkEventAdmin插件plugin = pluginContext->installPlugin(QUrl::fromLocalFile(pluginPath));}else{plugin = pluginContext->installPlugin(QUrl::fromLocalFile(pluginPath));}qDebug() << QString("Plugin[%1_%2] installed...").arg(plugin->getSymbolicName()).arg(plugin->getVersion().toString());// 启动插件plugin->start(ctkPlugin::START_TRANSIENT);qDebug() << "Plugin start...";// 提取并打印元数据QHash<QString, QString> headers = plugin->getHeaders();ctkVersion version = ctkVersion::parseVersion(headers.value(ctkPluginConstants::PLUGIN_VERSION));QString name = headers.value(ctkPluginConstants::PLUGIN_SYMBOLICNAME);QString number = headers.value("Plugin-Number");qDebug() << "version: " << version;qDebug() << "name: " << name;qDebug() << "number: " << number;qDebug() << endl;qDebug() << endl;}}catch (const ctkPluginException& e){qDebug() << QString("Failed to install or run plugin: %1").arg(e.what());return -2;}a.exec();framework->stop();framework->waitForStop(0);return 0;
}

存在问题

不管是方案1,还是方案2,都会在退出时有崩溃问题产生。可能和使用的qt版本,CTK版本不适配有关系。

在这里插入图片描述
如果在末尾加入了

framework->stop();
framework->waitForStop(0);

则会在退出时,有如下报错:
在这里插入图片描述
后续可能换CTK版本进行测试。

相关文章:

  • C++回顾 Day5
  • upload-labs靶场通关详解:第二关
  • 代码随想录算法训练营第60期第二十九天打卡
  • 超越 DeepSeek-R1,英伟达新模型登顶
  • 在cursor中使用MCP插件生成旅行规划到桌面的执行步骤分析
  • 统计匹配的二元组个数 - 华为OD机试真题(A卷、JavaScript题解)
  • 破解逆向专辑(一)
  • Qt界面设计时窗口中各控件布局及自适应方法
  • 如何用FastMCP快速开发自己的MCP Server?
  • 云硬盘的原理
  • 分布式-Redis分布式锁
  • 从零开始学习three.js(15):一文详解three.js中的纹理映射UV
  • SimpleMindMap:一个支持AI的思维导图软件
  • UGUI如何使用EventTrigger
  • AI Workflow
  • 数据中心机电建设
  • 夸克网盘链接失效检测工具
  • 混淆矩阵(Confusion Matrix)
  • PWN基础-ROP技术-ret2syscall突破NX保护
  • Mongo3.4升级到mongo6性能降低9倍
  • 工行回应两售出金条发现疑似杂质:情况不属实,疑似杂质应为金条售出后的外部附着物
  • 安徽亳州涡阳县司法局党组书记刘兴连落马
  • 北京:下调个人住房公积金贷款利率
  • 马上评|孩子亲近自然只能靠公园露营了吗
  • 是谁提议特朗普向好莱坞征税?
  • 重庆荣昌机关食堂五一期间受热捧:肉类总消耗2万斤,单日吃卤鹅800只