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,到此说明我们的插件加载成功了。

目录
相关文章
|
4月前
|
安全 网络协议 数据安全/隐私保护
掌握Qt和C++:构建你的第一个P2P应用程序
掌握Qt和C++:构建你的第一个P2P应用程序
222 3
|
11月前
|
开发者
Flutter笔记:build方法、构建上下文BuildContext解析
本文主要介绍Flutter中的build方法和构建上下文对象。
337 2
Flutter笔记:build方法、构建上下文BuildContext解析
|
开发工具 git
【Ant Design Pro】使用ant design pro做为你的开发模板(五)去除无效代码,生成一个清晰的开发模板
【Ant Design Pro】使用ant design pro做为你的开发模板(五)去除无效代码,生成一个清晰的开发模板
675 0
【Ant Design Pro】使用ant design pro做为你的开发模板(五)去除无效代码,生成一个清晰的开发模板
|
缓存 JavaScript 安全
【Ant Design Pro】使用ant design pro做为你的开发模板(四) 联调正式后台接口与运行时全局配置
【Ant Design Pro】使用ant design pro做为你的开发模板(四) 联调正式后台接口与运行时全局配置
1292 0
【Ant Design Pro】使用ant design pro做为你的开发模板(四) 联调正式后台接口与运行时全局配置
|
API
【Ant Design Pro】使用ant design pro做为你的开发模板(九)开发第一个完整的后台页面(二)
【Ant Design Pro】使用ant design pro做为你的开发模板(九)开发第一个完整的后台页面(二)
759 0
【Ant Design Pro】使用ant design pro做为你的开发模板(九)开发第一个完整的后台页面(二)
|
JavaScript API 开发者
为依赖Angular.js的上古项目给VSCode编写$scope定义跳转扩展插件
虽然Angular.js停止更新已经一年了,但依赖它的上古时代的项目并不少。由于都是使用js开发,很难为其提供很好的维护,所以直到今天开发维护也并不愉快。可以说没有开发插件的支持,再成熟老练的框架都发挥困难。
154 0
为依赖Angular.js的上古项目给VSCode编写$scope定义跳转扩展插件
CTK框架 - 插件依赖关系 - 给插件加上界面
在第一篇文章中吗,我们提到了MANIFEST.MF中有个字段是依赖关系,这次我们来测试一下这个依赖关系:
150 0
CTK框架 - 通信 - 插件服务注册和调用
接口就是虚函数(也可以是纯虚函数),也就是最终的服务的前身。 接口对外暴露功能,比如我们给之前写的mainwindow 加一个界面类,并且对外暴露一个popWindow()的接口
110 0
|
前端开发
【React工作记录三十五】ant design中默认回调加入参数
【React工作记录三十五】ant design中默认回调加入参数
54 0
|
前端开发
#yyds干货盘点 【React工作记录三十五】ant design中默认回调加入参数
#yyds干货盘点 【React工作记录三十五】ant design中默认回调加入参数
72 0