QT Style的机制和GTK的Style机制很类似,基本上就是
定义了一个基础的Style类,在Style类里面定义一系列的绘图相关函数接口,具体风格的Style类实现了这些函数接口。
在控件的实现中,控件的绘图函数调用Style类的绘图函数接口,绘制具体的控件UI
通过使用不同的Style实现,实现全局UI风格的改变
所不同的是这些函数的的颗粒度、分类及复杂程度。此外各种局部风格调整的机制,思路也是一致的,只是在具体实现上有不同的Approach
QStyle主要函数
QStyle类提供的功能主要包括提供标准的绘图函数和一些辅助函数
绘图类函数
QStyle类将UI的组成元素分为三类
Primitive Elements : 基本元素,一些基本的,被多个控件所复用的元素,如边框,倒角,箭头等(注意这里的基本元素的概念,和点,线等绘图基本元素的概念不同)。基本元素本身不能单独纯在。
Control Elements : 控件元素,如按键,复选框等,不单是这些单独的控件,也包括复杂控件中的组成元素,如滚动条中的滑块。它和基本元素不同的地方是,它能够单独处理用户交互操作。
Complex Contro Elements : 复杂控件元素,复杂控件元素包含有子控件元素。例如combo boxes(复合选择框)等。它的用户交互响应,取决于用户操作的对象是哪个子控件。
所以,对应的有三个函数用于绘制这三类UI元素。因为不同的控件对各自的UI有不同的要求,具体控件在绘制自己的UI的时候,调用这些函数是,所需要的参数也会有不同,所以这三类函数基本都需要传参指定:
绘制元素的类型
该类型对应的具体参数(用类的方式传递,基本上绘制不同的元素都需要传递自己特定的参数类(QStyleOption的子类))
用于具体绘图的QPainter类
控件本身(可选)
理论上说,传递一个控件对象本身,就应该包含它绘图时所需要的参数,但是传递QStyleOption来指定参数也有好处,就是任何控件都可以绘制任意所需要的UI元素,不一定只有按键控件才能传参来绘制按键UI
辅助类函数
辅助类的函数,这里列举一部分:
一组用来返回文本或子元素的边界矩形(Bounding Rect)的函数,用来获取对应元素的边界信息
用来提供符合特定GUI风格标准的图标位图的函数
用来获取一些风格相关尺寸信息(比如默认边框宽度,按键间距等等)的函数
获取Style标准调色板的函数
调色板
每个具体的Style类都会返回一个Palette调色板类,用分组的方式(ColorGroup)定义控件在不同状态下(Disabled/Active/Inactive/Normal)的各类颜色(QColor)/画笔(QBrush)信息。在一组的颜色画笔信息中,进一步以角色(ColorRole)的形式进行检索。
举例来说,你可以指定 例如 Active状态(group)下按键文本(ColorRole)来获取对应的颜色/画笔信息
Style和Painter
在Style类内部,最终还是调用Painter的画图函数来执行具体的画图动作,所以同一个Style对应不同的Painter,最终显示效果可能会有不同
Style Awareness的控件/程序
通常来说,编写应用程序的时候,除非对应用程序的外观有特定的要求。否则,为了能让QT应用程序的外观能够在各种平台和风格设定的情况下都能最大程度的兼容系统设定,不应该在代码中强制指定例如颜色,字体等等外观相关的参数。
而如果对应用程序的外观有特定的要求,也应该尽量通过Style相关的方式来支持,例如继承定制自己的Style,或者用Style-sheet在当前Style的基础上,修改部分UI风格。
在实现定制的控件的时候,也应该尽量使用Style相关绘图函数来绘制控件外观,而不是直接用各类绘图函数绘制固定的外观。
设置Style
你可以通过 QApplication::setStyle 或者 QWidget::setStyle 函数设置整个函数或者单个控件要使用的Style
你也可以通过 -style stylename 命令行参数指定应用程序要使用的UI风格
./application -style motif
除了子类化Style类,使用QT样式表(QStyleSheet)是另一种快速改变QT程序UI风格的方法,它很大程度上借鉴和参考了HTML层叠样式表的语法和思想。其效果叠加在Style的基础上
按官方文档的说法,QT样式表目前只能和内置Style配合工作,和子类化的Style的配合正在开发中
StyleSheet的使用
StyleSheet文件的默认后缀名为qss,可以通过命令行参数 -stylesheet filename.qss 来设置样式表,也可以通过QApplication::setStyleSheet 或 QWidget::setStyleSheet来设置应用程序或特定控件要使用的样式表
::setStyleSheet函数的参数是字符串(不是qss文件的名字,而是样式表的内容),所以直接使用的话,不方便一次设置大量的规则,但是可以使用资源文件将qss样式表嵌入到程序中,然后通过QApplication::setStyleSheet来使用,例如:
file.open(QFile::ReadOnly);
app.setStyleSheet(file.readAll());
QFile file(":/qss/mystylesheet.qss");
QFile file(":/qss/mystylesheet.qss"); file.open(QFile::ReadOnly); app.setStyleSheet(file.readAll());
样式表语法
一条样式表的描述规则可能类似这样:
QComboBox#myComboBox::down-arrow:pressed {
position:relative;
top:1px; left:1px;
}
QComboBox#myComboBox::down-arrow:pressed { position: relative; top: 1px; left: 1px; }
一条样式表规则由选择器 (Selector)和属性定义(declaration)组成。
QComboBox#myComboBox::down-arrow:pressed
QComboBox#myComboBox::down-arrow:pressed
这部分是选择器,用于指定样式表规则的应用对象,细分开来:
QComboBox#myComboBox 这一部分叫类型选择器(Type Selector),QComboBox指定了对象类名,#name指定对象的实例名(非必须)其它的选择器语法请参考官方文档。
down-arrow 子控件描述符(subcontrol),和前面的字段用::隔开,这里表示组合框的下拉按键
pressed 伪状态(Pseudo-States)描述符,和前面的字段用:隔开,这里表示压下状态
以上除了第一个字段,都不是必须,而是进一步限制规则适用范围
{
position:relative;
top:1px; left:1px;
}
{ position: relative; top: 1px; left: 1px; }
这部分是属性定义,由成对的属性名:属性值组成,用;分隔。
样式表规则的范围和优先级关系
因为样式表规则的层级关系比较复杂,另外设置的方式也有很多,有时候会发生多条规则都和某一个具体控件相关的情况,这样具体哪一条样式表规则最终被应用到控件的UI表现上,就需要有一个规则来定义,大致上牵涉到这个问题的情况有如下几种
规则冲突
多条规则制定了不同的内容,可能的情况有很多,比如
QPushButton:hover { color: white }
QPushButton { color: red }
这种情况,其实可以不算冲突,更加具体的类型描述符定义的规则拥有更高优先级,所以一个有鼠标悬停的按钮的文本颜色就是白色的,否则为红色
QPushButton:hover { color: white }
QPushButton:enabled { color: red }
这里就可能发生规则冲突,当一个使能的按钮有鼠标悬停的时候,颜色的定义是什么呢? QStyleSheet的判断原则是,后面规则的优先级高于前面的规则,所以这种情况颜色为红色
QPushButton { color: red }
QAbstractButton { color: gray }
这个相对难发现,一个基类及其子类都定义了针对文本颜色的规则,那么子类应用哪一条规则呢?你可能会认为自然是子类用自己的规则了,很可惜不是,样式表不考虑类的继承层级优先关系,所以还是后一条规则优先级高于前一条规则。如果确实要单独设定子类的规则,需要交换规则的顺序。
样式表层叠 Cascading
因为样式表可以应用在QApplication上,也可以单独应用在控件上,所以最终应用到一个具体控件的样式表,是通过叠加合并所有的父控件乃至应用程序的样式表设定来得到的。这种情况下,也有可能发生规则冲突, 例如:
myPushButton->setStyleSheet("* { color: blue }");
qApp->setStyleSheet("QPushButton { color: white }");
qApp->setStyleSheet("QPushButton { color: white }"); myPushButton->setStyleSheet("* { color: blue }");
这种情况下,控件自身的样式表的优先级高于父控件或应用程序的样式表
样式表继承 Inheritance
在标准的CSS样式表中,一个控件的字体和颜色属性如果没有明确设定,那么将自动继承自父控件,而在QT的样式表中,不会自动继承,例如:
qApp->setStyleSheet("QGroupBox { color: red; } ");
这种情况下,对于GroupBox中的添加的子控件,不会自动设置其颜色属性,如果要设置子控件的属性,需要明确设定:
qApp->setStyleSheet("QGroupBox, QGroupBox * { color: red; }");
注意,这里的继承和上面的层叠不一样,层叠指不同样式表设置同一个对象,继承指的则是样式表规则中选择器对控件层级关系的影响
Stylesheet是如何附加在Style上的
当设置StyleSheet的时候,一个QStyleSheetStyle会被生成并被设为应用程序实际的Style,而当前的Style则被reparent成QStyleSheetStyle的子对象,当应用程序或控件调用Style绘图的时候,实际调用的是QStyleSheetStyle对象,在QStyleSheetStyle类内部,会先调用函数按照StyleSheet定义的规则绘制UI,如果没有相关规则,再调用当前的Style对象的绘图函数绘制UI,所以QStyleSheet作为一个代理Style先对UI进行处理。