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
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可能是因为您的自定义样式没有正确应用到TabWidget上。请确保您已经正确地设置了TabWidget的样式,可以参考以下代码: ``` QTabWidget::tab-bar { alignment: center; } QTabBar::tab { height: 30px; width: 150px; background-color: #F0F0F0; border: 2px solid #CFCFCF; border-top-left-radius: 4px; border-top-right-radius: 4px; } QTabBar::tab:selected { background-color: #FFFFFF; border-bottom-color: #FFFFFF; } QTabBar::tab:!selected { margin-top: 2px; } ``` 请注意,这只是一个示例样式,您需要根据您的实际需求进行修改。如果您仍然无法解决问题,请提供更多细节,例如代码片段或截图,以便更好地帮助您解决问题。 ### 回答2: 在Qt的TabWidget中添加自定义样式时,有几个可能的原因导致样式不显示。 首先,可能是因为样式表的语法错误。请确保您的样式表中没有拼写错误、语法错误或其他错误。可以通过在样式表中添加最基本的样式来验证,然后逐步添加更多样式以确定是否出现错误。 其次,可能是因为样式表的优先级不正确。确保您的自定义样式表是在TabWidget中最后设置的,这样它的优先级会最高,确保样式能够应用到TabWidget上。 另外,还可能是因为样式被其他样式或属性覆盖了。请检查您的样式表中是否存在其他设置了相同属性的样式。如果有,可以使用!important关键字来覆盖。 最后,有可能是Qt的默认样式被重新设置或者覆盖了。在某些情况下,可能需要通过设置Qt的默认样式来确保自定义样式能够正确显示。可以通过设置setStyle()函数来重新设置Qt的样式,可以尝试不同的样式来查看是否能解决问题。 总之,要解决TabWidget中自定义样式不显示的问题,需要仔细检查样式表的语法、优先级和可能的覆盖情况。如果问题仍然存在,可能需要尝试不同的解决方案或者搜索相关的Qt文档和社区帖子以获取更多的帮助。 ### 回答3: 在Qt的TabWidget中添加自定义样式时,有几个常见的原因会导致样式不显示。 1. 样式表语法错误:在编写样式表时,可能会因为语法错误而导致样式无法显示。请确保样式表的写法正确,包括选择器、属性和值的书写方式。 2. 样式被其他样式覆盖:如果在TabWidget中同时应用了多个样式表,可能会导致样式冲突,使得自定义样式不生效。可以尝试通过调整样式表的加载顺序,或者使用更具体的选择器来解决样式冲突的问题。 3. 样式属性不适用于TabWidget:有些样式属性只适用于特定的控件,可能不适用于TabWidget。请查阅Qt官方文档,确保所使用的样式属性在TabWidget中是可用的。 如果以上几点排查后仍然无法解决问题,可以尝试以下几种方法: 1. 使用QTabBar设置样式:QTabWidget内部使用了QTabBar来管理选项卡,可以尝试通过设置QTabBar的样式来达到类似的效果。 2. 重新编译Qt源码:有些样式属性可能需要在Qt的源码中手动设置,如果遇到特殊的样式需求,可以尝试重新编译Qt源码,并在其中添加相应的样式。 3. 使用QSS代替样式表:有时候,使用Qt Style Sheet(QSS)比样式表更加方便和灵活。QSS是一种专门用于Qt应用程序的样式定义语言,可以通过给控件设置objectName,然后在QSS中使用选择器来定制样式。 综上所述,如果在Qt的TabWidget中添加的自定义样式不显示,首先要检查样式表语法是否正确,是否存在样式冲突,并确保所使用的样式属性适用于TabWidget。如果问题仍然存在,可以尝试使用QTabBar设置样式,重新编译Qt源码或者使用QSS代替样式表。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值