Qt学习官方示例一(自定义Style风格)

  今天在学习Qt示例affine时,发现了一种很有趣的定义style风格的方法

XFormWidget xformWidget(0);
    QStyle *arthurStyle = new ArthurStyle();
    xformWidget.setStyle(arthurStyle);

    QList<QWidget *> widgets = xformWidget.findChildren<QWidget *>();
    foreach (QWidget *w, widgets) {
        w->setStyle(arthurStyle);
        w->setAttribute(Qt::WA_AcceptTouchEvents);
    }

在这段代码中,首先将主页面渲染成arthurStyle风格,然后再寻找主页面的子widget,将其都渲染成arthurStyle风格。

于是我们进入这个ArthurStyle的头文件

#ifndef ARTHURSTYLE_H
#define ARTHURSTYLE_H

#include <QCommonStyle>

QT_USE_NAMESPACE

class ArthurStyle : public QCommonStyle
{
public:
    ArthurStyle();

    void drawHoverRect(QPainter *painter, const QRect &rect) const;

    void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
                       QPainter *painter, const QWidget *widget = 0) const override;
    void drawControl(ControlElement element, const QStyleOption *option,
                     QPainter *painter, const QWidget *widget) const override;
    void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
                            QPainter *painter, const QWidget *widget) const override;
    QSize sizeFromContents(ContentsType type, const QStyleOption *option,
                           const QSize &size, const QWidget *widget) const override;

    QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const override;
    QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
                         SubControl sc, const QWidget *widget) const override;

    int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const override;

    void polish(QPalette &palette) override;
    void polish(QWidget *widget) override;
    void unpolish(QWidget *widget) override;
};

#endif

可以看到它继承于QCommonStyle,并将很多函数进行了重写。首先我们要知道这段代码是如何渲染的。

下面是QCommonStyle的继承关系图

控件的绘画流程

知道这些以后,我们将开始对源码进行分析

ArthurStyle::ArthurStyle()
    : QCommonStyle()
{
    Q_INIT_RESOURCE(shared);
}

首先是构造函数,代码段 Q_INIT_RESOURCE(shared); 是用于初始化 Qt 资源文件的宏。

在 Qt 中,资源文件(.qrc 文件)是一种用于存储应用程序所需资源(如图像、样式表、字体等)的特殊文件。这些资源文件需要在应用程序启动时进行加载和注册,以便在运行时可以方便地访问这些资源。

Q_INIT_RESOURCE(shared); 宏的作用是加载名为 "shared" 的资源文件并注册其中定义的资源。具体而言,它会加载名为 "shared.qrc" 的资源文件,并将其中定义的资源与应用程序关联起来。

QPixmap cached(const QString &img)
{
    //
    if (QPixmap *p = QPixmapCache::find(img))
        return *p;

    QPixmap pm;
    pm = QPixmap::fromImage(QImage(img), Qt::OrderedDither | Qt::OrderedAlphaDither);
    if (pm.isNull())
        return QPixmap();

    QPixmapCache::insert(img, pm);
    return pm;
}

 在这段代码中首先判断要获取的图像是否存在于PixmapCache中,如果存在则返回该pixmap的指针,否则将image图片转换为pixmap,如果pixmap不为空,则将其插入缓存PixmapCache当中,并返回。则将是后面经常看到的函数,这里进行讲解。

根据上面的流程图,我们来看当进行渲染时,分别进行执行的函数,我们将按照流程图的顺序进行分析

QRect ArthurStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
                                  SubControl subControl, const QWidget *widget) const
{
    QRect rect;

    switch (control) {
    default:
        rect = QCommonStyle::subControlRect(control, option, subControl, widget);
        break;
    case CC_GroupBox:
        if (const QStyleOptionGroupBox *group
                = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
            switch (subControl) {
            default:
                rect = QCommonStyle::subControlRect(control, option, subControl, widget);
                break;
            case SC_GroupBoxContents:
                rect = QCommonStyle::subControlRect(control, option, subControl, widget);
                rect.adjust(0, -8, 0, 0);
                break;
            case SC_GroupBoxFrame:
                rect = group->rect;
                break;
            case SC_GroupBoxLabel:
                QPixmap titleLeft = cached(":res/images/title_cap_left.png");
                QPixmap titleRight = cached(":res/images/title_cap_right.png");
                QPixmap titleStretch = cached(":res/images/title_stretch.png");
                int txt_width = group->fontMetrics.width(group->text) + 20;
                rect = QRect(group->rect.center().x() - txt_width/2 + titleLeft.width(), 0,
                             txt_width - titleLeft.width() - titleRight.width(),
                             titleStretch.height());
                break;
            }
        }
        break;
    }

    if (control == CC_Slider && subControl == SC_SliderHandle) {
        rect.setWidth(13);
        rect.setHeight(27);
    } else if (control == CC_Slider && subControl == SC_SliderGroove) {
        rect.setHeight(9);
        rect.moveTop(27/2 - 9/2);
    }
    return rect;
}

 首先我们对传入参数进行分析

 QStyle::ComplexControl 是 Qt 框架中的一个枚举类型,用于表示复杂控件(Complex Control)的标识符。复杂控件是指由多个简单控件组成的复合控件,通常具有更复杂的外观和交互行为。

ComplexControl 枚举类型定义了一组整数值,每个值代表一个特定的复杂控件。这些值可以用于指定要绘制或操作的特定复杂控件,以及与之相关的样式元素。

这些枚举值可以使用qt助手进行查阅

QStyleOptionComplex 是 Qt 框架中的一个类,用于传递复杂控件(Complex Control)的样式选项给样式引擎。

QStyleOptionComplex 类是 QStyleOption 类的子类,它包含了用于描述复杂控件的样式属性的数据成员。

 

QStyle::SubControl 是 Qt 框架中的一个枚举类型,用于表示子控件(Sub Control)的标识符。子控件是复杂控件(Complex Control)中的可视化或交互元素,用于实现复杂控件的不同部分或状态。

SubControl 枚举类型定义了一组整数值,每个值代表一个特定的子控件。这些值用于指定要绘制、布局或操作的特定子控件,以及与之相关的样式元素。

通过对参数的分析,我们大概知道这个函数的作用,即对复杂控件及其子控件进行样式设计。

在这段代码中首先定义了一个矩形,首先通过枚举值来获取要重写复杂控件,当获取到复杂控件以后再对其子控件进行样式操作。

这里我们对Qt给出的样例进行讲解

//当为groupBox时
    case CC_GroupBox:
        //如果能将获取到的样式转换为 QStyleOptionGroupBox *时
        if (const QStyleOptionGroupBox *group
                = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
            //则对子控件进行样式修改
            switch (subControl) {
            default:
                //对没有修改的子控件使用QCommonStyle的样式
                rect = QCommonStyle::subControlRect(control, option, subControl, widget);
                break;
            //当为groupbox的内容区域时
            case SC_GroupBoxContents:
                //首先获取该子控件的矩形大小
                rect = QCommonStyle::subControlRect(control, option, subControl, widget);
                //在这里将矩形的上边框向下缩进了8个像素
                rect.adjust(0, -8, 0, 0);
                break;
             //当为groupbox的框架区域时
            case SC_GroupBoxFrame:
             //返回原始矩形大小
                rect = group->rect;
                break;
             //当为groupbox的Label区域时
            case SC_GroupBoxLabel:
                //首先将图片加载读取并加载至缓存当中
                QPixmap titleLeft = cached(":res/images/title_cap_left.png");
                QPixmap titleRight = cached(":res/images/title_cap_right.png");
                QPixmap titleStretch = cached(":res/images/title_stretch.png");
                //设置文本区域的宽度为从父组件中读group->text长度,再加上20像素
                int txt_width = group->fontMetrics.width(group->text) + 20;
                rect = QRect(group->rect.center().x() - txt_width/2 + titleLeft.width(), 0,
                             txt_width - titleLeft.width() - titleRight.width(),
                             titleStretch.height());
                //group->rect.center().x() - txt_width/2 + titleLeft.width()为计算左上角x值
                //0为左上角的y值
                //txt_width - titleLeft.width() - titleRight.width()为计算文本的宽度
                //titleStretch.height();为文本的高度
                break;
            }
        }
        break;
    }

  //如果为滑块的手柄
    if (control == CC_Slider && subControl == SC_SliderHandle) {
        rect.setWidth(13);
        rect.setHeight(27);
    //如果为滑块的滑槽
    } else if (control == CC_Slider && subControl == SC_SliderGroove) {
        rect.setHeight(9);
        rect.moveTop(27/2 - 9/2);
    }
    return rect;

通过代码可以看出,就是对复杂控件的大小进行控制

QRect ArthurStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
{
    QRect r;
    switch(element) {
    case SE_RadioButtonClickRect:
        r = widget->rect();
        break;
    case SE_RadioButtonContents:
        r = widget->rect().adjusted(20, 0, 0, 0);
        break;
    default:
        r = QCommonStyle::subElementRect(element, option, widget);
        break;
    }

    if (qobject_cast<const QRadioButton*>(widget))
        r = r.adjusted(5, 0, -5, 0);

    return r;
}

首先我们先对传入的参数进行分析

SubElement element

 我们使用qt助手进行查阅,可以发现这是一个枚举值表达一个widget子区域,style接口使用这些区域去绘画一个不同部分的widget(英语不是很好),也就是说,你可以通过这个枚举值进行判断你传入widget的类型,然后再,通过该widget来获取矩形的大小,进而对该矩形大小进行自定义调整

 QStyleOption *option为样式形式

QWidget *widget为传入的widget

这里出现一个函数adjusted(20, 0, 0, 0);这个函数的意思是将矩形分别向左,上,右,下增加多少像素,在这个函数中,将矩形向左增加了20个像素。

下面是对代码的分析

QRect ArthurStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
{
    //首先定义一个矩形
    QRect r;
    //通过传入的枚举值进行判断传入状态widget
    switch(element) {
    //当为radiobutton点击区域范围时
    case SE_RadioButtonClickRect:
        //直接获取矩形面积
        r = widget->rect();
        break;
    //当为radiobutton内容区域时
    case SE_RadioButtonContents:
        //首先将获取的矩形向左再增加20个像素
        r = widget->rect().adjusted(20, 0, 0, 0);
        break;
    default:
        //如果不存在时,则使用缺省值
        r = QCommonStyle::subElementRect(element, option, widget);
        break;
    }
    //再对获取到的矩形进行像素调整左边界增加5个像素,右边界减少5个像素
    if (qobject_cast<const QRadioButton*>(widget))
        r = r.adjusted(5, 0, -5, 0);
    //返回渲染以后新矩形的面积
    return r;
}

分析完这个函数以后我们对下一个函数进行分析

QSize ArthurStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
                                    const QSize &size, const QWidget *widget) const
{
    QSize newSize = QCommonStyle::sizeFromContents(type, option, size, widget);


    switch (type) {
    case CT_RadioButton:
        newSize += QSize(20, 0);
        break;

    case CT_PushButton:
        newSize.setHeight(26);
        break;

    case CT_Slider:
        newSize.setHeight(27);
        break;

    default:
        break;
    }

    return newSize;
}

首先还是对参数进行分析 

ContentsType type,通过qt助手进行查阅

 这是一个枚举值用于描述这个可获取内容的类型,他们被使用去计算不同类型widget内容大小

在这段函数中,首先获取传入widget的大小,将其赋给QSize newSize,然后通过不同的枚举类型判断传入的是哪一个widget,我们可以知道之前对radioBtn内容区域向左增加了20个像素,因此这里,对于radioBtn的大小同样增加了20个像素,对于按钮和滑动条,则是直接设置了高度,当设计完毕以后将newSize进行返回。

下面我们对下一个函数进行分析

int ArthurStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, const QWidget *widget) const
{
    if (pm == PM_SliderLength)
        return 13;
    return QCommonStyle::pixelMetric(pm, opt, widget);
}

PixelMetric是Qt框架中的一个枚举类型,用于表示各种不同的度量指标(Metric)的标识符。这些度量指标描述了小部件和样式之间的尺寸和间距等度量值。

因此这个函数的作用就是对小组件细节大小进行进一步的控制,在这个函数中,首先判断枚举值是否为滑动条的长度,如果是,则返回13像素,那么之后的滑动条的长度都被设为了13像素。

这部分先讲到这里,下一部分将对之后的几个函数进行讲解。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是用C++实现的代码: ```cpp #include <QCommonStyle> #include <QPainter> #include <QStyleOption> #include <QStyleOptionButton> #include <QStyleOptionButtonV2> #include <QPixmap> class CustomButtonStyle : public QCommonStyle { public: void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override { if (element == CE_PushButton) { const auto buttonOption = qstyleoption_cast<const QStyleOptionButtonV2*>(option); if (buttonOption) { if (buttonOption->state & State_Sunken) { // 按下状态 painter->drawPixmap(option->rect, QPixmap("button_pressed.png")); } else if (buttonOption->state & State_MouseOver) { // 悬浮状态 painter->drawPixmap(option->rect, QPixmap("button_hover.png")); } else { // 普通状态 painter->drawPixmap(option->rect, QPixmap("button_normal.png")); } } } else if (element == CE_CheckBox) { const auto checkOption = qstyleoption_cast<const QStyleOptionButton*>(option); if (checkOption) { // check状态 if (checkOption->state & State_On) { painter->drawPixmap(option->rect, QPixmap("check_on.png")); } else { painter->drawPixmap(option->rect, QPixmap("check_off.png")); } } } else { QCommonStyle::drawControl(element, option, painter, widget); } } }; ``` 在上面的代码中,我们同样是继承了QCommonStyle类,并重写了其drawControl()方法。与Python的实现类似,我们也是通过option->state属性来判断按钮的状态,并根据状态绘制不同的图片。最后,我们可以将自定义的样式应用到QPushbutton上: ```cpp auto button = new QPushButton("Button"); button->setStyle(new CustomButtonStyle()); ``` 这样,我们就可以得到一个自定义样式的QPushbutton了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值