Qt 插件编程实践

缘由

最近在用Qt做项目,在网上找插件编写的资料,没有完整的代码,要下载的资源都被传到需要积分的网站上了,感觉很不爽。因此把插件示例项目编写完整,并在github上开了一个qtDemo项目,写了这篇文章。
作为一个拖砖项目,望大家在学习同时,不要忘记了分享的精神。这个项目我会把学习Qt的代码不断更新上来,若有同道者,请pull request给我,本项目收集Qt示例程序,谢谢!

技术选择

我的项目最低支持msvc 10.0 x86,g++ 4.8.5,Qt5.5.1,因为需要跨平台,项目管理使用cmake(最低支持3.10),现在同时支持windows和linux,IDE我使用qcreator,在windows下你也可以选择visual studio 2019。

Cmake介绍

使用cmake主要是因为它的跨平台性好,可以脱离操作系统与IDE的束缚,而且已经被广泛支持了。
设置cmake所需最低版本号

cmake_minimum_required(VERSION 3.10)

项目及版本号,项目名称可以通过${PROJECT_NAME}取得

project(plg1 VERSION 0.1.0)

将当前目录加入文件包含搜索目录,若不设置,vs2019与qcreator的当前目录会不兼容。

set(CMAKE_INCLUDE_CURRENT_DIR ON)

打开全局moc与全局uic

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)

查找系统中已安装的Qt版本,需要的库。
最好每一个库都要写,Qt也会根据依赖关系自动添加

find_package(Qt5 REQUIRED Widgets)
find_package(Qt5Widgets)
find_package(Qt5Core)
find_package(Qt5Gui)

需要建立名称为QTDIR的系统环境变量,指定安装的Qt目录。

收集我的们源文件,这有很多方法,大家可以去了解并使用自己喜欢的方式。

FILE(GLOB SRC_FILES "*.cpp" "*.h" "*.ui")

创建工程文件

add_executable(${PROJECT_NAME} ${SRC_FILES})               #可执行文件创建方式
add_library(${PROJECT_NAME} SHARED ${SRC_FILES})      #动态链接库创建方式

添加子项目,也就是我们的插件

add_subdirectory(sub1)
add_subdirectory(sub2)

添加Qt5依赖项

target_link_libraries(${PROJECT_NAME} Qt5::Widgets Qt5::Core Qt5::Gui)

Qt5插件

项目结构

本示例项目包括三个工程,一个主工程,两插件工程。cmake的项目是以目录为基础的,每个工程的目录下会有一个cmakelists.txt工程文件。

插件基类

主工程中的plugindemoplugin.h是所有插件的基类,我们的每个插件都继承自这个类,它做了插件基础声明及我们的插件能做的行为。本示例每一个插件会给主程序返回一个widget作为centerWidget显示,并提供一个name和information的查询接口,提供插件必要的信息。

class QtPluginDemoInterface
{
public:
    virtual ~QtPluginDemoInterface() {}
    virtual QString name() = 0;
    virtual QString information() = 0;      
    virtual QWidget *centerWidget() = 0;  //返回一个Widget设置到centerwidget中进行显示
};
//s声明接口
#define PluginDemoInterface_iid "com.Interface.MainInterface"
Q_DECLARE_INTERFACE(QtPluginDemoInterface, PluginDemoInterface_iid)

插件定义

我们这只对sub1进行一下说明,sub2是类似的,请自行阅读代码。
子项目的工程文件(cmakelists.txt)与主项目的主要的差别是一个是创建可执行文件,一个是创建动态链接库。
头文件plugindemo.h :

#include "../plugindemoplugin.h"

class pluginDemo : public QObject, QtPluginDemoInterface
{
    Q_OBJECT                //Qt类的标识宏,初学Qt的小伙伴要注意,这行是Qt类必须的
    Q_INTERFACES(QtPluginDemoInterface)             //这两行声明是插件要求的
    Q_PLUGIN_METADATA(IID PluginDemoInterface_iid)

public:
    pluginDemo(){};
    ...     //方法声明,省略
};

源文件plugindemo.cpp :

QWidget *pluginDemo::centerWidget()
{
    auto btn = new QPushButton("One");  //我们返回一个按钮,作为简单的widget示例
    return  btn;
}

... //其它的省略了,只是固定信息返回

主程序中加载插件

mainwindow.h定义:

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    int MainWindow::loadPlugins();
    void MainWindow::populateMenus(QObject * pluginInterface,QtPluginDemoInterface*i );
    void MainWindow::slt_WidgetActionTriggered();

private:
    Ui::MainWindow *ui;
};

通过loadPlugins函数加载插件:

int MainWindow::loadPlugins()
{
    QDir pluginsDir = QDir(QCoreApplication::applicationDirPath());  //这里要注意路径需要配置好,把子工程的输出目录配置到主工程
    if(!pluginsDir.cd("plugins")) return -1;                                          //下的plugins目录中
    foreach (QString fileName, pluginsDir.entryList(QDir::Files))
    {
        QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
        QObject *plugin = pluginLoader.instance();
        if(plugin)
        {
            auto centerInterface = qobject_cast<QtPluginDemoInterface*>(plugin);
            if(centerInterface)
            {
                populateMenus(plugin,centerInterface);  //将插件作为菜单中的一项
            }
        }
    }
    return count;
}

生成菜单函数:

void MainWindow::populateMenus(QObject * pluginInterface,QtPluginDemoInterface*i )
{
    static auto menu = menuBar()->addMenu("widgets");  //建立一个菜单项
    auto act  = new QAction(i->name(),pluginInterface); //建立action,取得的插件对象被绑定在其上
    connect(act,&QAction::triggered,this,&MainWindow::slt_WidgetActionTriggered);  //事件链接
    menu->addAction(act);
}

菜单点击事件响应:

void MainWindow::slt_WidgetActionTriggered()
{
    QtPluginDemoInterface * plg = qobject_cast<QtPluginDemoInterface*>(sender()->parent()); //取得插件对象
    auto centerWidget = plg->centerWidget();        //取得插件中返回的widget
    //我们返回的widget其实是QPushButton,用其配置信息为其设置显示内容
    (qobject_cast<QPushButton*>(centerWidget))->setText(plg->information());    
    setCentralWidget(centerWidget);
}

项目地址

https://github.com/zhoutk/qtDemo

命令行编译

git clone https://github.com/zhoutk/qtDemo
cd qtDemo/plugin & mkdir build & cd build
cmake ..
cmake --build .      

编译时注意:cmake默认为x86架构,需要与你安装的Qt版本对应;编译好了,运行前,请注意目录结构是否正确。

小结

抛砖引玉,不吝赐教,谢谢阅读!

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 界面 主窗口界面设计 标题栏:直接设Window-Title属性;Window-icon属性可加图标。底部状态栏:...
    码园老农阅读 3,727评论 1 13
  • Qt/QML 编码规范 一. 概述 良好的编码规范可以大幅提高程序的可读和可理解性,最终目标是实现更友好的可维护性...
    赵者也阅读 1,942评论 0 5
  • Qt/QML 插件系统 本文将简要介绍一下 Qt 和 QML 的插件系统,并用几个简单的示例介绍 QML 的几种插...
    赵者也阅读 6,481评论 0 4
  • 前言 在最初的接触Qt时,心中也是产生疑惑Qt程序到底使用什么IDE更舒服,因为我更喜欢VS环境,所以VS编程更多...
    YBshone阅读 1,793评论 0 7
  • VS+Qt配置 VS+QT混合编程后打包的项目可以移植到2017程序中,此处的版本不影响使用。 VS+Qt配置 V...
    Grace持盈阅读 7,809评论 0 0