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