今天在学习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像素。
这部分先讲到这里,下一部分将对之后的几个函数进行讲解。