利用插件扩展Qt本身

简述

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

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

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

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

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

编写 Qt 扩展

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

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

基类目录Qt 模块大小写敏感性
QAccessibleBridgePluginaccessiblebridgeQt GUI区分大小写
QImageIOPluginimageformatsQt GUI区分大小写
QPictureFormatPlugin (obsolete)pictureformatsQt GUI区分大小写
QAudioSystemPluginaudioQt Multimedia区分大小写
QDeclarativeVideoBackendFactoryInterfacevideo/declarativevideobackendQt Multimedia区分大小写
QGstBufferPoolPluginvideo/bufferpoolQt Multimedia区分大小写
QMediaPlaylistIOPluginplaylistformatsQt Multimedia区分大小写
QMediaResourcePolicyPluginresourcepolicyQt Multimedia区分大小写
QMediaServiceProviderPluginmediaserviceQt Multimedia区分大小写
QSGVideoNodeFactoryPluginvideo/videonodeQt Multimedia区分大小写
QBearerEnginePluginbearerQt Network区分大小写
QPlatformInputContextPluginplatforminputcontextsQt Platform Abstraction区分大小写
QPlatformIntegrationPluginplatformsQt Platform Abstraction区分大小写
QPlatformThemePluginplatformthemesQt Platform Abstraction区分大小写
QGeoPositionInfoSourceFactorypositionQt Positioning区分大小写
QPlatformPrinterSupportPluginprintsupportQt Print Support区分大小写
QSGContextPluginscenegraphQt Quick区分大小写
QScriptExtensionPluginscriptQt Script区分大小写
QSensorGesturePluginInterfacesensorgesturesQt Sensors区分大小写
QSensorPluginInterfacesensorsQt Sensors区分大小写
QSqlDriverPluginsqldriversQt SQL区分大小写
QIconEnginePluginiconenginesQt SVG区分大小写
QAccessiblePluginaccessibleQt Widgets区分大小写
QStylePluginstylesQt Widgets区分大小写

注意: 派生的插件默认存储在标准插件目录的子目录中,如果没有被存储在相应的目录中,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 目录中,该目录与可执行程序处于同一位置。

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

这里写图片描述

样式插件

工程文件 - .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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值