一、qss文件的使用

  1. // 设置qss
  2. QFile qss(":/prefix/style.qss");
  3. if (qss.open(QFile::ReadOnly)) {
  4. qApp->setStyleSheet(qss.readAll());
  5. qss.close();
  6. }

二、语法规则

1.Qt 样式表

Qt样式表(style sheet) 是用于定制用户界面的强有力的机制,其概念、术语是受到HTML中的级联样式表( Cascading Style Sheets, CSS) 启发而来的,只是Qt样式表是应用于窗体界面的。
与HTML的CSS类似, Qt的样式表是纯文本的格式定义,在应用程序运行时可以载入和解析这些样式定义。使用样式表可以定义各种界面组件(QWidget类及其子类)的样式,从而使应用程序的界面呈现不同的效果。很多软件具有换肤功能,使用Qt的样式表就可以容易地实现这样的功能。
在UI 设计器中集成了Qt样式表的编辑功能。在设计窗时,选择窗体或某个界面组件,单击鼠标右键,在弹出的快捷菜单中选择” Change styleSheet …” 菜单项就可以出现样式表编辑对话框。图16-4所示是某个窗体的样式表编辑对话框,已经对窗体和一些类设置了样式定义,例如:

  1. QWidget{
  2. background-color: rgb(255, 255, 0);
  3. color: rgb(255, 0, 0);
  4. font: 12pt "宋体";
  5. }

这定义了QWidget类的背景颜色、字体大小和名称、前景颜色。这个样式定义会应用于QWidget 类及其子类。

  1. QLineEdit {
  2. border: 2px groove gray;
  3. border-radius: 5px;
  4. padding: 2px 4px;
  5. border-color: rgb (12, 45, 68);
  6. }

这定义了QLineEdit 类的显示效果,包括边框宽度、圆角边框的半径和边框颜色等。
image.png
图1 样式表编辑对话框
在图1中的对话框内,上方有几个具有下拉菜单的按钮,可以添加一些常用的样式属性,如前景色color、背景色background-color 、选中后颜色selection-color,以及背景图片background-image等。

2.Qt样式表句法

2.1 一般句法格式

Qt 样式表的句法(syntax)与HTML的CSS句法几乎完全相同。Qt样式表包含一系列的样式法则,一个样式法则由一个选择器(selector) 和一些声明(declaration ) 组成。例如:

  1. QPlainTextEdit(
  2. font: 12pt "仿宋";
  3. color: rgb (255, 255, 0) ;
  4. background-color: rgb (0, 0, 0);
  5. }

其中, QPlainTextEdit就是选择器,表明后面花括号里的样式声明应用于QPlainTextEdit 类及其子类。样式声明部分是样式法则列表,每个样式法则由属性和值组成,每条法则用分号结束。每条样式法则由“属性:值”构成,例如:font: 12pt "仿宋";。表示font属性,字体大小为12pt,字体名称为“仿宋”;当一个属性有多个值时,多个值用空格隔开。

2.2 选择器(selector)

Qt样式表支待CSS2中定义的所有选择器,表16-1显示的是一些常用的选择器。
表16-1 Qt样式表中的选择器类型

选择器 例子 用途
通用选择器 * 所有组件
类型选择器 QPushButton 所有QPushButton类及其子类的组件
属性选择器 QPushButton[ flat= “false” ] 所有flat属性为false的QPushButton类及其子类的组件。如果样式表应用后组件的属性再发生变化,需要重新应用样式表才能刷新显示效果
非子类选择器 .QPushButton 所有QPushButton类的组件,但是不包括QPushButton 的子类
ID 选择器 QPushButton#btnOK ObjectName为btnOK的QPushButton实例
从属对象选择器 QDialog QPushButton 所有从属于QDialog的QPushButton类的实例,即QDia log对话框里的所有QPushButton
子对象选择器 QDialog > QPushButton 所有直接从属于QDialog的QPushButton类的实例

这些选择器的定义为选择界面组件提供了灵活性。选择器可以组合使用, 一个样式声明可以
应用于多个选择器,例如:

  1. QPlainTextEdit, QLineEdit, QPushButton, QCheckBox {
  2. color: rgb(255, 255, 0);
  3. background- color: rgb (O, 0, 0);
  4. }

这个样式声明将同时应用于QPlainTextEdit、QLineEdit 、QPushButton和QCheckBox的实例。

  1. QLineEdit[readOnly="true"], QCheckBox[checked="true"] {
  2. background-color: rgb (255, 0, 0)
  3. }

上面的这个样式应用于readOnly属性为true的QLineEdit 和checked属性为true的QCheckBox实例,功能是使其背景颜色为红色。
在Qt中,可以为一个界面组件使用QObject::setPropertys()设置一个动态属性,例如,在数据表编辑界面上,一些字段是必填字段,就可以为这些字段的关联组件设置一个required属性为true,如:

  1. editName->setProperty("required", "true");
  2. comboSex->setProperty("required", "true");
  3. checkAgree->setProperty("rPqnired", "true");

这样设置了三个界面组件的动态属性required为true 。
那么,可以应用下面的样式将这种必填字段的背景颜色设置为亮绿色。

  1. *[required="true"] {
  2. background-color: lime
  3. }

2.3 子控件(sub-controls)

对于一些组合的界面组件,需要对其子控件进行选择,如QComboBox的下拉按钮,或QSpinBox的上、下按钮。通过选择器的子控件可以对这些界面元素进行显示效果控制。例如:

  1. QComboBox::drop-down {
  2. image: url(:/images/images/down.bmp);
  3. }

选择器QComboBox::drop-down选择了QComboBox的drop-down子控件,定义的样式是设置其image属性为资源文件中的图片down.bmp 。

  1. QSpinBox::up-button {
  2. image: url(:/images/images/up.bmp);
  3. }
  4. QSpinBox::down-button {
  5. image:url(:/images/images/down.bmp);
  6. }

这两条样式定义语句分别定义了QSpinBox的上、下按钮的图片,用资源文件中的图片替代了缺省的图片。
这样定义的QComboBox和QSpinBox具有如图16-5所示的显示效果。

Qt中常用的子控件见表16-2, 所有子控件的详细描述见Qt 的帮助文档。
表16-2 Qt 样式表中常见的子控件列表

子控件名称 说明
::branch QTreeView的分支指示器
::chunk QProgressBar的进度显示块
::close-button QDockWidget或QTabBar页面的关闭按钮
::down-arrow QComboBox, QHeaderYiew(排序指示器), QScrollBar或QSpinBox的下拉箭头
::down-button QComboBox的下拉按钮
::float-button QDockWidget 的浮动按钮
::groove QSlider的凹槽
::indicator QAbstractltemView,QCheckBox,QRadioButton,可勾选的QMenu菜单项,或可勾选的QGroupBox的指示器
::handle QScrollBar,QSplitter或QSlider的滑块
::icon QAbstractltemView或QMenu的图标
::item QAbstractltemView,QMenuBar,QMenu或QStatusBar的一个项
::left-arrow QScrollBar的向左箭头
::menu-arrow 具有下拉菜单的QToolButton的下拉箭头
::menu-button QToolButton的菜单按钮
::menu-indicator QPushButton的菜单指示器
::right-arrow QMenu或QScrollBar的右侧箭头
::pane QTabWidget的面板
::scroller QMenu或QTabBar的卷轴
::section QHeaderView的分段
::separator QMenu或QMainWindow的分隔器
::tab QTabBar或QToolBox的分页
::tab-bar QTabWidget的分页条。这个子控件只用于控制QTabBar在QTabWidget中的位置,定义分页的样式使用tab子控件
::text QAbstractlternView的文字
::title QGroupBox或QDockWidget的标题
::up-arrow QHeaderView(排序指示器), QScrollBar或QSpinBox的向上箭头
::up-button QSpinBox的向上按钮

2.4 伪状态( pseudo-states)

选择器可以包含伪状态,使得样式法则只能应用于界面组件的某个状态,也就是一种条件应用法则。伪状态出现在选择器的后面,用一个冒号( : )隔开。如下面的样式法则:

  1. QLineEdit:hover{
  2. background-color: black;
  3. color: yellow;
  4. }

定义了当鼠标移动到QLineEdit上方时( hover ),改变QLineEdit的背景色和前景色。
可以对伪状态取反,方法是在伪状态前面加一个感叹号(!),如:

  1. QLineEdit:!read-only{
  2. background-color: rgb(235, 255,251);
  3. }

这定义了readonly属性为false 的QLineEdit 的背景色。
伪状态可以串联使用,相当千逻辑与的计算,例如:

  1. QCheckBox:hover:checked{
  2. color: red;
  3. }

这定义了当鼠标移动到一个被勾选了的QCheckBox组件上方时,其字体颜色变为红色。
伪状态可以并联使用,相当千逻辑或的计算,例如:

  1. QCheckBox:hover, QCheckBox:checked{
  2. color: red;
  3. }

这表示鼠标移动到QCheckBox组件上方,或QCheckBox组件被勾选时,字体颜色变为红色。
子控件也可以使用伪状态,如:

  1. QCheckBox::indicator:checked{
  2. image: url(:/images/images/checked.bmp);
  3. }
  4. QCheckBox::indicator:unchecked{
  5. image: url(:/images/images/unchecked.bmp);
  6. }

这里定义了QCheckBox的indicator在checked和unchecked两种状态下的显示图片。
Qt样式定义中常见的一些伪状态见表16-3 , 熟悉这些伪状态并灵活应用可以定义自己想要的界面效果。
表16-3 Qt样式表中常见的伪状态

伪状态 描述
::active 当组件处于一个活动的窗体时,此状态为真
::adjoins-item QTreeView::branch与一个条目相邻时,此状态为负
::alternate 当QAbstractltemView的alternatingRowColors()属性为true时,绘制交替的行时此状态为真
::bottom 组件处于底部,如QTabBar的表头位于底部
::checked 组件被勾选,如QAbstractButton的checked属性为true
::closable 组件可以被关闭,例如当QDockWidget的DockWidgetClosable属性为true时
::closed 条目处于关闭状态,例如QTreeView的一个没有展开的条目
::default 条目是缺省的,如一个缺省的QPushButton按钮,或QMenu中一个缺省的action
::disabled 条目被禁用
::editable QCornboBox是可编辑的
::edit-focus 条目有编辑焦点
::enabled 条目被使能
::exclusive 条目是一个排他性组的一部分,例如一个排他性QActionGroup的一个菜单项
::first 第一个项,例如QTabBar中的第一个页
::flat 条目是flat的,例如QPushButton的flat属性设置为true时
::focus 条目具有输入焦点
::has-children 条目有子条目,例如QTreeView的一个节点具有子节点
::horizontal 条目具有水平方向
::hover 鼠标移动到条目上方时
::last 最后一个项,例如QTabBar中的最后一页
::left 条目位于左侧,例如QTabBar的页头位于左侧
::maximized 条目处于最大化,例如最大化的QMdiSubWindow窗口
::minimized 条目处于最小化,例如最小化的QMdiSubWindow窗口
::movable 条目是可移动的
::off 对于可以切换状态的条目,其状态处于“off’’
::on 对于可以切换状态的条目,其状态处于”on”
::open 条目处于打开状态,例如QTreeView的一个展开的条目
::pressed 条目上按下了鼠标
::read-only 条目是只读或不可编辑的
::right 条目位于右侧,例如QTabBar的页头位于右侧
::selected 条目被选中,例如QTabBar中一个被选中的页,或QMenu中一个被选中的菜单项
::top 条目位于顶端,例如QTabBar的页头位于顶端
::unchecked 条目处于被被选中状态
::vertical 条目处于垂直方向

2.5 属性

Qt样式表内对每一个选择器可定义多条样式规则,每个规则是一个“属性:值”对, Qt样式表中可定义的属性很多,可以在Qt的帮助文件中查找“Qt Style Sheets Reference” 查看所有属性的详细说明。
在图16-4所示的样式表编辑对话框中,从上方的几个按钮的下拉菜单中可以设计常用的一些属性,如“Add Resource”按钮下三个菜单项可以从项目的资源文件中选择图片作为background-image、border-image或image属性的值;“Add Color”按钮的下拉菜单用于设置组件的各种颜色,包括前景色、背景色、边框颜色等,颜色的值可以用rgb()、rgba()函数表示,或Qt能识别的颜色常量。
使用样式表可以定义组件复杂的显示效果。每个界面组件都可以用如图16-7 所示的盒子模型(BoxModel)来表示,模型由四个同心矩形表示。

(1) content是显示内容矩形区域,如QLineEdit用于显示文字的区域,min-width、max-width、min-height和max-height属性定义最大/最小宽度或高度,就是定义这个矩形区,例如:

  1. QLineEdit {
  2. min-width: 50px;
  3. max-height: 40px;
  4. }

这定义QLineEdit 最小宽度为50px,最大高度是40px,其中px是单位,表示像素。
(2) padding是包围content的矩形区域,通过padding属性可以定义padding的宽度,或padding-top、padding-bottom、padding-left和padding-right分别定义padding的上、下、左、右宽度,例如:

  1. QLineEdit{
  2. padding: 0px 10px 0px 10px;
  3. }

这设定padding的上、右、下、左的宽度, 它等效于:

  1. QLineEdit{
  2. padding-top: 0px;
  3. padding-right: 10px;
  4. padding-bottom: 0px;
  5. padding-left: 10px;
  6. }

(3) border是包围padding的边框,通过border属性(或border-width、border-style、border-color)
可以定义边框的线宽、线型和颜色,也可以分别定义border的上、下、左、右的线宽和颜色。使用border-radius 可以定义边框转角的圆弧半径,从而构造具有圆角矩形的编辑或按钮等组件,例如:

  1. QLineEdit {
  2. border-width: 2px;
  3. border-style: solid;
  4. border-color: gray;
  5. border-radius: 10px;
  6. padding: 0px 10px;
  7. }

这使得QLineEdit 具有灰色边框线条、圆角矩形的效果。
通过border-radius 、min-width和min-height等属性可以设计圆形的按钮,如:

  1. QPushButton {
  2. border: 2px groove red;
  3. border-radius: 30px;
  4. min-width: 60px;
  5. min-height: 60px;
  6. }

使得边框转角半径等于content宽度或长度的一半,宽度和长度相等, 就可以得到一个圆形的按钮。
使用border-image属性还可以为组件设置背景图片,图片会填充border矩形框之内的区域,一般使用材质图片设置背景,以使界面具有统一的特色,例如:

  1. QLineEdit, QPushButton{
  2. border-image: url(:/images/images/border.jpg);
  3. }

(4) margin是border之外与父组件之间的空白边距,可以分别定义上、下、左、右的边距大小。
缺省的情况下,margin、border-width和padding属性缺省值为零,这种情况下,四个同心矩形就是重合的一个矩形。
使用Qt样式表可以为界面组件设计各种美观的显示效果,美观而特殊的界面不仅需要编程的能力,更重要的是美工设计能力。

3 样式表的使用

3.1 程序中使用样式表

有多种方法可以应用样式表。
第一种是在使用Qt Designer设计窗体时,直接用样式表编辑器为窗体或窗体的某个部件设计样式表,则设计的样式保存在窗体的ui文件里,窗体创建时会自动用所设计的样式表。这样设计的样式表对应用程序是固定的,无法取得换肤的效果,且需要为每个窗体都设计样式表,重复性工作量太大。
第二种是使用setStyleSheet函数应用样式,使用qApp的setStyleSheet( )函数可以为应用程序全局设置样式,使用QWidget::setStyleSheet( )可以为一个窗口、一个对话框或一个界面组件设置样式。例如:

  1. qApp->setStyleSheet( " QLineEdit { background-color: gray } ");

这里使用应用程序全局变量qApp为QLineEdit设置样式,如果应用程序内的某些QLineEdit组件没有再被设置样式,则QLineEdit组件的背景色为灰色。

  1. MainWindow->setStyleSheet( " QLineEdit { background-color: gray } ");

这是为主窗口MainWindow内的QLineEdit组件设置样式,即背景色为亮绿色。

  1. editName->setStyleSheet (" color: blue;"
  2. "background-color: yellow;"
  3. "selection-color: yellow;"
  4. "selection-background-color: blue;");

这是设置一个ObjectName为editName的QLineEdit组件的样式,注意这时在样式表中无需设置selector名称,所设置的样式是应用于editName这个QLineEdit组件的。
这样将样式表固定在程序中,很显然也是无法实现切换界面效果的。为了实现切换界面效果(换肤)的目的,一般将样式定义表保存为qss后缀的纯文本文件,然后在程序中打开文件,读取文本内容,再调用setStyleSheet( )函数应用样式。示例代码如下:

  1. QFile file(":/qss/mystyle.qss");
  2. file.open (QFile::ReadOnly);
  3. QString styleSheet = QString::fromLatinl(file.readAll());
  4. qApp->setStyleSheet(styleSheet);

3.2 样式定义的明确性

当多条样式法则对一个属性定义了不同值时,就会出现冲突,例如:

  1. QPushButton#btnSave{ color: gray }
  2. QPushButton{ color: red }

这两条法则都可以应用于ObjectName为btnSave的QPushButton组件,都定义了其前景色,这就会出现冲突。这时,选择器的明确性(specificity)决定组件适用的样式法则,即法则应用于更明确的组件。在上面的例子中, QPushButton#btnSave被认为是比QPushButton更明确的选择器,因为它指向一个对象,而不是QPushButton的所有实例。所以,如果是在一个窗口上应用以上两条法则,则btnSave按钮的前景色为gray,而其他按钮的前景色为red。
同样,具有伪状态的选择器被认为比没有伪状态的选择器明确性更强,如:

  1. QPushButton:hover{ color: white }
  2. QPushButton{ color: red }

这样,当鼠标在按钮上停留时颜色为white, 否则颜色为red。
如果两个选择器具有相同的明确性,则以法则出现的先后顺序为准,后出现的法则起作用,例如:

  1. QPushButton:hover{ color: white }
  2. QPushButton:enabled{ color: red }

这里的两个选择器具有相同的明确性,所以,当鼠标停留在一个使能的按钮上时,只有第二条法则起作用。这种情况下,如果希望不出现冲突,应该修改法则以使其更明确,如下面这两条法则是不冲突的。

  1. QPushButton:hover:enabled{ color: white }
  2. QPushButton:enabled{ color: red }

父子关系的两个类作为选择器时,具有相同的明确性,例如:

  1. QPushButton { color: red}
  2. QAbstractButton { color: gray}

这两个选择器的明确性相同,所以只依赖于语句的先后顺序。
确定法则的明确性,Qt样式表遵循CSS2的规定,在设计样式表时应尽量明确并避免冲突情况。

3.3 样式定义的级联性

样式定义可以在qApp、窗口或一个具体组件中定义,任何一个组件的样式是其父组件、父窗口和qApp的样式的融合。当出现冲突时,组件会使用离自己最近的样式定义,即按顺序使用组件自己的样式、或父组件的样式定义、或父窗口的样式定义,或qApp的样式定义,而不考虑样式选择器的确定性。
例如,在QApplication中设置全局样式:

  1. qApp->setStyleSheet( " QPushButton { color: red } ");

那么应用程序中所有未再定义样式的QPushButton的前景颜色为red 。
如果在MainWindow中再定义样式:

  1. MainWindow->setStyleSheet( " QPushButton { color: blue } ");

则MainWindow上的按钮的前景色为blue,而不是red 。
如果MainWindow上有一个名称为btnSave的QPushButton按钮,其定义样式如下:

  1. btnSave->setStyleSheet( " color: yellow; background-color: black; " );

则按钮btnSave 按照自己的样式显示前景和背景色。
Qt样式表功能强大,可以设计自己想要的界面效果,但是这需要有较好的美工设计基础,需要在使用样式表的过程中不断尝试和总结经验。