CTK框架 - 第一个插件
前面我们已经介绍了CTK框架的基本信息,接下来我们来一步一步搭建CTK的第一个插件。
上一篇文章地址:CTK框架介绍和环境搭建_turbolove的博客-CSDN博客
项目地址:CTKTEST 项目给了对应步骤的标签,可以根据标签拉取不同的代码
第一步: 创建项目
我们先创建一个CMake的空项目
然后将我们上一篇文章提到的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})
文件目录结构如下:
将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文件夹,目录结构如下图:
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,到此说明我们的插件加载成功了。