界面优化
- 1 QSS
- 1.1 背景介绍
- 1.2 基本语法
- 1.3 QSS设置方式
- 1.3.1 指定控件样式设计
- 1.3.2 全局样式设置
- 1.3.3 使用 Qt Designer 编辑样式
- 1.4 选择器
- 1.4.1选择器概况
- 1.4.2 子控件选择器(Sub-Controls)
- 1.4.3伪类选择器(Pseudo-States)
- 1.5 样式属性
- 1.5.1 盒模型(Box Model)
- 1.6控件样式示例
- 1.6.1按钮
- 1.6.2 复选框
- 1.6.3 单选框
- 1.6.4输入框
- 1.6.5 列表
- 1.6.6 菜单栏
- 1.6.7 登录界面
- 1.7 小结
1 QSS
1.1 背景介绍
在网页前端开发领域中,CSS是一个至关重要的部分.描述了一个网页的"样式"从而起到对网页美化的作用
所谓样式,包括不限于大小,位置,颜色,背景,间距,字体等等
现在的网页很难找到没有CSS的.可以说让"界面好看"是一个刚需
网页开发作为GUI的典型代表,也对于其他客户端GUI开发产生了影响. Qt也是其中之一.
Qt仿照CSS的模式,引入了QSS,来对Qt中的控件做出样式上的设定,从而允许程序员写出界面更好看的代码.
同样受到HTML的影响,Qt还引入了QML来描述界面,甚至还可以直接把一个原生的html页面加载到界面上.这部分内容咱们不讨论.
当然,由于Qt本身的设计理念和网页前端还是存在一定差异的,因此QSS中只能支持部分CSS属性.
整体来说QSS要比CSS更简单一些.
另外,大家也不必担心,没有CSS基础的同学并不影响学习QSS.
注意: 如果通过QSS设置的样式和通过C++代码设置的样式冲突,则QSS优先级更高.
1.2 基本语法
对于 CSS来说,基本的语法结构非常简单
QSS也沿用了这样的设定
选择器{
属性名:属性值
}
其中:
- 选择器描述了"哪个widget要应用样式规则".
- 属性则是一个键值对,属性名表示要设置哪种样式,属性值表示了设置的样式的值.
例如:
QPushButton { color: red; }
上述代码的含义表示,针对界面上所有的QPushButton,都把文本颜色设置为红色
编写QSS时使用单行的格式和多行的格式均可.
代码示例:QSS基本使用
1)在界面上创建一个按钮
2)编写代码,设置样式
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->pushButton->setStyleSheet("QPushButton {color: red;}");
}
3)运行程序,观察效果,可以看到按钮文本变成了红色
注意:上述代码中,我们是只针对这一一个按钮通过
setStyleSheet
方法设置的样式.此时这个样式 仅针对该按钮生效.如果创建其他按钮,其他按钮不会受到影响.
1.3 QSS设置方式
1.3.1 指定控件样式设计
QWidget
中包含了setStyleSheet
方法,可以直接设置样式. .
上述代码我们已经演示了上述设置方式.
另一方面,给指定控件设置样式之后,该控件的子元素也会受到影响.
代码示例:子元素受到影响
1)在界面上创建一个按钮
2)修改 widget.cpp ,这次我们不再给按钮设置样式,而是给 widget 设置样式(widget 是QPushButton 的父控件)
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//给 widget 本身设置样式
this->setStyleSheet("QPushButton{color:red;}");
}
运行程序,可以看到这种方法设置样式,对于子控件也会生效
1.3.2 全局样式设置
还可以通过QApplication
的setStyleSheet
方法设置整个程序的全局样式.
全局样式优点:
- 使同一个样式针对多个控件生效,代码更简洁.
- 所有控件样式内聚在一起, 便于维护和问题排查.
代码示例 1 :使用全局样式
1)在界面上创建三个按钮
2)编辑main.cpp,设置全局样式
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//设置全局样式
a.setStyleSheet("QPushButton{color:red;}");
Widget w;
w.show();
return a.exec();
}
3)运行程序,可以看到三个按钮全部变成了红色
代码示例 2 :样式的层叠特性
如果通过全局样式给某个控件设置了属性1,通过指定控件样式给控件设置属性2,那么这两个属性都会产生作用.
在上述例子的基础上加上这行代码
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置指定控件样式
ui->pushButton->setStyleSheet("QPushButton{font-size:50px;}");
}
运行程序,第一个按钮同时具备了颜色和字体大小的样式
形如上述这种属性叠加的效果,我们称为"层叠性". CSS全称为Cascading Style
Sheets,其中Cascading就是"层叠性"的意思. QSS也继承了这 样的设定. 实际上把QSS叫做QCSS也许更合适一些
代码示例 3 :样式的优先级
如果全局样式和指定样式冲突,则优先使用指定样式
在示例 1 的基础上加上冲突的指定样式
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置第一个按钮为绿色
ui->pushButton->setStyleSheet("QPushButton{color:green;}");
}
运行程序,可以看到,第一个按钮的字体颜色变成了绿色
在CSS中也存在类似的优先级规则.通常来说都是"局部"优先级高于"全局"优先级.
相当于全局样式先"奠定基调",再通过指定控件样式来"特事特办" .
1.3.3 使用 Qt Designer 编辑样式
QSS也可以通过Qt Designer直接编辑,从而起到实时预览的效果.同时也能避免C++和QSS代码的耦合.
代码示例:使用Qt Designer编辑样式
1)在界面上创建一个按钮
2)右键按钮,选择"改变样式表"
3)在弹出的样式表编辑器中,可以直接填写样式.填写完毕,点击OK即可.
4)此时Qt Designer的预览界面就会实时显示出样式的变化.
5)运行程序,可以看到样式确实发生了改变.
这种方式设置样式,样式内容会被以xml格式记录到ui文件中.
<property name="styleSheet">
<string notr="true">QPushButton { color: red; }</string>
</property>
同时在控件的styleSheet属性中也会体现.
1.4 选择器
1.4.1选择器概况
QSS的选择器支持以下几种:
总体来说, QSS选择器的规则和CSS选择器基本一致.
上述选择器我们也不需要全都掌握,就只熟悉最常用的几个即可(上述加粗的).
代码示例:使用类型选择器选中子类控件
1)在界面上创建一个按钮.
2) 修改main.cpp,设置全局样式
注意,此处选择器使用的是QWidget. QPushButton也是QWidget的子类,所以会受到QWidget选择器的影响.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置全局样式
a.setStyleSheet("QWidget { color: red; }");
Widget w;
w.show();
return a.exec();
}
- 运行程序,可以看到按钮的文本颜色已经是红色了。
5)如果把上述样式代码修改为下列代码
a.setStyleSheet(".QWidget { color: red; }");
此时按钮的颜色不会发生改变.此时只是选择QWidget类, 而不会选择QWidget 的子类QPushButton了.
代码示例:使用id选择器
1)在界面.上创建3个按钮, obj ectName为pushButton ,pushButton_ 2 ,pushButton _3
2)编写main.cpp,设置全局样式
先通过QPushButton设置所有的按钮为黄色.
再通过#pushButton 和#pushButton_ 2分别设置这两个按钮为红色和绿色.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置全局样式
QString style = "";
style += "QPushButton { color: yellow; }";
style += "#pushButton { color: red; }";
style += "#pushButton_2 { color: green; }";
a.setStyleSheet(style);
Widget w;
w.show();
return a.exec();
}
- 执行程序,观察效果
当某个控件身上,通过类型选择器和ID选择器设置了冲突的样式时,ID选择器样式优先级更高.
同理,如果是其他的多种选择器作用同一个控件时出现冲突的样式,也会涉及到优先级问题.
Qt文档上有具体的优先级规则介绍(参见The Style Sheet Syntax的Conflict Resolution章节).
这里的规则计算起来非常复杂(CSS中也存在类似的设定),咱们此处对于优先级不做进一步讨论.
实践中我们可以简单的认为,选择器描述的范围越精准,则优先级越高.一般来说,ID选择器优先级是最高的.
代码示例:使用并集选择器
1)创建按钮,label,单行输入框
2)编写main.cpp,设置全局样式
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置全局样式
a.setStyleSheet("QPushButton, QLabel, QLineEdit { color: red; } ");
Widget w;
w.show();
return a.exec();
}
- 运行程序,可以看到这三种控件的文字颜色都设置为了红色
并集选择器是一-种很好的代码复用的方式.很多时候我们希望界面上的多个元素风格是统一的,就可以使用并集选择器,把样式属性同时指定给多种控件.
1.4.2 子控件选择器(Sub-Controls)
有些控件内部包含了多个"子控件" .比如QComboBox的下拉后的面板,比如QSpinBox的上下按钮
等.
可以通过子控件选择器: :
,针对. 上述子控件进行样式设置.
哪些控件拥有哪些子控件,参考文档Qt Style Sheets Reference中List of Sub-Controls章
节.
代码示例:设置下拉框的下拉按钮样式
1)在界面.上创建一个下拉框,并创建几个选项
2)创建resource.qrc,并导入图片down. png
图标可以从阿里巴巴矢量图标库下载
阿里巴巴矢量图库
3)修改main.cpp,编写全局样式.
- 使用子控件选择器QComboBox: :down-arrow选中了QComboBox 的下拉按钮.
- 再通过image属性设置图片.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString style = "";
style += "QComboBox::down-arrow{image:url(:/down.png)}";
a.setStyleSheet(style);
Widget w;
w.show();
return a.exec();
}
4)执行程序,观察效果
1.4.3伪类选择器(Pseudo-States)
伪类选择器,是根据控件所处的某个状态被选择的.例如按钮被按下,输入框获取到焦点,鼠标移动到某个控件上等.
- 当状态具备时,控件被选中,样式生效.
- 当状态不具备时, 控件不被选中,样式失效.
使用:
的方式定义伪类选择器.
常用的伪类选择器
这些状态可以使用!来取反.比如: !hover
就是鼠标离开控件时, : !pressed
就是鼠标松开时,
等等.
代码示例:设置按钮的伪类样式.
1)在界面上创建一个按钮
2)编写main.cpp,创建全局样式
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString style = "";
style += "QPushButton{color:red;}";
style += "QPushButton:hover{color:green;}";
style += "QPushButton:pressed{color:blue;}";
a.setStyleSheet(style);
Widget w;
w.show();
return a.exec();
}
3)运行程序,可以看到,默认情况下按钮文字是红色,鼠标移动上去是绿色,鼠标按下按钮是蓝色.
1.5 样式属性
QSS中的样式属性非常多,不需要都记住.核心原则还是用到了就去查.
大部分的属性和CSS是非常相似的.
文档的
Qt Style Sheets Reference
章节详细介绍了哪些控件可以设置属性,每个控件都能设置哪些属性等.
相关的代码示例,在后面具体介绍.
在翻阅文档的时候涉及到一个关键术语"盒模型" (Box Model).这里我们需要介绍一下.
1.5.1 盒模型(Box Model)
在文档的Customizing Qt Widgets Using Style Sheets
的The Box Model
章节介绍了盒模型.
一个遵守盒模型的控件,由上述几个部分构成.
- Content矩形区域:存放控件内容.比如包含的文本/图标等.
- Border矩形区域:控件的边框.
- Padding矩形区域:内边距.边框和内容之间的距离.
- Margin矩形区域:外边距.边框到控件geometry 返回的矩形边界的距离
默认情况下,外边距,内边距,边框宽度都是0.
可以通过一-些QSS属性来设置上述的边距和边框的样式.
代码示例:设置边框和内边距
1)在界面上创建一个label
2)修改main.cpp,设置全局样式
border: 5px solid red
相当于border-style: solid;border-width: 5px;border-color: red;
三个属性的简写形式.padding-left: 10px;
是给左侧设置内边距.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyleSheet("QLabel{border:10px solid red;padding-left:10px;}");
Widget w;
w.show();
return a.exec();
}
代码示例:设置外边距
为了方便确定控件位置,演示外边距效果,我们使用代码创建一-个按钮.
1)修改widget.cpp,创建按钮,并设置样式.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* btn = new QPushButton(this);
btn->setGeometry(0,0,100,100);
btn->setText("hello");
btn->setStyleSheet("QPushButton{border:5px solid red;margin:20px;}");
const QRect& rect = btn->geometry();
qDebug() << rect;
}
2)运行程序,可以看到,当前按钮的边框被外边距挤的缩小了.但是获取到的按钮的Geometry 是不变的.
1.6控件样式示例
下面给出一些常用控件的样式示例.
1.6.1按钮
代码示例:自定义按钮
1)界面上创建一个按钮
2)右键->改变样式表,使用Qt Designer设置样式
QPushButton{
font-size:20px;
border:2px solid #8f8f91;
border-radius:20px;
background-color:#dadbde
}
QPushButton:pressed{
background-color:#f6f7fa
}
3)执行程序,可以看到效果
点击按钮前
点击按钮后
属性小结
形如#dadbde是计算机中通过十六进制表示颜色的方式. 这里在"常用控件"章节已经介绍过了.此处不再赘述.
另外,在实际开发中,颜色具体使用哪种好看,是需要一定的"艺术细菌"的.往往公司会有专门 的美工/设计人员来负责.程序员只需要按照设计稿实现程序即可.
1.6.2 复选框
代码示例:自定义复选框
1)创建一个resource.qrc文件,并导入以下图片.
图片可以去阿里巴巴矢量图标库下载
使用黑色作为默认形态.
使用蓝色作为hover形态.
使用红色作为pressed形态.
注意这里的文件命名.
使用阿里矢量图标库,可以下载到上述图片. 下载的时候可以手动选择颜色.
2)创建一个复选框
3)编辑复选框的样式
QCheckBox{font:20px;}
QCheckBox::indicator{
width:20px;
height:20px;
}
QCheckBox::indicator:unchecked{
image:url(:/checkbox-unchecked.png)
}
QCheckBox::indicator:unchecked:hover{
image:url(:/checkbox-unchecked_hover.png)
}
QCheckBox::indicator:unchecked:pressed{
image:url(:/checkbox-unchecked_pressed.png)
}
QCheckBox::indicator:checked{
image:url(:/checkbox-checked.png)
}
QCheckBox::indicator:checked:hover{
image:url(:/checkbox-checked_hover.png)
}
QCheckBox::indicator:checked:pressed{
image:url(:/checkbox-checked_pressed.png)
}
4)运行程序,可以看到此时的复选框就变的丰富起来了.
1.6.3 单选框
代码示例:自定义单选框
1)创建resource.qrc文件,并导入以下图片.
图片可以去阿里巴巴矢量图标库下载
使用黑色作为默认形态.
使用蓝色作为hover形态.
使用红色作为pressed形态.
注意这里的文件命名.
2)在界面上创建两个单选按钮
3)在Qt Designer中编写样式
此处为了让所有QRadioButton都能生效,把样式设置在Widget.上了. 并且使用后代选择器选中
了QWidget里面的QRadioButton .
4)运行程序,观察效果
1.6.4输入框
代码示例:自定义单行编辑框
1)在界面上创建一个单行编辑框
2)在Qt Designer中编写样式.
QLineEdit {
border-width: 1px;
border-radius: 10px;
border-color: rgb(58, 58, 58);
border-style: inset;
padding: 0 8px;
color: rgb(255, 255, 255);
background:rgb(100, 100, 100);
selection-background-color: rgb(187, 187, 187);
selection-color: rgb(60, 63, 65);
}
- 执行程序,观察效果
1.6.5 列表
关于qlineargradient
qlineargradient有6个参数.
x1,y1:标注了一个起点.
x2, y2:标注了一个终点.
这两个点描述了一个"方向".
例如:
- x1:0,y1:0, x2:0,y2: 1就是垂直方向从上向下进行颜色渐变.
- x1:0,y1:0, x2: 1,y2:0就是水平方向从左向右进行颜色渐变.
- x1:0,y1:0, x2:1,y2:1就是从左上往右下方向进行颜色渐变. stop0和stop1描述了两个颜色.渐变过程就是从stop0往stop1进行渐变的.
代码示例:自定义列表框
1)在界面上创建一个ListView
- 编写样式表
QListView::item:hover {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #FAFBFE, stop: 1 #DCDEF1);
}
QListView::item:selected {
border: 1px solid #6a6ea9;
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #6a6ea9, stop: 1 #888dd9);
}
3) 执行程序,查看效果
1.6.6 菜单栏
代码示例:自定义菜单栏
1)创建菜单栏
创建项目的时候选择 QMainWindow
创建几个菜单项
2)
编写样式
QMenuBar {
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 lightgray, stop:1 darkgray);
spacing: 6px; /* spacing between menu bar items */
}
QMenuBar::item {
padding: 3px 10px;
background-color: rgb(0, 255, 0);
border-radius: 3px;
}
QMenuBar::item:selected { /* when selected using mouse or keyboard */
background: #a8a8a8;
}
QMenuBar::item:pressed {
background: #888888;
}
QMenu {
background-color: white;
margin: 0 2px; /* some spacing around the menu */
}
QMenu::item {
padding: 2px 25px 2px 20px;
border: 3px solid transparent; /* reserve space for selection border */
}
QMenu::item:selected {
border-color: darkblue;
background: rgba(100, 100, 100, 150);
}
QMenu::separator {
height: 2px;
background: lightblue;
margin-left: 10px;
margin-right: 5px;
}
3)运行程序,查看效果
1.6.7 登录界面
基于上述学习过的QSS样式,制作一个美化版本的登录界面.
1)在界面上创建元素
2)使用布局管理器,把上述元素包裹一下.
- 使用QVBoxLayout 来管理上述控件.
- 两个输入框和按钮的minimumHeight均设置为50. (元素在布局管理器中无法直接设置width和height,使用minimumWidth和minimumHeight代替,此时垂直方向的sizePolicy要设为fixed).
- 右键QCheckBox ,选择Layout Ali gnment可以设置checkbox的对齐方式(左对齐,居中对
齐,右对齐).
3)设置背景图片.
把上述控件添加一个父元素QFrame,并设置QFrame和窗口一样大.
顶层窗口的QWidget无法设置背景图片.因此我们需要再套上一层QFrame.背景图片就设置
到QFrame上即可.
创建resource.qrc,并导入图片
编写QSS样式.
- 使用border-image 设置背景图片,而不是background-image .主要是因为border-
image是可以自动缩放的.这一点在窗口大小发生改变时是非常有意义的.
添加背景
QFrame {border-image:url(:/hexagon.png);}
设置各种样式
QLineEdit{
color:#8d98a1;
background-color:#405361;
padding:0 10px;
font-size:20px;
border-style:none;
border-radius:10px;
}
QCheckBox{
color:white;
background-color:transparent;
}
QPushButton{
font-size:20px;
color:white;
background-color:#555;
border-style:outset;
border-radius:10px;
}
QPushButton:pressed{
color:black;
background-color:#ced1db;
border-style:inset;
}
运行程序,效果如下:
1.7 小结
QSS本身给Qt提供了更丰富的样式设置的能力,但是整体来说QSS的功能是不如CSS的…
在CSS中,整个网页的样式,都是CSS一手负责, CSS功能更强大,并且也更可控.
相比之下,Qt中是以原生api为主,来控制控件之间的尺寸,位置等, QSS只是起到辅助的作用.
而且Qt中提供的一些"组合控件" (像QComboBox, QSpinBox等)内部的结构是不透明的,此时进行一些样式设置也会存在一定的局限性.
另外,做出好看的界面,光靠QSS是不够的.更重要的是需要专业美工做出设计稿.