CTK框架 - 第一个插件

简介: 前面我们已经介绍了CTK框架的基本信息,接下来我们来一步一步搭建CTK的第一个插件。

CTK框架 - 第一个插件

前面我们已经介绍了CTK框架的基本信息,接下来我们来一步一步搭建CTK的第一个插件。


上一篇文章地址:CTK框架介绍和环境搭建_turbolove的博客-CSDN博客


项目地址:CTKTEST 项目给了对应步骤的标签,可以根据标签拉取不同的代码


第一步: 创建项目

我们先创建一个CMake的空项目

6a38478ff29642a89e8aa639deda25e0.png然后将我们上一篇文章提到的CMakeList.txt替换到这个项目中,并且作出了一点小的调整,修改了项目的名称,并且添加了自动拷贝对应的动态库到运行目录。修改后的CmakeLists.txt如下

cmake_minimum_required(VERSION 3.23)
project(CTKTEST)
# debug需要覆盖对应的dll, 并且需要删除configuration文件夹,让它重新生成
#指定C++标准
set(CMAKE_CXX_STANDARD 17)
#指定输出目录
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/output)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/output)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/output)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/output)
#自动编译QT文件
#set(CMAKE_PREFIX_PATH "C:/Qt/6.5.1/msvc2019_64")
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
#开启包含当前编译目录
set(CMAKE_INCLUDE_CURRENT_DIR ON)
#指定QT版本和对应的库
set(QT_VERSION 5)
set(REQUIRED_LIBS Core Gui Widgets OpenGL Sql
        #        Core5Compat
        )
set(REQUIRED_LIBS_QUALIFIED Qt${QT_VERSION}::Core Qt${QT_VERSION}::Gui Qt${QT_VERSION}::Widgets Qt${QT_VERSION}::OpenGL Qt${QT_VERSION}::Sql
        #        Qt${QT_VERSION}::Core5Compat
        )
#寻找QT库
find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} REQUIRED)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src
        ${CMAKE_CURRENT_SOURCE_DIR}/third_party/CTK/include/ctk-0.1
        )
file(GLOB HEADERS
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h
        )
file(GLOB SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
        )
file(GLOB UIS
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.ui
        )
if (CMAKE_BUILD_TYPE AND (CMAKE_BUILD_TYPE STREQUAL "Debug"))
    file(GLOB LIB_CTK ${PROJECT_SOURCE_DIR}/third_party/CTK/libd/ctk-0.1/*.lib)
    link_directories(${PROJECT_SOURCE_DIR}/third_party/CTK/libd/ctk-0.1)
else ()
    file(GLOB LIB_CTK ${PROJECT_SOURCE_DIR}/third_party/CTK/lib/ctk-0.1/*.lib)
    link_directories(${PROJECT_SOURCE_DIR}/third_party/CTK/lib/ctk-0.1)
endif ()
# 指定格式为utf-8
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
#拷贝对应的动态库到执行目录(不用手动拷贝)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
    file(COPY "${PROJECT_SOURCE_DIR}/third_party/CTK/libd/ctk-0.1/CTKCore.dll" DESTINATION ${PROJECT_SOURCE_DIR}/output)
    file(COPY "${PROJECT_SOURCE_DIR}/third_party/CTK/libd/ctk-0.1/CTKPluginFramework.dll" DESTINATION ${PROJECT_SOURCE_DIR}/output)
    file(COPY "${PROJECT_SOURCE_DIR}/third_party/CTK/libd/ctk-0.1/plugins/liborg_commontk_eventadmin.dll" DESTINATION ${PROJECT_SOURCE_DIR}/output/lib/plugins)
else ()
    file(COPY "${PROJECT_SOURCE_DIR}/third_party/CTK/lib/ctk-0.1/CTKCore.dll" DESTINATION ${PROJECT_SOURCE_DIR}/output)
    file(COPY "${PROJECT_SOURCE_DIR}/third_party/CTK/lib/ctk-0.1/CTKPluginFramework.dll" DESTINATION ${PROJECT_SOURCE_DIR}/output)
    file(COPY "${PROJECT_SOURCE_DIR}/third_party/CTK/lib/ctk-0.1/plugins/liborg_commontk_eventadmin.dll" DESTINATION ${PROJECT_SOURCE_DIR}/output/lib/plugins)
endif ()
#使用指定的源文件来生成目标可执行文件
add_executable(${PROJECT_NAME} WIN32 main.cpp ${HEADERS} ${SOURCES} ${UIS})
target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBS_QUALIFIED} ${LIB_CTK})

文件目录结构如下:

e3b8e1259b4144eebf5273ec698b775e.png

将main.cpp也拷贝进来,做一些微调

#include <QApplication>
#include <iostream>
#include <QStyleFactory>
#include <QDir>
#include <QDirIterator>
#include "ctkPluginFrameworkFactory.h"
#include "ctkPluginFramework.h"
#include "ctkPluginException.h"
#include "ctkPluginContext.h"
#include "ctkPluginFrameworkLauncher.h"
int main(int argc, char* argv[])
{
    QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QApplication app(argc, argv);
    app.setApplicationName("ctktest");
    QString path = QCoreApplication::applicationDirPath();
    // 在插件的搜索路径列表中添加一条路径
#ifdef _DEBUG
    ctkPluginFrameworkLauncher::addSearchPath(path + "/libs/plugins");
#else
    ctkPluginFrameworkLauncher::addSearchPath(path + "/libs/plugins");
#endif // _DEBUG
    // 设置并启动 CTK 插件框架
    try {
        ctkPluginFrameworkLauncher::start("org.commontk.eventadmin");
    }
    catch (ctkException e)
    {
        std::cout << e.message().toStdString() << std::endl;
    }
    // 启动插件工厂
    ctkPluginFrameworkFactory* ctkFrameWorkFactory = new ctkPluginFrameworkFactory;
    QSharedPointer<ctkPluginFramework> framework= ctkFrameWorkFactory->getFramework();
    try{
        framework->init();
        framework->start();
    }
    catch(const ctkPluginException& e)
    {
        std::cout << "framework init fail" << std::endl;
    }
    return app.exec();
}

这个时候编译运行,插件工厂启动成功,说明我们的项目配置没有问题,即可进行下一步。


第二步:编写我们的第一个插件

我们来写一个Core插件作为我们的核心插件,在这之前我们需要知道CTK插件的编写规则。

首先创建core文件夹,目录结构如下图:

57ae8607940041618e2d6b496c295f16.png

Core的CMakeLists.txt如下:

cmake_minimum_required(VERSION 3.19)
project(Core)
#指定C++标准
set(CMAKE_CXX_STANDARD 17)
#指定输出目录
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/../output/plugins)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/../output/plugins)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/../output/plugins)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/../output/plugins)
#自动编译QT文件
#set(CMAKE_PREFIX_PATH "C:/Qt/6.5.1/msvc2019_64")
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
#开启包含当前编译目录
set(CMAKE_INCLUDE_CURRENT_DIR ON)
#指定QT版本和对应的库
set(QT_VERSION 5)
set(REQUIRED_LIBS Core Gui Widgets
#        Core5Compat
        )
set(REQUIRED_LIBS_QUALIFIED Qt${QT_VERSION}::Core Qt${QT_VERSION}::Gui Qt${QT_VERSION}::Widgets
#        Qt${QT_VERSION}::Core5Compat
        )
#寻找QT库
find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} REQUIRED)
include_directories(src
        ../third_party/CTK/include/ctk-0.1
        )
file(GLOB HEADERS
        src/*.h
        )
file(GLOB SOURCES
        src/*.cpp
        )
file(GLOB UIS
        src/*.ui
        )
# 指定格式为utf-8
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
if (CMAKE_BUILD_TYPE AND (CMAKE_BUILD_TYPE STREQUAL "Debug"))
    file(GLOB LIB_CTK ${PROJECT_SOURCE_DIR}/../third_party/CTK/libd/ctk-0.1/*.lib)
    link_directories(${PROJECT_SOURCE_DIR}/../third_party/CTK/libd/ctk-0.1)
else ()
    file(GLOB LIB_CTK ${PROJECT_SOURCE_DIR}/../third_party/CTK/lib/ctk-0.1/*.lib)
    link_directories(${PROJECT_SOURCE_DIR}/../third_party/CTK/lib/ctk-0.1)
endif ()
#使用指定的源文件来生成目标可执行文件
add_library(${PROJECT_NAME} SHARED resources.qrc ${HEADERS} ${SOURCES} ${UIS})
target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBS_QUALIFIED} ${LIB_CTK})

Activator注册器

每个插件都有自己的注册器,因此我们需要给我们的插件写一个注册器。


mainwindowactivator.h

#ifndef CTKTEST_MAINWINDOWACTIVATOR_H
#define CTKTEST_MAINWINDOWACTIVATOR_H
#include <QObject>
#include "ctkPluginActivator.h"
class MainWindowActivator : public QObject, public ctkPluginActivator
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "Core")
    Q_INTERFACES(ctkPluginActivator)
public:
    MainWindowActivator();
    void start(ctkPluginContext *context);
    void stop(ctkPluginContext *context);
};
#endif //CTKTEST_MAINWINDOWACTIVATOR_H

mainwindowactivator.cpp

#include "mainwindowactivator.h"
#include <iostream>
MainWindowActivator::MainWindowActivator()
{
}
void MainWindowActivator::start(ctkPluginContext *context)
{
    std::cout <<  "core start" << std::endl;
}
void MainWindowActivator::stop(ctkPluginContext *context)
{
    std::cout <<  "core stop" << std::endl;
}

资源文件

我们需要给插件创建一个资源文件,资源文件的格式如下:

<RCC>
 <qresource prefix="/Core/META-INF">
   <file>MANIFEST.MF</file>
    </qresource>
</RCC>

插件加载后会寻找同名前缀/META-INF,所以前缀格式固定,将MANIFEST.MF文件添加进来


MENIFEST.MF文件内容如下:

Plugin-SymbolicName:Core
Plugin-Version:1.0.0

注意: 资源文件的prefix必须填写成Target/META-INF 其中 Target最好和工程名称一致,因此我这里写的是Core,工程名称是CMakeLists.txt中的project的名称。Target中不能有下划线,否则也找不到正确的文件。


运行插件

我们先编译Core项目将Core.dll编译出来,之后修改main函数如下,这里添加了加载库的部分:

#include <QApplication>
#include <iostream>
#include <QStyleFactory>
#include <QDir>
#include <QDirIterator>
#include "ctkPluginFrameworkFactory.h"
#include "ctkPluginFramework.h"
#include "ctkPluginException.h"
#include "ctkPluginContext.h"
#include "ctkPluginFrameworkLauncher.h"
int main(int argc, char* argv[])
{
    QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QApplication app(argc, argv);
    app.setApplicationName("ctktest");
    QString path = QCoreApplication::applicationDirPath();
    // 在插件的搜索路径列表中添加一条路径
#ifdef _DEBUG
    ctkPluginFrameworkLauncher::addSearchPath(path + "/libs/plugins");
#else
    ctkPluginFrameworkLauncher::addSearchPath(path + "/libs/plugins");
#endif // _DEBUG
    // 设置并启动 CTK 插件框架
    try {
        ctkPluginFrameworkLauncher::start("org.commontk.eventadmin");
    }
    catch (ctkException e)
    {
        std::cout << e.message().toStdString() << std::endl;
    }
    // 启动插件工厂
    ctkPluginFrameworkFactory* ctkFrameWorkFactory = new ctkPluginFrameworkFactory;
    QSharedPointer<ctkPluginFramework> framework= ctkFrameWorkFactory->getFramework();
    try{
        framework->init();
        framework->start();
    }
    catch(const ctkPluginException& e)
    {
        std::cout << "framework init fail" << std::endl;
    }
    QString dir = QCoreApplication::applicationDirPath();
    dir += "/plugins/Core.dll";
    QUrl url = QUrl::fromLocalFile(dir);
    QSharedPointer<ctkPlugin> plugin;
    try
    {
        plugin = framework->getPluginContext()->installPlugin(url);
    }catch(ctkPluginException e){
        std::cout << e.message().toStdString() << e.getType() << std::endl;
    }
    try{
        plugin->start(ctkPlugin::START_TRANSIENT);
    }catch(ctkPluginException e){
        std::cout << e.message().toStdString() << e.getType() << std::endl;
    }
    return app.exec();
}

再编译运行CTKTEST的时候输出core start,到此说明我们的插件加载成功了。

目录
相关文章
|
Linux
Linux制作deb格式安装包教程
Linux制作deb格式安装包教程
1979 0
|
9月前
|
安全 前端开发 JavaScript
从胡志明市的麻袋到柬埔寨诈骗园区:一名韩国“钓鱼工程师”的坠落,揭开跨国网络犯罪黑产链
2025年11月,越南胡志明市发现一名重伤的韩国男子,揭开了柬埔寨一跨国网络诈骗集团的黑幕。该团伙以“公司化”模式运营,利用高仿真钓鱼平台 targeting 韩国投资者,技术骨干李承久因内斗遭弃尸未遂。案件暴露东南亚诈骗产业链:IT人员被诱骗参与、技术沦为犯罪工具。中韩警方联合打击,呼吁警惕“高薪海外IT岗”陷阱,强调技术伦理与全球协作反诈。
826 5
|
C语言 C++ Windows
QT多插件通信框架CTK编译记录
本文记录了编译QT多插件通信框架CTK的过程,包括编译结果截图、部署配置、Log4Qt编译配置、参考链接和拓展资料。文中提供了详细的编译步骤和配置文件示例,以及相关的资源链接。
926 0
QT多插件通信框架CTK编译记录
|
机器学习/深度学习 人工智能 自然语言处理
20用于深度学习训练和研究的数据集
无论是图像识别,自然语言处理,医疗保健还是任何其他人工智能领域感兴趣,这些数据集都是非常重要的,所以本文将整理常用且有效的20个数据集。
1370 1
|
人工智能 IDE 开发工具
从0到1彻底掌握Trae:手把手带你实战开发AI Chatbot,提升开发效率的必备指南!
Trae是字节跳动推出的一款免费的AI集成的开发环境,集成了Claude3.5与GPT-4o等主流AI模型,提供AI问答、智能代码生成、智能代码补全,多模态输入等功能。支持界面全中文化,为中文开发者提供了高效的开发体验
11793 11
从0到1彻底掌握Trae:手把手带你实战开发AI Chatbot,提升开发效率的必备指南!
|
JavaScript 前端开发 索引
vue学习第三章
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中的v-bind指令,包括基本使用、动态绑定class及style等,希望能为你的前端学习之路提供帮助。持续关注,更多精彩内容即将呈现!🎉🎉🎉
312 1
vue学习第三章
|
存储 缓存 Oracle
Java线程池,白话文vs八股文,原来是这么回事!
本文介绍了Java线程池的原理、实现方式及相关参数。首先,通过类比公司员工的方式解释了线程池的核心概念,如核心线程、最大线程数、任务队列和拒绝策略。接着,详细描述了线程池的任务处理流程,并提供了使用`ThreadPoolExecutor`和`Executors`创建线程池的代码示例,强调了`ThreadPoolExecutor`的灵活性和`Executors`的局限性。最后,总结了线程池的相关参数及不同类型的线程池实现,并附带常见面试题及其解答,帮助读者全面理解线程池的应用场景和优化方法。
424 4
|
存储 定位技术 Swift
Swift 中的枚举与结构体,包括它们的定义、特性、使用场景及示例
本文深入探讨了 Swift 中的枚举与结构体,包括它们的定义、特性、使用场景及示例。枚举适合表示有限的、离散的状态或选项,结构体则适用于具有特定属性和行为的数据结构。两者在存储方式、继承性和灵活性上有所不同,但在实际开发中常结合使用,以充分发挥各自优势。
508 3
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
4640 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
存储 搜索推荐 Linux
深度探索Linux操作系统 —— 构建桌面环境1
深度探索Linux操作系统 —— 构建桌面环境
748 8

热门文章

最新文章