简要说明
Qt样式表的术语和语法规则几乎与HTML CSS相同。如果已经了解CSS,那么可以快速熟悉QSS的语法规则。
样式规则
样式表由一系列样式规则组成。样式规则由选择器和声明组成。选择器指定哪些小部件受规则影响,而声明则指定应该在小部件上设置哪些属性。例如
QPushButton { color: lightblue; }
在上面的样式规则中,QPushButton是选择器,而{ color: lightblue; }是声明。该规则指定QPushButton及其子类应该使用浅蓝色作为前景色。
Qt样式表通常不区分大小写(例如:color、Color、COLOR和cOloR指的是同一属性)。唯一的例外是类名、对象名和Qt属性名,它们区分大小写。
可以为同一声明指定多个选择器,使用逗号(,)分隔选择器。例如
QPushButton, QLineEdit, QComboBox { color: red; }
等同于
QPushButton { color: red; }
QLineEdit { color: red; }
QComboBox { color: red; }
样式规则的声明部分是属性:值对的列表,包含在大括号({})中,并用分号分隔。例如
QPushButton { color: red; background-color: white; }
选择器
Qt样式表支持CSS2中定义的所有选择器。下表总结了最有用的选择器类型。
选择器 | 示例 | 解释 |
---|---|---|
通用选择器 | * | 匹配所有小部件 |
类型选择器 | QPushButton | 匹配QPushButton及其子类的实例 |
属性选择器 | QPushButton[flat=“false”] | 匹配非平面的QPushButton实例。可以使用这个选择器来测试任何支持QVariant::toString()的Qt属性。此外,对于类的名称,还支持特殊的类属性。这个选择器也可以用来测试动态属性。除了=,还可以使用~=来测试QStringList类型的Qt属性是否包含给定的QString。警告:如果在样式表设置之后Qt属性的值发生变化,可能需要强制重新计算样式表。实现此目的一种方法是取消样式表的设置并重新设置它。 |
类选择器 | .QPushButton | 匹配QPushButton的实例,但不匹配它的子类。这相当于*[class~=“QPushButton”]。 |
ID选择器 | QPushButton#okButton | 匹配对象名为okButton的所有QPushButton实例。 |
后代选择器 | QDialog QPushButton | 匹配QDialog的后代对象中所有QPushButton实例。 |
子选择器 | QDialog > QPushButton | 匹配QDialog的直接子对象中所有QPushButton实例。 |
子控件
对于样式化复杂的小部件,需要访问小部件的子控件,比如QComboBox的下拉按钮或QSpinBox的上下箭头。选择器可能包含子控件,而这使得定制子控件成为可能。例如:
QComboBox::drop-down { image: url(dropdown.png); }
上面的规则样式化了所有QComboBox的下拉按钮。虽然双冒号(::)语法让人想起CSS3伪元素,但Qt子控件在概念上与这些不同,具有不同的级联语义。
子控件总是相对于另一个元素——引用元素进行定位的。这个引用元素可以是小部件或其他小控件。例如,QComboBox的::drop-down默认情况下放置在QComboBox的填充矩形的右上角。::drop-down默认情况下放置在::drop-down的内容矩形的中心。
可以使用subcontrol-origin属性更改要使用的原点矩形。例如,想在QComboBox的空白矩形中放置drop-down,而不是在默认的填充矩形,可以指定:
QComboBox {
margin-right: 20px;
}
QComboBox::drop-down {
subcontrol-origin: margin;
}
使用子控件的subcontrol-position属性更改边框内下拉列表的对齐方式。
width和height属性可用于控制子控件的大小。注意,设置image会隐式地设置子控件大小。
相对定位方案(position: relative)允许子控件的位置从其初始位置偏移。例如,当QComboBox的下拉按钮被按下时,我们可能希望里面的箭头被偏移以产生“按下”的效果。要实现这一点,我们可以指定:
QComboBox::down-arrow {
image: url(down_arrow.png);
}
QComboBox::down-arrow:pressed {
position: relative;
top: 1px; left: 1px;
}
绝对定位方案(position: absolute)允许子控件的位置和大小相对于引用元素进行更改。一旦定位,它们将被视为小部件并可以使用box model进行样式化。
注意:对于复杂的小部件,如QComboBox和QScrollBar,如果一个属性或子控件是定制的,那么所有其他属性或子控件也必须是定制的。
伪状态
选择器可能包含伪状态,这些伪状态表示根据小部件的状态限制规则的应用。伪状态出现在选择器的末尾,中间有一个冒号":"。例如,当鼠标悬停在QPushButton上时,应用以下规则:
QPushButton:hover { color: white; }
可以使用感叹号运算符来否定伪状态。例如,当鼠标不悬停在QRadioButton上时,应用以下规则:
QRadioButton:!hover { color: red; }
伪状态可以被链接,在这种情况下,逻辑与是隐含的。当鼠标悬停在选中的QCheckBox上时,应用以下规则:
QCheckBox:hover:checked { color: white; }
否定的伪状态可能出现在伪状态链中。例如,当鼠标悬停在未按下的QPushButton上时,应用以下规则:
QPushButton:hover:!pressed { color: blue; }
如果需要,逻辑或可以用逗号运算符来表示:
QCheckBox:hover, QCheckBox:checked { color: white; }
伪状态可以与子控件组合出现。例如:
QComboBox::drop-down:hover { image: url(dropdown_bright.png); }
冲突解决
当多个样式规则指定具有不同值的相同属性时,会出现冲突。考虑以下样式表:
QPushButton#okButton { color: gray; }
QPushButton { color: red; }
这两个规则都匹配对象名为okButton的QPushButton实例,并且颜色属性存在冲突。要解决这一冲突,我们必须考虑到选择器的特殊性。在上面的例子中,QPushButton#okButton被认为比QPushButton更具体,因为它(通常)引用单个对象,而不是类的所有实例。
类似地,带有伪状态的选择器比不指定伪状态的选择器更具体。因此,下面的样式表指定当鼠标悬停在QPushButton上时,它应该有白色的文本,否则就是红色的文本:
QPushButton:hover { color: white; }
QPushButton { color: red; }
这有一个棘手的问题:
QPushButton:hover { color: white; }
QPushButton:enabled { color: red; }
这里,两个选择器具有相同的专一性,因此,如果在启用按钮时鼠标悬停在按钮上,则第二个规则优先。如果我们想要文本是白色的,我们可以这样重新排列规则:
QPushButton:enabled { color: red; }
QPushButton:hover { color: white; }
或者,可以让第一条规则更具体:
QPushButton:hover:enabled { color: white; }
QPushButton:enabled { color: white; }
在使用类型选择器时也会出现类似的问题。考虑下面的例子:
QPushButton { color: red; }
QAbstractButton { color: gray; }
这两个规则都适用于QPushButton实例(因为QPushButton继承自QAbstractButton),颜色属性存在冲突。因为QPushButton继承了QAbstractButton,所以我们很容易假设QPushButton比QAbstractButton更具体。但是,对于样式表计算,所有类型选择器都具有相同的特殊性,最后出现的规则优先。换句话说,包括QPushButton在内的所有QAbstractButtons的颜色都被设置为灰色。如果我们真的希望QPushButton有红色的文本,我们总是可以重新排序规则。
为了确定规则的特异性,Qt样式表遵循CSS2规范:
选择器的特异性计算如下:
- 计算选择器中ID属性的数量(= a)
- 计算选择器中其他属性和伪类的数量(= b)
- 计算选择器中元素名称的数量(= c)
- 忽略伪元素[例如,子控件]。
连接三个数字a-b-c(在一个基数很大的数字系统中)给出了这种特性。
级联
可以在QApplication、父小部件和子小部件上设置样式表。通过合并小部件的祖先(父、祖父等)上设置的样式表,以及QApplication上设置的任何样式表,可以获得任意小部件的有效样式表。
当出现冲突时,小部件自己的样式表总是优先于任何继承的样式表,而不考虑冲突规则的特殊性。同样,父小部件的样式表比祖父小部件的样式表更有效,等等。
这样做的一个结果是,在小部件上设置样式规则自动使其优先于祖先小部件的样式表或QApplication样式表中指定的其他规则。考虑下面的例子。首先,我们在QApplication上设置了一个样式表:
qApp->setStyleSheet("QPushButton { color: white; }");
然后在QPushButton对象上设置样式表:
myPushButton->setStyleSheet("* { color: blue; }");
尽管应用程序范围的样式表提供了更具体的规则集,但是QPushButton上的样式表强制QPushButton(和任何子小部件)具有蓝色文本。
如果设置成如下的样式表,也具有相同效果
myPushButton->setStyleSheet("{ color: blue; }");
除了如果QPushButton有子元素(这是不可能的),样式表对它们没有影响。
样式表级联是一个复杂的主题。有关详细信息,请参考CSS2规范。
继承
在经典的CSS中,当没有显式设置项的字体和颜色时,它会自动从父对象继承。默认情况下,当使用Qt样式表时,小部件不会自动从其父小部件继承其字体和颜色设置。
例如,考虑QGroupBox中的QPushButton:
qApp->setStyleSheet("QGroupBox { color: red; } ");
QPushButton没有显式的颜色集。因此,它具有系统颜色,而不是继承它的父对象QGroupBox的颜色。如果我们想设置QGroupBox及其子元素的颜色,可以这样写:
qApp->setStyleSheet("QGroupBox, QGroupBox * { color: red; }");
相反,使用QWidget::setFont()和QWidget::setPalette()设置字体和面板会传播到子窗口小部件。
如果希望字体和面板传播到子部件,可以设置Qt::AA_UseStyleSheetPropagationInWidgetStyles标志,如下所示:
QCoreApplication: setAttribute (Qt:: AA_UseStyleSheetPropagationInWidgetStyles,true);
当启用小部件样式的字体和调色板传播时,通过Qt样式表进行的字体和调色板更改的行为将与用户手动调用样式表所针对的所有QWidget上的相应QWidget::setPalette()和QWidget::setFont()方法一样。如果这会导致在c++中的传播,则会导致在样式表中的传播,反之亦然。
C++命名空间中的小部件
类型选择器可用于指定特定类型的小部件的样式。例如
class MyPushButton : public QPushButton {
// ...
}
// ...
qApp->setStyleSheet("MyPushButton { background: yellow; }");
Qt样式表使用小部件的QObject::className()来决定何时应用类型选择器。当自定义小部件位于名称空间中时,QObject::className()返回<命名空间>::<类名>。这与子控件的语法冲突。要解决这个问题,在名称空间内使用小部件的类型选择器时,必须将“::”替换为“——”。例如
namespace ns {
class MyPushButton : public QPushButton {
// ...
}
}
// ...
qApp->setStyleSheet("ns--MyPushButton { background: yellow; }");
设置 QObject属性
从4.3及以上版本,任何可设计的Q_PROPERTY都可以使用qproperty-<属性名>语法。
MyLabel { qproperty-pixmap: url(pixmap.png); }
MyGroupBox { qproperty-titleColor: rgb(100, 200, 100); }
QPushButton { qproperty-iconSize: 20px 20px; }
如果属性引用了用Q_ENUMS声明的枚举,则应按名称引用其常量,而不是它们的数值。
参考
参考Qt助手,如有错误,请指正,谢谢!