QStyle(2):子类化QStyle基础

QStyle(2):子类化QStyle基础

本文为原创文章,转载请注明出处,或注明转载自“黄邦勇帅(原名:黄勇)

本文出自本人原创著作《Qt5.10 GUI完全参考手册》网盘地址:
https://pan.baidu.com/s/1iqagt4SEC8PUYx6t3ku39Q
《C++语法详解》网盘地址:https://pan.baidu.com/s/1dIxLMN5b91zpJN2sZv1MNg

若对C++语法不熟悉,建议参阅本人所著《C++语法详解》一书,电子工业出版社出版,该书语法示例短小精悍,对查阅C++知识点相当方便,并对语法原理进行了透彻、深入详细的讲解,可确保读者彻底弄懂C++的原理,彻底解惑C++,使其知其然更知其所以然。此书是一本全面了解C++不可多得的案头必备图书。

把绘制自定义部件外观的步骤大致分为三大板块,如下:
①、样式元素:即指定需要绘制的图形元素(比如焦点框、按钮,工具栏等)。样式元素使用QStyle类中的一系列枚举(共有11个枚举)进行描述。
②、样式选项:包含了需要绘制的图形元素的所有信息,比如包含了图形元素的文本、调色板等,样式选项使用QStyleOption及其子类进行描述。
③、样式绘制函数:即绘制图形元素的函数,这些函数是QStyle类的成员函数,比如drawControl()等(在前文已见过)

13.4.1 样式元素

样式元素是指需要绘制的图形元素,一个部件由多个样式元素组成,比如,当样式接收到绘制按钮的请求时,会绘制标签(文本和图标)、按钮斜面(Bevel)和焦点框等,这些都是样式元素,图13-6为一个按钮样式元素的组成概念图。
在这里插入图片描述

共有3种样式元素,如下
①、原始元素:由QStyle::PrimitiveElement枚举(见表13-5)描述,使用前缀“PE_”标识,原始元素通常由部件使用,包括框架、按钮斜面、旋转框、滚动条、组合框的箭头等,原始元素不能单独存在,是其他界面元素(如复杂控件和控件元素)的组成部分,原始元素不参与用户的交互,只是被动的绘制和显示。
②、控件元素:由QStyle::ControlElement枚举(见表13-3)描述,使用前缀“CE_”标识。控件元素执行某种操作或向用户显示一些信息,控件元素会与用户交互,可以是单独的部件(比如QPushButton),也可以是其他部件的一部分(比如QScrollBar的滑块)。控件元素还可以由多个子元素组成,子元素仅用于计算边界区域,不能被绘制,然后便可在计算出的边界区域中绘制子元素,子元素使用QStyle::SubElement枚举描述(使用前缀SE_标识,见表13-7)。
③、复杂控件元素:由QStyle::ComplexControl枚举(见表13-4)描述,使用前缀“CC_”标识。复杂控件元素包含子控件,比如QSpinBox、QScrollBar、QToolButton等。复杂控件元素的子控件由QStyle::SubControl枚举定义(使用前缀SC_标识,见表13-6)。
④、因为部件通常都是由矩形组成的,因此各种控件元素其实描述的就是一个矩形区域。
表13-3~表13-8列出了描述各种样式元素的枚举及元素的状态标志,各种样式元素之间的关系,会在后文讲解,此处暂不讲解

注:以下表仅列出部分内容,完整的内容可参阅Qt帮助文档。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

13.4.2 样式绘制函数

为讲解需要,此处先列出需要使用到的QStyle类中的部分函数,其余函数见后文(以下的表请查阅帮助文档)

1、virtual void QStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget = Q_NULLPTR) const = 0;	//纯虚函数
	该函数用于绘制复杂控件元素。表示使用painter,样式选项option绘制控件control。
	widget参数是可选的,可用作绘制控件的辅助工具。
	参数option是指向QStyleOptionComplex对象的指针,可使用qstyleoption_cast()函数把该对象转换为正确的子类型。
	option的rect成员变量必须位于逻辑坐标中,重新实现此函数,在调用QStyle::drawPrimitive()或QStyle::drawControl()函数之前,应使用QStyle::visualRect()把逻辑坐标转换为屏幕坐标。
	表13-9为复杂控件元素及其关联的样式选项子类,以及使用的相应的状态标志。
2、virtual void QStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = Q_NULLPTR) const = 0;		//纯虚函数
	该函数用于绘制控件元素。表示使用painter,样式选项option绘制元素element。
	widget参数是可选的,可用作绘制控件的辅助工具。
	参数option是指向QStyleOption对象的指针,可使用qstyleoption_cast()函数把该对象转换为正确的子类型。
	表13-10为控件元素及其关联的样式选项子类,以及使用的相应的状态标志。
3、virtual void QStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter,const QWidget *widget = Q_NULLPTR) const = 0;	//纯虚函数
	该函数用于绘制原始元素。表示使用painter,样式选项option绘制元素element。
	widget参数是可选的,可用作绘制的辅助工具。
	表13-11为原始元素及其关联的样式选项子类,以及使用的相应的状态标志。
4、virtual void QStyle::drawItemPixmap(QPainter *painter, const QRect &rectangle, int alignment, const QPixmap &pixmap) const;	//虚函数
	表示使用painter根据对齐方式alignment,在矩形rectangle中绘制像素图pixmap
5、virtual void QStyle::drawItemText(QPainter *painter, const QRect &rectangle, int alignment, const QPalette &palette, bool enabled, const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const;	//虚函数
	表示使用painter,根据对齐方式alignment,调色板palette,在矩形rectangle中绘制文本text。若指定了颜色角色textRole,则使用该颜色角色的调色板颜色绘制文本,参数enabled用于指示是否启用该项。重新实现此函数时,enabled参数应影响项目的绘制方式。
6、virtual void QStyle::polish(QWidget *widget);					//虚函数
virtual void QStyle::polish(QApplication *application);			//虚函数
virtual void QStyle::polish(QPalette &palette);					//虚函数
	以上函数用于初始化部件的外观,会在部件创建完成之后,在第一次显示之前被调用,默认实现什么也不做。子类化QStyle时,可利用以上函数的调用时机,对部件的一些属性进行初始化。具体使用方法见示例13.5。
7、virtual void QStyle::unpolish(QWidget *widget);				//虚函数
virtual void QStyle::unpolish(QApplication *application);			//虚函数
	以上函数与polish()函数相对应,需要注意的是,只有在部件被销毁时才会被调用。
8、virtual QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget = Q_NULLPTR) const = 0;	//纯虚函数
	返回复杂控件control的子控件subControl的矩形
9、virtual QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget = Q_NULLPTR) const = 0;	//纯虚函数
	返回由样式选项option所描述的控件的子元素element的矩形,重新实现该函数的示例见下一小节

13.4.3 子类化QStyle类的方法

子类化QStyle类自定义样式时,其父类通常应选择QStyle的子类而不是QStyle,因为QStyle的子类实现了QStyle类中的虚函数,子类化其子类可以减少重新实现虚函数所需要的工作量,否则其工作量是比较庞大的。
QStyle共有两个子类,分别为QCommonStyle及QCommonStyle的子类QProxyStyle,在子类化时可根据情况进行选择,其中QCommonStyle类实现了部件的共同界面外观,因此该类实现的界面并不一定完整,而QProxyStyle类则实现了一个QStyle(通常是默认的系统样式),因此该类的实现比较完整。
自定义部件外观样式时,可以根据需要绘制的部件而选择drawControl()或drawComplexControl()或drawPrimitive()函数进行绘制,下面以示例对此方法进行说明

示例13.5:子类化QCommonStyle实现自定义按钮样式(效果见图13-7)
//m.h文件的内容
#ifndef M_H
#define M_H
#include<QtWidgets>
class B:public QCommonStyle{    Q_OBJECT   	//子类化QCommonStyle类
public:B(){}
//重新实现drawControl()函数以绘制控件的自定义外观(本示例用于绘制一个QPushButton)
void drawControl(ControlElement e, const QStyleOption *op,
                    			QPainter *pr, const QWidget *w = Q_NULLPTR) const{ 
QPen pn(QColor(111,1,1));			//红色
    		QBrush bs(QColor(111,111,111));   	//灰色
    		QBrush bs1(QColor(1,111,1));		//绿色
    		QBrush bs2(QColor(222,222,222));  	//白色
	//参数op,携带了绘制部件时的状态及其他一些信息。本示例绘制的是按钮,因此把op转换为
//QStyleOptionButton类型。
    		const QStyleOptionButton *pb=qstyleoption_cast<const QStyleOptionButton*>(op);
    		QRect r=pb->rect;					//获取设置的按钮的大小
	//若鼠标进入按钮则使用绿色填充其背景
    		if(pb->state&QStyle::State_MouseOver)    {    pr->fillRect(r,bs1);    }
	//若按钮处于凸起状态(通常为未选中,未被按下状态),则使用灰色填充其背景。
//注意:凸起状态和鼠标进入按钮的状态,二者只能存在其一。
    		else if(pb->state&QStyle::State_Raised)    {    pr->fillRect(r,bs);    }
	//若按钮被按下,则使用白色填充其背景
    		if(pb->state&QStyle::State_Sunken)    { pr->fillRect(r,bs2);    }
	//当按钮具有焦点时,绘制按钮的焦点边框(红色)
    		if(pb->state&QStyle::State_HasFocus)    {
    			pr->save();   	pn.setWidth(4);  	pn.setStyle(Qt::DashLine);    pr->setPen(pn);
    			pr->drawRect(r.adjusted(1,1,-2,-2));    pr->restore();    }
	//绘制按钮显示的文本。
    		pr->drawText(r,Qt::AlignCenter,pb->text);	}
void polish(QWidget *w){   
//设置Qt::WA_Hover属性后,将使鼠标在进入或离开部件时产生绘制事件,若不设置此属性,
//则鼠标进入或离开部件时,部件不会更新。
 w->setAttribute(Qt::WA_Hover,true);     }
void unpolish(QWidget *w){    w->setAttribute(Qt::WA_Hover,false);    }	};
#endif // M_H

//m.cpp文件的内容
#include "m.h"
int main(int argc, char *argv[]){    QApplication aa(argc,argv);
QWidget w;
QPushButton *pb1=new QPushButton("AAA",&w); pb1->move(22,22);pb1->resize(221,22);
QPushButton *pb2=new QPushButton("BBB",&w); pb2->move(22,88);
pb1->setStyle(new B());  				//按钮pb1使用自定义的样式
w.resize(444,333);    w.show();    return aa.exec();  }

在这里插入图片描述

示例13.6:子类化QCommonStyle实现自定义微调按钮样式(效果见图13-8)
//m.h文件的内容
#ifndef M_H
#define M_H
#include<QtWidgets>
class B:public QCommonStyle{    Q_OBJECT
public:	B(){}
//本示例需重新实现drawComplexControl()函数,因为微调按钮是复杂控件元素
void drawComplexControl(ComplexControl c,const QStyleOptionComplex *op,
            						QPainter *pr, const QWidget *w = Q_NULLPTR) const{
	//参数op,携带了绘制部件时的状态及其他一些信息。本示例绘制的是微调按钮,
//因此把op转换为QStyleOptionSpinBox类型。
    		const QStyleOptionSpinBox *pb=qstyleoption_cast<const QStyleOptionSpinBox*>(op);
    		qDebug()<<pb->state;				//可输出pb以查看微调按钮的状态值
	//获取微调按钮向上/向下箭头和文本编辑区域所占据的矩形
    		QRectF r1=subControlRect(c,op,QStyle::SC_SpinBoxUp,w);
    		QRectF r2=subControlRect(c,op,QStyle::SC_SpinBoxDown,w);
    		QRect r3=subControlRect(c,op,QStyle::SC_SpinBoxEditField,w);
	//创建一些画刷和画笔
    		QBrush bs(QColor(111,111,111));   	//灰色
    		QBrush bs1(QColor(111,1,1));		//红色
    		QPen pn(QColor(1,111,1));			//绿色
	//填充微调按钮箭头区域(含向上/向下箭头)的背景色为灰色
    		pr->fillRect(r1.x(), r1.y(), r1.width(), r1.height()+r2.height(),bs);
	//创建向上箭头(一个指向上的三角形)的路径
    		QPainterPath ph1;
  		ph1.moveTo(r1.x()+r1.width()/2,r1.y());	ph1.lineTo(r1.bottomLeft());
    		ph1.lineTo(r1.bottomRight());			ph1.closeSubpath();
    		pr->drawPath(ph1);
	//创建向下箭头(一个指向下的三角形)的路径
    		QPainterPath ph2;
    		ph2.moveTo(r2.x(),r2.y());   		ph2.lineTo(r2.topRight());
   	 	ph2.lineTo(r2.bottomLeft().x()+r2.width()/2,r1.height()+r2.height());
    		ph2.closeSubpath();   				pr->drawPath(ph2);
	//绘制鼠标按下箭头时的外观
    		if(pb->state&QStyle::State_Sunken) {
//把当前鼠标的坐标转换为相对于微调按钮的坐标
      		QPoint pt1=w->mapFromGlobal(QCursor::pos());   
        		pr->save();  			//在绘制前保存画刷
        		pr->setBrush(bs1);
			//若鼠标按下的是向上箭头则使用画刷bs1(红色)填充其路径ph1,否则填充ph2。
        		if(pt1.y()<r1.height()){		pr->drawPath(ph1);	}
        		else{	pr->drawPath(ph2);	}
			//恢复画刷,若不恢复画刷,则前面设置的画刷bs1会继续作用于之后的绘制。
        		pr->restore();      			}
	//绘制当微调按钮获得焦点时的焦点边框
    		if(pb->state&QStyle::State_HasFocus){
    			pr->save();
    			pr->setPen(pn);   	//绿色画笔
    			pr->drawRect(r3.adjusted(-1,-1,r1.width(),1));
    			pr->restore();	}    }};
#endif // M_H

//m.cpp文件的内容
#include "m.h"
int main(int argc, char *argv[]){    QApplication aa(argc,argv);
    QWidget w;
    QPushButton *pb1=new QPushButton("AAA",&w); pb1->move(22,22);pb1->resize(221,22);
    QSpinBox *px=new QSpinBox(&w); px->move(55,55);    px->resize(221,44);
    px->setStyle(new B());   		//微调按钮使用自定义样式
    w.resize(444,333);    w.show();    return aa.exec();  }

在这里插入图片描述

以上示例使用drawControl()或drawComplexControl()函数直接对整个部件的外观进行绘制,若使用drawPrimitive()函数,则还可以绘制部件的某个元素,比如可绘制按钮的PE_PanelButtonCommand元素、PE_FrameDefaultButton元素等,大至代码如下

void drawPrimitive(PrimitiveElement e, const QStyleOption *op, QPainter *pr,
 										const QWidget *w = Q_NULLPTR) const
	{	……
switch(e){……
		case PE_PanelButtonCommand:
				//绘制元素的代码
		case PE_FrameDefaultButton:
				//绘制元素的代码		
		……
}
……
}

13.5.1 QStyle::PixMetric枚举的使用

本文作者:黄邦勇帅(原名:黄勇)

在这里插入图片描述

  • 7
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值