利用插件扩展Qt本身

简述

Qt 提供了两套用于创建插件的 API:

  • High-Level API:用于扩展 Qt 本身(例如:自定义数据库驱动、图像格式、文本编解码、自定义样式等)
  • Low-Level API:用于扩展 Qt 应用程序

例如,如果要编写自定义的 QStyle 子类,并且动态地加载 Qt 应用程序,则可以使用更高级别的 API。

由于较高级别的 API 构建在较低级别的 API 之上,所以某些问题对两者来说是共同的。

版权所有:一去丶二三里,转载请注明出处:http://blog.csdn.net/liang19890820

编写 Qt 扩展

要编写扩展 Qt 本身的插件,需要对适当的插件基类进行子类化,实现一些功能并添加一个宏。

下表总结了一些插件基类:

基类 目录 Qt 模块 大小写敏感性
QAccessibleBridgePlugin accessiblebridge Qt GUI 区分大小写
QImageIOPlugin imageformats Qt GUI 区分大小写
QPictureFormatPlugin (obsolete) pictureformats Qt GUI 区分大小写
QAudioSystemPlugin audio Qt Multimedia 区分大小写
QDeclarativeVideoBackendFactoryInterface video/declarativevideobackend Qt Multimedia 区分大小写
QGstBufferPoolPlugin video/bufferpool Qt Multimedia 区分大小写
QMediaPlaylistIOPlugin playlistformats Qt Multimedia 区分大小写
QMediaResourcePolicyPlugin resourcepolicy Qt Multimedia 区分大小写
QMediaServiceProviderPlugin mediaservice Qt Multimedia 区分大小写
QSGVideoNodeFactoryPlugin video/videonode Qt Multimedia 区分大小写
QBearerEnginePlugin bearer Qt Network 区分大小写
QPlatformInputContextPlugin platforminputcontexts Qt Platform Abstraction 区分大小写
QPlatformIntegrationPlugin platforms Qt Platform Abstraction 区分大小写
QPlatformThemePlugin platformthemes Qt Platform Abstraction 区分大小写
QGeoPositionInfoSourceFactory position Qt Positioning 区分大小写
QPlatformPrinterSupportPlugin printsupport Qt Print Support 区分大小写
QSGContextPlugin scenegraph Qt Quick 区分大小写
QScriptExtensionPlugin script Qt Script 区分大小写
QSensorGesturePluginInterface sensorgestures Qt Sensors 区分大小写
QSensorPluginInterface sensors Qt Sensors 区分大小写
QSqlDriverPlugin sqldrivers Qt SQL 区分大小写
QIconEnginePlugin iconengines Qt SVG 区分大小写
QAccessiblePlugin accessible Qt Widgets 区分大小写
QStylePlugin styles Qt Widgets 区分大小写

注意: 派生的插件默认存储在标准插件目录的子目录中,如果没有被存储在相应的目录中,Qt 将不会找到插件。

吐槽 Qt

你没看错,正在进行时 - 吐槽、吐槽、吐槽。。。

利用插件扩展Qt本身

Qt 中有很多示例,其中有一个样式插件 - Style Plugin,可以正常运行,但无法显示实际效果(说好的红色按钮呢?)一脸懵逼!

除此之外,项目还包含了一些冗余的代码,并且助手里有些描述也有问题(过时了)。例如:

keys() returns a list of style names that this plugin can create, while create() takes such a string and returns the QStyle corresponding to the key. Both functions are pure virtual functions reimplemented from QStylePlugin.

查看 QStylePlugin 文档:

...
class Q_WIDGETS_EXPORT QStylePlugin : public QObject
{
    Q_OBJECT
public:
    explicit QStylePlugin(QObject *parent = Q_NULLPTR);
    ~QStylePlugin();

    virtual QStyle *create(const QString &key) = 0;
};
...

What?纯虚函数 keys() 呢?。。。如果没记错的话,在 Qt 4.8 版本是有这个纯虚函数的,但是 5.x 已经去掉了。

所以,尽信书不如无书!即使是助手等一些官方教程也有过时的时候。。。遇到问题不要心存侥幸,抱着似是而非的心态,有疑惑一定要搞明白!

好吧,那就以此为例,顺便解决所有遇到的问题。

一个简单的样式插件

编写样式插件,涉及以下步骤:

  • 实现一个 QStyle,为 GUI 提供样式和外观。
  • 编写样式插件 - 子类化 QStylePlugin
    • 重新实现纯虚函数 create()
    • 使用 Q_PLUGIN_METADATA() 宏导出该类
  • 使用合适的 .pro 文件构建插件

在插件创建完成之后,使用以下方式加载:

  • 使用 QStyleFactory::create() 创建并返回一个 QStyle 对象
  • 使用 QApplication::setStyle() 设置程序的样式
  • 样式插件必须被放置在 styles 目录中,该目录与可执行程序处于同一位置。

下面,来实现一个带有蓝色文本的按钮样式插件,效果如下:

利用插件扩展Qt本身

样式插件

工程文件 - .pro

由于我们正在构建的是一个共享库,而不是可执行文件。所以在 .pro 中,需要将 TEMPLATE 设置为 lib,同时还必须将 CONFIG 设置为 plugin

plugin.pro 内容如下:

QT += widgets
TEMPLATE = lib
CONFIG += plugin
TARGET = stylePlugin

HEADERS += \
    simple_style.h \
    style_plugin.h

OTHER_FILES += simple_style.json

win32 {
    CONFIG(debug, release|debug):DESTDIR = ../debug/styles/
    CONFIG(release, release|debug):DESTDIR = ../release/styles/
} else {
    DESTDIR = ../styles/
}

注意: 根据 QStylePlugin 的要求,插件需要被存储在 styles 文件夹中,因为这是 Qt 搜索样式插件的路径。

自定义样式 - QStyle

QProxyStyle 是一个很方便的类,包装了一个 QStyle,用于动态覆盖特定的样式行为。所以,自定义的样式可以用它来实现。

simple_style.h 内容如下:

#ifndef SIMPLE_STYLE_H
#define SIMPLE_STYLE_H

#include <QProxyStyle>

class SimpleStyle : public QProxyStyle
{
    Q_OBJECT

public:
    SimpleStyle() {}

    void polish(QPalette &palette) override
    {
        palette.setBrush(QPalette::ButtonText, Qt::blue);
    }
};

#endif // SIMPLE_STYLE_H

插件类 - StylePlugin

StylePlugin 继承了 QStylePlugin,是插件类。

style_plugin.h 内容如下:

#ifndef STYLE_PLUGIN_H
#define STYLE_PLUGIN_H

#include <QStylePlugin>
#include "simple_style.h"

class StylePlugin : public QStylePlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "simple_style.json")

public:
    StylePlugin() {}

    QStyle *create(const QString &key) override
    {
        if (key.toLower() == "simplestyle")
            return new SimpleStyle();

        return Q_NULLPTR;
    }
};

#endif // STYLE_PLUGIN_H

create() 用于创建并返回给定样式 keyQStyle 对象,如果插件无法创建样式,则应该返回 Q_NULLPTR

注意: 样式 key 通常是所需样式的类名,这些 key 不区分大小写。区分大小写因插件而异,所以在实现新插件时需要检查。

插件的元数据 - Json 文件

Json 元数据文件 simple_style.json 需要包含插件支持的样式名称:

{
    "Keys": [ "simplestyle" ]
}

注意: 这个文件非常关键,在插件生成之后,可以通过 QStyleFactory::keys() 来检测有效的 keys。也就是说,Json 文件中设置的值会由这个静态函数返回。

加载插件

一切准备就绪,来实现一个简单的应用程序,以动态加载插件。

工程文件 - .pro

由于这里需要的是一个可执行程序,所以需要将 TARGET 设置为 app。

app.pro 内容如下:

QT += widgets
SOURCES += main.cpp
TARGET = app

win32 {
    debug:DESTDIR = ../debug/
    release:DESTDIR = ../release/
} else {
    DESTDIR = ../
}

main() 函数

QApplication 对象初始化完成后,使用 QStyleFactory::keys() 来检测所有有效的 keys,并使用 create() (它是所有样式插件的包装器)来使用自定义的样式插件。

main.cpp 内容如下:

#include <QApplication>
#include <QPushButton>
#include <QStyleFactory>
#include <qDebug>

int main(int argv, char *args[])
{
    QApplication app(argv, args);

    // 设置样式
    qDebug() << QStyleFactory::keys();
    QApplication::setStyle(QStyleFactory::create("simplestyle"));

    QPushButton button("Blue Button");
    button.resize(300, 100);
    button.show();

    return app.exec();
}

有效的 keys:

(“simplestyle”, “Windows”, “WindowsXP”, “WindowsVista”, “Fusion”)

可以看出,simplestyle 已经被包含进去了!

源码地址

GitHub 源码地址:StylePlugin

上一篇:当阿里面试官问我:Java创建线程有几种方式?我就知道问题没那么简单


下一篇:vm虚拟机进入boot manager解决