Qt高级——Qt插件开发

Qt高级——Qt插件开发

一、Qt插件机制

1、Qt插件简介

插件是一种遵循一定规范的应用程序接口编写出来的程序,定位于开发实现应用软件平台不具备的功能的程序。

2、Qt插件API

Qt提供了两种API用于创建插件:一种是高阶API,用于扩展Qt本身的功能,如自定义数据库驱动,图像格式,文本编码,自定义样式等;一种是低阶API,用于扩展Qt应用程序。

3、通过插件扩展应用程序功能

A、定义一个接口集(只有纯虚函数的类),用来与插件交流。
B、用宏Q_DECLARE_INTERFACE()将该接口告诉Qt元对象系统。
C、应用程序中用QPluginLoader来加载插件。
D、用宏qobject_cast()来判断一个插件是否实现了接口。

4、创建插件

创建一个插件的步骤如下:
A、声明插件类,插件类继承自QObject和插件实现的接口。
B、用宏Q_INTERFACES()将插件接口告诉Qt元对象系统。
C、用宏Q_EXPORT_PLUGIN2()导出插件类。
D、用适当的.pro文件构建插件。
在加载插件前,?QCoreApplication对象必须被初始化。

二、插件开发实例

1、创建工程

创建工程,选择“Other Project”->“Subdirs Project”,填写工程名称为PluginApp,选择保存目录。

2、创建应用工程

在PluginApp工程上右键选择“New Subproject”菜单项,选择创建一个GUI应用,工程名称为MainWindow。

填写工程应用名称

填写主界面类的名称:

在MainWindow应用增加一个接口Echonterface.h。

#ifndef ECHOINTERFACE_H
#define ECHOINTERFACE_H

#include <QString>

//定义接口
class EchoInterface
{
public:
    virtual ~EchoInterface() {}
    virtual QString echo(const QString &message) = 0;
};

#define EchoInterface_iid "Examples.Plugin.EchoInterface"

QT_BEGIN_NAMESPACE
Q_DECLARE_INTERFACE(EchoInterface, EchoInterface_iid)
QT_END_NAMESPACE

#endif // ECHOINTERFACE_H

3、创建插件子工程

在PluginApp工程上右键选择“New Subproject”菜单项,选择创建一个空的Qt工程,名称为EchoPlugin。

EchoPlugin.pro工程文件内容如下:

TEMPLATE        = lib
CONFIG         += plugin
QT             += widgets
INCLUDEPATH    += ../MainWindow
TARGET          = $$qtLibraryTarget(echoplugin)
DESTDIR         = ../plugins

在插件子工程中添加一个插件类EchoPlugin,实现如下:
EchoPlugin.h文件:

#ifndef ECHOPLUGIN_H
#define ECHOPLUGIN_H

#include <QObject>
#include <QtPlugin>
#include "EchoInterface.h"

class EchoPlugin : public QObject, public EchoInterface
{
    Q_OBJECT
    Q_INTERFACES(EchoInterface)
public:
    explicit EchoPlugin(QObject *parent = 0);
    QString echo(const QString &message);
};

#endif // ECHOPLUGIN_H

EchoPlugin.cpp文件:

#include "EchoPlugin.h"

EchoPlugin::EchoPlugin(QObject *parent) :
    QObject(parent)
{
}

QString EchoPlugin::echo(const QString &message)
{
    return message;
}

Q_EXPORT_PLUGIN2(EchoPlugin, EchoPlugin);

4、应用的实现

实现MainWindow主界面
Widget.h文件:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "EchoInterface.h"

QT_BEGIN_NAMESPACE
class QString;
class QLineEdit;
class QLabel;
class QPushButton;
class QGridLayout;
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
private slots:
    void sendEcho();

private:
    void createGUI();
    //加载插件
    bool loadPlugin();

    EchoInterface *echoInterface;
    QLineEdit *lineEdit;
    QLabel *label;
    QPushButton *button;
    QGridLayout *layout;
};

#endif // WIDGET_H

Widget.cpp文件:

#include "Widget.h"
#include <QtGui>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    createGUI();
    setLayout(layout);
    setWindowTitle("Echo Plugin Example");

    if (!loadPlugin())
    {
        QMessageBox::information(this, "Error", "Could not load the plugin");
        lineEdit->setEnabled(false);
        button->setEnabled(false);
    }
}
void Widget::sendEcho()
{
    QString text = echoInterface->echo(lineEdit->text());
    label->setText(text);
}

void Widget::createGUI()
{
    lineEdit = new QLineEdit;
    label = new QLabel;
    label->setFrameStyle(QFrame::Box | QFrame::Plain);
    button = new QPushButton(tr("Send Message"));

    connect(lineEdit, SIGNAL(editingFinished()),
            this, SLOT(sendEcho()));
    connect(button, SIGNAL(clicked()),
            this, SLOT(sendEcho()));

    layout = new QGridLayout;
    layout->addWidget(new QLabel(tr("Message:")), 0, 0);
    layout->addWidget(lineEdit, 0, 1);
    layout->addWidget(new QLabel(tr("Answer:")), 1, 0);
    layout->addWidget(label, 1, 1);
    layout->addWidget(button, 2, 1, Qt::AlignRight);
    layout->setSizeConstraint(QLayout::SetFixedSize);
}

bool Widget::loadPlugin()
{
    bool ret = true;
    //获取当前应用程序所在路径
    QDir pluginsDir(qApp->applicationDirPath());
#if defined(Q_OS_WIN)
    if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
        pluginsDir.cdUp();
#elif defined(Q_OS_MAC)
    if (pluginsDir.dirName() == "MacOS")
    {
        pluginsDir.cdUp();
        pluginsDir.cdUp();
        pluginsDir.cdUp();
    }
#elif defined(Q_OS_LINUX)
    pluginsDir.cdUp();
#endif
    //切换到插件目录
    pluginsDir.cd("plugins");
    //遍历plugins目录下所有文件
    foreach (QString fileName, pluginsDir.entryList(QDir::Files))
    {
        QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));

        QObject *plugin = pluginLoader.instance();
        if (plugin)
        {
            //插件名称
            QString pluginName = plugin->metaObject()->className();
            //对插件初始化
            if(pluginName == "EchoPlugin")
            {
                echoInterface = qobject_cast<EchoInterface *>(plugin);
                if (echoInterface)
                    ret =  true;
                break;
            }
            else
            {
                ret = false;
            }
        }
    }
    return ret;
}

Widget::~Widget()
{

}

Main.cpp文件:

#include "Widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

程序运行结果如下:

查看构建目录,生成的插件文件存放在plugins目录下:

三、定位插件

Qt应用程序将会自动感知可用的插件,因为插件都被存储在标准的子目录当中。因此应用程序不需要任何查找或者加载插件的代码。
在开发过程中,插件的目录是QTDIR/plugins(QTDIR是Qt的安装目录),每个类型的插件放在相应类型的目录下面。如果想要应用程序使用插件,但不想用标准的插件存放路径,可以在应用程序的安装过程中指定要使用的插件的路径,可以使用QSettings,保存插件路径,在应用程序运行时读取配置文件。应用程序可以通过QCoreApplication::addLibraryPath()函数将指定的插件路径加载到应用程序中。
使插件可加载的一种方法是在应用程序所在目录创建一个子目录,用于存放插件。如果要发布和Qt一起发布的插件(存放在plugins目录)中的任何插件,必须拷贝plugins目录下的插件子目录到应用程序的根目录下。

四、静态插件

1、静态插件简介

将一个插件与一个应用程序一起使用的通常和最灵活的方法是将插件编译成一个动态库,动态库可以独立转移,并在运行时被检测和加载。
插件可以静态链接到应用程序。构建Qt的静态版本是包含Qt的预定义插件的唯一选项。使用静态插件使部署不易出错,但缺点是插件中没有的功能不能在应用程序的完全重编译和重发布的情况下添加。
Qt提供了如下静态插件:

2、静态插件使用

要静态链接静态插件,必须在应用程序中使用Q_IMPORT_PLUGIN宏,同时需要使用QTPLUGIN增加相应的插件到工程中。如:

#include <QtPlugin>

Q_IMPORT_PLUGIN(qjpeg)
Q_IMPORT_PLUGIN(qgif)

在.pro工程文件中,

QTPLUGIN     += qjpeg                 qgif

3、创建静态插件

使用如下步骤可以创建一个静态插件:
A、在.pro工程文件中增加CONFIG += static?
B、在应用程序中使用?Q_IMPORT_PLUGIN()宏导入静态插件
C、在应用程序.pro工程文件中使用?LIBS链接应用程序和静态插件。

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

时间: 2024-10-02 21:11:38

Qt高级——Qt插件开发的相关文章

Qt高级——Qt信号槽机制源码解析

Qt高级--Qt信号槽机制源码解析 基于Qt4.8.6版本 一.信号槽机制的原理 1.信号槽简介 信号槽是观察者模式的一种实现,特性如下:A.一个信号就是一个能够被观察的事件,或者至少是事件已经发生的一种通知:B.一个槽就是一个观察者,通常就是在被观察的对象发生改变的时候--也可以说是信号发出的时候--被调用的函数:C.信号与槽的连接,形成一种观察者-被观察者的关系:D.当事件或者状态发生改变的时候,信号就会被发出:同时,信号发出者有义务调用所有注册的对这个事件(信号)感兴趣的函数(槽).信号和

Qt高级——Qt日志信息处理

Qt高级--Qt日志信息处理 一.Qt日志功能简介 Qt有Debug.Warning.Critical.Fatal四种级别的调试信息.qDebug:调试信息qWarning:警告信息qCritical:严重错误qFatal:致命错误Qt4提供了qInstallMsgHandler(Qt5:qInstallMessageHandler)对qDebug.qWarning.qCritical.qFatal等函数输出信息的重定向处理.qInstallMsgHandler是一个回调函数,由qDebug.q

Qt高级——Qt数据可视化性能优化

Qt高级--Qt数据可视化性能优化 一.数据可视化简介 1.数据可视化简介 数据可视化即采用图形图表等对采集的数据进行展示,可以非常直观的查看传感器采集到的数据.本文将使用Qt的标准组件QTableWidget.标准模型.自定义模型分别实现对数据的表格展示. 2.系统环境 个人PC:ThinkPad T450操作系统:RHEL7.3 WorkStation内存容量:8G磁盘容量:SSD 100GCPU:Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz 二.标准界面

QT高级编程技巧(二)-- 编写多线程和并发应用

学习QT多线程编程之前,有必要先熟悉事件循环的概念.先看一个单线程界面程序的主函数代码: int main(int argc, char* argv[]) { QApplication app(argc, argv); // 构造主窗口对象并显示 MainWindow w; w.show(); // 进入事件循环 return app.exec(); } 在程序初始化完成后,主线程进入main()函数开始执行应用代码.一般地,我们在主线程上构建界面对象,然后进入事件循环以处理控件绘制.用户输入.

Qt高级——QMake快速入门

Qt高级--QMake快速入门 一.QMake简介 qmake是Trolltech公司创建的用来为不同的平台和编译器书写Makefile的工具.qmake是一个用来简化在不同平台间开发工程的构建过程的工具.qmake会自动生成MakeFile文件,可以用于任何软件项目中,无论是否由Qt编写.qmake会注意所有的编译器和平台的依赖性,开发者只需关注自己的代码.qmake作为Qt库和Qt所提供的工具的主要连编工具,可以自动的包含moc和uic的连编规则. 二.QMake入门教程 1.创建一个工程

Qt高级——QTestLib单元测试框架

Qt高级--QTestLib单元测试框架 一.QTestLib简介 1.QTestLib简介 QTestLib是Qt提供的一种针对基于Qt编写的程序或库的单元测试框架.QTestLib提供了单元测试框架的基本功能,并提供了针对GUI测试的扩展功能. 2.QTestLib特性 QTestLib是为了简化QT程序或库的单元测试工作而设计的.QTestLib特性如下:A.轻量级:QTestlib只包含6000行代码和60个导出符号B.自包含:对于非GUI测试,QTestlib只需要Qt核心库的几个符号

Qt高级——QMake用户指南

Qt高级--QMake用户指南 本文翻译自Qt 4.8官方文档. 一.QMake使用 QMake提供了一个用于管理应用程序.库.其它组件的构建过程的面向工程系统.QMake扩展了每个工程文件的信息,生成一个执行编译和链接过程的必须命令的MakeFile. 1.描述工程 工程文件.pro描述了工程信息.工程文件信息会被qmake用于生成包含构建过程中所需的所有命令的MakeFile.工程文件通常包含一系列头文件和源文件,通用配置信息以及音乐程序指定的细节,如应用程序的链接库.搜索路径.工程文件包含

Qt高级——D-Bus快速入门

Qt高级--D-Bus快速入门 一.D-Bus简介 1.D-Bus简介 D-Bus是Desktop Bus的缩写,是针对桌面环境优化的IPC(InterProcess Communication)机制,用于进程间的通信或进程与内核的通信.D-Bus是为Linux系统开发的进程间通信(IPC)和远程过程调用(RPC)机制,使用统一的通信协议来代替现有的各种IPC解决方案.D-Bus允许系统级进程(如:打印机和硬件驱动服务)和普通用户进程进行通信.D-Bus使用一个快速的二进制消息传递协议,D-Bu

Qt高级——QtCreator常用快捷键

Qt高级--QtCreator常用快捷键 F1? ?? ??? 查看帮助 F2? ?? ??? 跳转到函数定义 Shift+F2? ? 声明和定义之间切换 F3 查找下一个 F4? ?? ??? 头文件和源文件之间切换 Ctrl+1? ?? ?? ? 欢迎模式 Ctrl+2? ?? ??? 编辑模式 Ctrl+3? ?? ??? 调试模式 Ctrl+4? ?? ??? 项目设置模式 Ctrl+5? ?? ??? 帮助模式? ?? Ctrl+6? ?? ??? 输出模式 Alt+0? ?? ???