CTK框架——CTK Plugin Framework快速入门

CTK框架——CTK Plugin Framework快速入门

一、CTK Plugin Framework简介

1、CTK Plugin Framework简介

CTK Plugin Framework基于Qt Plugin System和Qt Service Framework实现,并且增加了以下特性来扩展:
A、插件元数据(由MANIFEST.MF文件提供);
B、一个定义良好的插件生命周期和上下文;
C、综合服务发现和注册;
在Qt Plugin System中,插件的元数据由JSON文件提供。
CTK Plugin Framework的核心架构主要包含两个组件:Plugin System和Service Registry。这两个组件是相互关联的,在API级别上的组合使得系统更加全面、灵活。
A、Plugin System
CTK Core依赖于QtCore模块,因此CTK Plugin Framework基于Qt Plugin System。Qt API允许在运行时加载和卸载插件,热插拔功能在CTK Plugin Framework中得到了加强,以支持透明化延迟加载和解决依赖关系。
插件的元数据被编译进插件内部,可以通过API进行提取。此外,插件系统还使用SQLite缓存了元数据,以避免应用程序加载时间问题。另外,Plugin System支持通过中央注册中心使用服务。
B、Service Registry
Qt Service Framework是Qt Mobility项目发布的一个Qt 解决方案,Qt服务框架允许“声明式服务”和按需加载服务实现。为了启用动态(非持久性)服务,Qt Mobility服务框架可以与Service Registry一起使用。

2、CTK Plugin Framework框架的优点

CTK Plugin Framework以OSGi规范为模型,并实现了几乎完整的OSGI框架API,因此使用CTK Plugin Framework开发基于Qt的C++应用程序有如下优点:
A、降低复杂性
使用CTK Plugin Framework进行应用开发只需进行插件开发,插件隐藏了内部实现,并通过定义良好的服务来和其它插件通信。隐藏内部机制意味着可以自由地更改实现,不仅有助于Bug数量的减少,还使得插件的开发变得更加简单,因为只需要实现已经定义好的一定数量的功能接口即可。
B、可复用
标准化的组件模型,在应用程序中使用第三方组件变得非常简单。
C、版本控制
在CTK Plugin Framework中,所有的插件都经过严格的版本控制,只有能够协作的插件才会被连接在一起。
D、动态更新
OSGi组件模型是一个动态模型,插件可以在不关闭整个系统的情况下被安装、启动、停止、更新和卸载。
E、自适应
OSGi组件模型是从头设计的,以允许组件的混合和匹配,要求必须指定组件的依赖关系,并且需要组件在其可选依赖性并不总是可用的环境中生存。Service Registry是一个动态注册表,其中插件可以注册、获取和监听服务。OSGI动态服务模型允许插件找出系统中可用的功能,并调整它们所能提供的功能,使得代码更加灵活, 并且能够更好地适应变化。
F、透明性
插件和服务是CTK插件环境中的一等公民。管理API提供了对插件的内部状态的访问,以及插件之间的连接方式。可以停止部分应用程序来调试某个问题,或者可以引入诊断插件。
G、开发简单
CTK插件相关的API非常简单,核心API不到25个类。核心API足以编写插件、安装、启动、停止、更新和卸载,并且还包含了所有的监听类。
CTK Plugin Framework不仅仅是组件的标准,还指定了如何安装和管理组件的API。API可以被插件用来提供一个管理代理,管理代理可以非常简单,如命令shell、图形桌面应用程序、Amazon EC2的云计算接口、或IBM Tivoli管理系统。标准化的管理API 使得在现有和未来的系统中集成CTK Plugin Framework变得非常容易。
H、懒加载
OSGi技术有很多的机制来保证只有当类真正需要的时候才开始加载插件。例如,插件可以用饿汉式启动,但是也可以被配置为仅当其它插件使用它们时才启动。服务可以被注册,但只有在使用时才创建。懒加载场景可以节省大量的运行时成本。
I、非独占性
CTK Plugin Framework不会接管整个应用程序,可以选择性地将所提供的功能暴露给应用程序的某些部分,或者甚至可以在同一个进程中运行该框架的多个实例。
J、非侵入
在一个CTK插件环境中,不同插件均有自己的环境。插件可以使用任何设施,框架对此并无限制。CTK服务没有特殊的接口需求,每个QObject都可以作为一个服务,每个类(包括非QObject)都可以作为一个接口。

二、CTK Plugin Framework架构

1、CTK Plugin Framework简介

CTK Plugin Framework设计参考了OSGi(Java的动态组件系统),并提供了一种能让应用程序(动态地)由许多不同的可重用组件组成的开发模型,允许通过服务进行通信。
OSGi规范的核心部分是一个框架,核心框架定义了应用程序的生命周期模式和服务注册。基于OSGI核心框架定义了大量的OSGi服务:日志、配置管理、HTTP(运行servlet)、XML分析、设备访问、软件包管理、许可管理、用户管理、IO连接、连线管理、Jini和UPnP。
CTK Plugin Framework框架的分层模型如下:

A、Plugins(插件):由开发人员创建的CTK组件;
B、Services Layer(服务层):通过为C++对象提供一个
publish-find-bind模型,以动态方式连接插件;
C、Life Cycle Layer(生命周期层):用于安装、启动、停止、更新和卸载插件的API;
D、Security(安全性):处理安全方面。

2、插件模块层

Plugin是CTK Plugin Framework的核心,是模块化特性的体现。
插件由插件激活器类Activator启动,激活器可以获取代表插件框架的插件上下文环境,插件上下文对象不能共享。

Plugin是基于C++/Qt的一个共享库,包含了资源文件和元数据(metadata)。
元数据的目的在于准确描述Plugin的特征,除了让CTK Plugin Framework对Plugin适当地进行各种处理(例如:依赖解析)外,还能更好的对Plugin进行标识,以帮助用户对Plugin进行理解。

元数据被定义在MANIFEST.MF文件中,典型的MANIFEST.MF文件如下:

Plugin-SymbolicName: HelloCTK
Plugin-ActivationPolicy: eager
Plugin-Category: demo
Plugin-ContactAddress: https://github.com/scorpiostudio
Plugin-Description: A plugin for say hello
Plugin-Name: HelloCTK
Plugin-Vendor: scorpio
Plugin-Version: 1.0.0

元数据主要分为两部分:
A、Plugin的标识符(必须):唯一标识一个 Plugin,由Plugin-SymbolicName表示。
B、可读信息(可选):帮助更好地理解和使用Plugin,不对模块化特性产生任何的影响。可选信息如Plugin-Name、Plugin-Vendor。
3、服务层
CTK插件框架提供了插件间通信的动态服务模型,一个激活的插件可以在任何时候注册(注销)0个或多个服务到框架。服务注册是一个具有可选注册属性的发布接口。通过接口和过滤表达式可以从插件框架获得服务引用。框架发布服务生命周期事件。
服务可以通过ctkPluginContext对象注册到插件框架。服务的注册和注销可以在任何时候进行。
服务是服务的提供者和使用者之间的一个契约,使用者一般不关心其实现的细节,只关心是否满足契约(服务应该提供什么功能、满足什么格式)。使用服务的过程包含了发现服务和达成协议的形式,即需要通过服务的标志性特征来找到对应的服务。
一个插件可以创建一个对象,并在一个或多个接口(通常是一个只有纯虚方法的C++类)下使用CTK Service Registry注册它。其它插件可以要求registry列出在特定接口下注册的所有服务(对象)。一个插件甚至可以等待一个特定的服务出现,然后收到回复。
因此,一个插件可以注册一个服务,也可以获得一个服务并侦听服务的出现或消失。任意数量的插件可以在相同的接口下注册服务,并且任意数量的插件都可以得到相同的服务。publish-find-bind模型如下:

如果多个插件在同一个接口下注册对象,则可以通过其属性进行区分。每个服务注册都有一套标准的自定义属性,可以使用过滤器来选择感兴趣的服务。属性也可以被用于应用程序级的其他角色。
A、发布服务
为了让其它Plugin能发现服务,必须用上下文对其进行注册,需要用到接口名、服务对象(接口的具体实现)和一个可选的ctkDictionary类型的属性信息:
ctkDictionary properties;
properties.insert("name", "scorpio");
properties.insert("age", 30);
ctkServiceRegistration registration = context->registerService<HelloService>(new HelloServiceImpl(), properties);
得到一个ctkServiceRegistration对象,用于更新服务的属性:
registration.setProperties(newProperties);
注销服务:
registration.unregister();
registration对象不能和其它Plugin共享,因为registration对象和发布服务的Plugin的生命周期相互依存。如果Plugin已经不存在于框架执行环境中,那么registration对象也不应该存在。
此外,如果在删除发布的服务前Plugin停止,框架会帮助删除这些服务。
B、获取服务
一旦服务被发布,服务将对其他Plugin可用。获取服务的方式非常简单,只需要提供一个接口名即可:

ctkServiceReference reference = context->getServiceReference<HelloService>();

reference对象是服务对象的间接引用。间接引用可以将服务的使用和服务的实现进行解耦。将服务注册表作为两者的中间人,不仅能够达到跟踪和控制服务的目的,同时还可以在服务消失以后通知使用者。
接口的返回类型是ctkServiceReference,可以在Plugin之间互享,因为reference对象和使用服务的Plugin的生命周期无关。

4、生命周期层

生命周期层主要用于控制Plugin的安装、启动、停止、更新和卸载,可以从外部管理应用或者建立能够自我管理的应用(或将两者相结合),并且给了应用本身很大的动态性。
Plugin的使用需要使用生命周期层的API来和CTK Plugin Framework的生命周期层进行交互。
Plugin生命周期的状态转换图:

生命周期层的API主要由三个核心部分组成:ctkPluginActivator、ctkPluginContext和ctkPlugin。
(1)、ctkPluginActivator
ctkPluginActivator:自定义plugin的启动和停止。
ctkPluginActivator是一个接口,必须由框架中的每个插件实现。插件必须提供一个由插件框架调用的插件激活器类。框架可以根据需要创建一个插件的ctkPluginActivator实例。如果一个实例的ctkPluginActivator::start()方法成功执行,则需要保证在插件停止时调用同一个实例的ctkPluginActivator::stop() 方法。
当插件进入ACTIVE状态时,框架会调用start方法,当插件离开ACTIVE状态时,插件框架会调用stop方法。每一个插件都会接收到一个访问插件框架的唯一ctkPluginContext对象。
(2)、ctkPluginContext
ctkPluginContext是一个plugin在框架内的执行上下文,用于授予对其它方法的访问,以便该插件可以与框架交互。
ctkPluginContext提供的方法允许插件:
A、订阅由框架发布的事件;
B、使用Framework Service Registry注册服务对象;
C、从Framework Service Registry检索ServiceReferences;
D、为引用的服务获取和发布服务对象;
E、在框架中安装新的插件;
F、获取框架中安装的插件列表;
G、获得一个插件的ctkPlugin对象;
H、为(由框架为插件提供的)持久存储区域中为文件创建QFile对象。
当使用ctkPluginActivator::start()方法启动时,将创建一个 ctkPluginContext对象,并将其提供给与此上下文关联的插件。当使用ctkPluginActivator::stop()方法停止时,相同的ctkPluginContext对象将被传递给与此上下文关联的插件。ctkPluginContext对象通常用于其关联插件的私有用途,并不意味着与插件环境中的其它插件共享。
与ctkPluginContext对象关联的ctkPlugin对象称为上下文插件。
ctkPluginContext对象只有在它的上下文插件执行时才有效;即在上下文插件处于STARTING、STOPPING和ACTIVE状态的时段内。如果随后使用ctkPluginContext对象,则必须抛出一个ctkIllegalStateException异常。当上下文插件停止后,ctkPluginContext对象不能被重用。
Framework是唯一能够创建ctkPluginContext对象的实体,并且ctkPluginContext对象只在创建它们的Framework中有效。
(3)、ctkPlugin
ctkPlugin是Framework中已安装的插件。
ctkPlugin对象是定义一个已安装插件的生命周期的访问点,在插件环境中安装的每个插件都必须有一个相关的ctkPlugin对象。此外,插件必须有一个唯一的标识,在插件的生命周期中,唯一标识不能改变(即使是在插件更新时),卸载和重新安装插件必须创建一个新的唯一标识。
插件有以下状态(状态是动态可变的,在特定条件下可以互相转换):
UNINSTALLED
INSTALLED
RESOLVED
STARTING
STOPPING
ACTIVE
要确定插件是否处于有效状态之一,可以使用States类型进行“或”运算。
插件只能在状态为STARTING、ACTIVE或STOPPING状态时执行代码。一个UNINSTALLED插件是一个僵尸,不能被设置为另一个状态。
框架是唯一允许创建ctkPlugin对象的实体,并且ctkPlugin对象仅在创建它们的框架内有效。

三、CTK Plugin Framework加载插件

1、CTK Plugin Framework使用流程

CTK插件框架的使用流程如下:
A、初始化并启动插件框架:由ctkPluginFramework::init() 和ctkPluginFramework::start()完成;
B、获取上下文:由ctkPluginFramework::getPluginContext()完成
C、安装插件:由ctkPluginContext::installPlugin()完成,返回一个ctkPlugin对象。
D、启动插件:由ctkPlugin::start()完成。
E、获取服务引用:由ctkPluginContext::getServiceReference()完成。
F、获取指定ctkServiceReference引用的服务对象:由ctkPluginContext::getService()完成。
G、调用服务。

2、创建CTK插件

本节创建一个简单的插件。
接口定义HelloService.h如下:

#ifndef HELLOSERVICE_H
#define HELLOSERVICE_H

#include <QtPlugin>

class HelloService
{
public:
    virtual ~HelloService() {}
    virtual void printHello() = 0;
};

#define HelloService_iid "org.commontk.service.demo.HelloService"
Q_DECLARE_INTERFACE(HelloService, HelloService_iid)

#endif // HELLOSERVICE_H

接口实现:
HelloServiceImpl.h文件:

#ifndef HELLOSERVICEIMPL_H
#define HELLOSERVICEIMPL_H

#include <QObject>
#include "HelloService.h"
#include <ctkPluginContext.h>

class HelloServiceImpl : public QObject, public HelloService
{
    Q_OBJECT
    Q_INTERFACES(HelloService)
public:
    HelloServiceImpl(ctkPluginContext* context);
    void printHello();
};

#endif // HELLOSERVICEIMPL_H

HelloServiceImpl.cpp文件:

#include "HelloServiceImpl.h"
#include <ctkPluginContext.h>
#include <QtDebug>

HelloServiceImpl::HelloServiceImpl(ctkPluginContext *context)
{
    context->registerService<HelloService>(this);
}

void HelloServiceImpl::printHello()
{
    qDebug() << "Hello,CTK Plugin!";
}

插件实现:
HelloCTKPlugin.h文件:

#ifndef HELLOCTKPLUGIN_H
#define HELLOCTKPLUGIN_H

#include <QObject>
#include <QtPlugin>
#include <ctkPluginActivator.h>
#include <QScopedPointer>
#include "HelloService.h"

class HelloCTKPlugin : public QObject, public ctkPluginActivator
{
    Q_OBJECT
    Q_INTERFACES(ctkPluginActivator)
    //Qt5版本
#if(QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
    Q_PLUGIN_METADATA(IID "HelloCTKPlugin")
#endif
public:
    void start(ctkPluginContext* context);
    void stop(ctkPluginContext* context);
private:
    QScopedPointer<HelloService> m_service;

};

#endif // HELLOCTKPLUGIN_H

HelloCTKPlugin.cpp文件:

#include "HelloCTKPlugin.h"
#include "HelloServiceImpl.h"

void HelloCTKPlugin::start(ctkPluginContext *context)
{
    m_service.reset(new HelloServiceImpl(context));
}

void HelloCTKPlugin::stop(ctkPluginContext *context)
{
    Q_UNUSED(context)
}
//Qt4版本
#if(QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
Q_EXPORT_PLUGIN2(HelloCTK, HelloCTKPlugin)
#endif

插件元数据文件MANIFEST.MF:

Plugin-SymbolicName: HelloCTKPlugin
Plugin-ActivationPolicy: eager
Plugin-Category: demo
Plugin-ContactAddress: https://github.com/scorpiostudio
Plugin-Description: A plugin for print hello
Plugin-Name: HelloCTKPlugin
Plugin-Vendor: scorpio
Plugin-Version: 1.0.0

QRC资源文件resource.qrc:

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

插件工程文件HelloCTK.pro:

QT += core
QT -= gui

TARGET = HelloCTKPlugin
CONFIG += plugin
TEMPLATE = lib

# CTK 安装路径
CTK_INSTALL_PATH = /usr/local/CTK

# CTK插件相关库所在路径(CTKCore.lib、CTKPluginFramework.lib)
CTK_LIB_PATH = $$CTK_INSTALL_PATH/lib/ctk-0.1

# CTK插件相关头文件所在路径(ctkPluginFramework.h)
CTK_INCLUDE_PATH = $$CTK_INSTALL_PATH/include/ctk-0.1

LIBS += -L$$CTK_LIB_PATH -lCTKCore -lCTKPluginFramework

INCLUDEPATH += $$CTK_INCLUDE_PATH

HEADERS +=     HelloService.h     HelloServiceImpl.h     HelloCTKPlugin.h

SOURCES +=     HelloServiceImpl.cpp     HelloCTKPlugin.cpp

RESOURCES +=     Resource.qrc

工程树如下:

3、CTK插件框架使用实例

本例以HelloCTKPlugin插件为例,演示如何使用CTK Plugin Framework来加载插件并获取特定的服务。
工程文件如下:

QT       += core
QT       -= gui
TARGET = test
CONFIG   += console
CONFIG   -= app_bundle

TEMPLATE = app

#CTK安装路径
CTK_INSTALL_PATH = /usr/local/CTK

#CTK插件相关库所在路径(CTKCore.lib、CTKPluginFramework.lib)
CTK_LIB_PATH = $$CTK_INSTALL_PATH/lib/ctk-0.1

#CTK插件相关头文件所在路径(ctkPluginFramework.h)
CTK_INCLUDE_PATH = $$CTK_INSTALL_PATH/include/ctk-0.1

LIBS += -L$$CTK_LIB_PATH -lCTKCore -lCTKPluginFramework
INCLUDEPATH += $$CTK_INCLUDE_PATH

SOURCES += main.cpp

main.cpp文件:

#include <QCoreApplication>
#include <QDirIterator>
#include <QtDebug>

#include <ctkPluginFrameworkFactory.h>
#include <ctkPluginFramework.h>
#include <ctkPluginException.h>
#include <ctkPluginContext.h>

#include "../HelloCTK/HelloService.h"

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    ctkPluginFrameworkFactory frameWorkFactory;
    QSharedPointer<ctkPluginFramework> framework = frameWorkFactory.getFramework();
    try
    {
        // 初始化并启动插件框架
        framework->init();
        framework->start();
        qDebug() << "CTK Plugin Framework start ...";
    }
    catch (const ctkPluginException &e)
    {
        qDebug() << "Failed to initialize the plugin framework: " << e.what();
        return -1;
    }

    // 获取插件上下文
    ctkPluginContext* context = framework->getPluginContext();

    // 获取插件所在位置
    QString path = QCoreApplication::applicationDirPath() + "/plugins";

    // 遍历路径下的所有插件
    QDirIterator itPlugin(path, QStringList() << "*.dll" << "*.so", QDir::Files);
    while (itPlugin.hasNext())
    {
        QString strPlugin = itPlugin.next();
        try
        {
            // 安装插件
            QSharedPointer<ctkPlugin> plugin = context->installPlugin(QUrl::fromLocalFile(strPlugin));
            // 启动插件
            plugin->start(ctkPlugin::START_TRANSIENT);
            qDebug() << QString("Plugin %1 start ...").arg(
                         plugin.data()->getSymbolicName());
        }
        catch (const ctkPluginException &e)
        {
            qDebug() << "Failed to install plugin" << e.what();
            return -1;
        }
    }

    // 获取服务引用
    ctkServiceReference reference = context->getServiceReference<HelloService>();
    if (reference)
    {
        // 获取指定 ctkServiceReference 引用的服务对象
        HelloService* service = qobject_cast<HelloService *>(
                                    context->getService(reference));
        if (service != NULL)
        {
            // 调用服务
            service->printHello();
        }
    }

    return app.exec();
}

ctkPluginContext是框架内一个插件的执行上下文,ctkPluginContext用于授予对其它方法的访问权,以便插件可以与框架进行交互。使用ctkPluginContext对象可以安装新的插件,并获得发布服务对象。
在运行程序前,需要在构建目录中(或可执行程序同级目录)创建一个plugins目录,用于专门存放插件。
将HelloCTKPlugin插件libHelloCTKPlugin.so放到plugins目录下,执行结果如下:

HelloCTKPlugin插件已经可以对外提供服务。

原文地址:http://blog.51cto.com/9291927/2122371

时间: 2024-10-11 00:19:55

CTK框架——CTK Plugin Framework快速入门的相关文章

CTK框架——CTK Widgets快速入门

CTK框架--CTK Widgets快速入门 一.CTK Widgets模块简介 1.CTK Widgets模块简介 CTK Widgets模块是CTK封装的用于通用功能以及生物医学成像专用领域的Qt组件库.CTK中所有部分都有大量的测试相关代码,在源码目录下Libs/xxx/Testin/Cpp目录中.CTK官方文档:http://www.commontk.org/docs/html/modules.htmlImageGallery演示了CTK Widgets模块大部分组件的效果:http:/

实体框架(Entity Framework)快速入门--实例篇

在上一篇 <实体框架(Entity Framework)快速入门> 中我们简单了解的EF的定义和大体的情况,我们通过一步一步的做一个简单的实际例子来让大家对EF使用有个简单印象,看操作步骤 第一步:创建控制台项目 这个就不多说了,如果新建项目你还不知道,那先去学学基础吧. 第二步:创建实体模型 在项目上右击 添加新建项目→Ado .Net 实体数据模型 如下图所示: 第三步:与现有的 数据库 进行连接生成EF实体 在做这步之前,首先确定你是否已经有现有数据库,当然在这提供我自己的数据库脚本.

Robot Framework 快速入门

Robot Framework 快速入门 目录 介绍 概述 安装 运行demo 介绍样例应用程序 测试用例 第一个测试用例 高级别测试用例 数据驱动测试用例 关键词keywords 内置关键词 库关键词 用户定义关键词 变量 定义变量 使用变量 组织测试用例 测试套件 启动和卸载 使用标签 创建测试库 介绍概述 Robot Framework 是一个关键词驱动的自动测试框架.测试用例位于HTML或者TSV(以tab分隔值)文件,使用在测试库中实现的关键词来在测试中运行程序.因为Robot Fra

Entity Framework快速入门--IQueryable与IEnumberable的区别

IEnumerable接口 公开枚举器,该枚举器支持在指定类型的集合上进行简单迭代.也就是说:实现了此接口的object,就可以直接使用foreach遍历此object: IQueryable 接口 它继承 IEnumerable 接口,而因为.net版本加入Linq和IQueryable后,使得IEnumerable不再那么单调,变得更加强大和丰富. 为了区别两个接口,我们通过一个实际的例子来解释一下. 根据上篇随笔的实例,编写如下代码: static void Main( string []

CodeIgniter框架——数据库类(配置+快速入门)

CodeIgniter用户指南——数据库类 数据库配置 入门:用法举例 连接数据库 查询 生成查询结果 查询辅助函数 Active Record 类 事务 表格元数据 字段元数据 自定义函数调用 查询缓存 数据库维护类 数据库工具类 1.数据库配置 CodeIgniter 有一个配置文件让你存放数据库连接值(username:用户名,password:密码,database name:数据库名,等等..). 该配置文件位于application/config/database.php. 你也可以

Entity Framework快速入门--IQueryable与IEnumberable的区别(转载)

IEnumerable接口 公开枚举器,该枚举器支持在指定类型的集合上进行简单迭代.也就是说:实现了此接口的object,就可以直接使用foreach遍历此object: IQueryable 接口 它继承 IEnumerable 接口,而因为.net版本加入Linq和IQueryable后,使得IEnumerable不再那么单调,变得更加强大和丰富. 为了区别两个接口,我们通过一个实际的例子来解释一下. 根据上篇随笔的实例,编写如下代码: static void Main(string[] a

Yii2框架RESTful API教程(一) - 快速入门

前不久做一个项目,是用Yii2框架写一套RESTful风格的API,就去查了下<Yii 2.0 权威指南 >,发现上面写得比较简略.所以就在这里写一篇教程贴,希望帮助刚接触Yii2框架RESTful的小伙伴快速入门. 一.目录结构 实现一个简单地RESTful API只需用到三个文件.目录如下: frontend ├─ config │ └ main.php ├─ controllers │ └ BookController.php └─ models └ Book.php 二.配置URL规则

【大话QT之十二】基于CTK Plugin Framework的插件版本动态升级

应用需求: 某些场景下我们可能面临这样的问题,在运行着的应用程序不能终止的情况下,升级某个功能(或添,或减,或修改).在不采用CTK Plugin Framework插件系统架构的情况下这将是很困难的,我们需要停止运行程序,然后在相关代码中作出修改,然后再重新编译,再重新启动我们的程序.而如果是基于CTK Plugin Framework插件系统架构构建的系统,则很容易的实现插件的动态升级.在[大话Qt之四]ctkPlugin插件系统实现项目插件式开发中,我对ctkPlugin做了简单介绍,在次

【大话QT之十二】基于CTK Plugin Framework的插件版本号动态升级

应用需求: 某些场景下我们可能面临这种问题,在执行着的应用程序不能终止的情况下,升级某个功能(或添,或减.或改动).在不採用CTK Plugin Framework插件系统架构的情况下这将是非常困难的,我们须要停止执行程序,然后在相关代码中作出改动,然后再又一次编译.再又一次启动我们的程序. 而假设是基于CTK Plugin Framework插件系统架构构建的系统,则非常easy的实现插件的动态升级.在[大话Qt之四]ctkPlugin插件系统实现项目插件式开发中,我对ctkPlugin做了简