目录
QSS
在网页前端开发中,CSS是一个很重要的部分,用来描述一个网页的样式,从而起到对网页的美化作用,而 Qt 作为客户端的GUI开发,界面好看也是一个很重要的因素。
Qt 仿照 CSS 的模式引入了 QSS 对控件做出样式上的设定,从而使界面更好看。
Qt 与网页不一样,因此 QSS 中只能支持部分 CSS 属性。
【注意】:有时候同时通过 QSS 和 C++代码设置的样式,两者出现冲突,那么 QSS 的优先级更高。
基本语法
这是CSS的语法结构:
选择器 { 属性名: 属性值; }
QSS 沿用了这种设定:
- 选择器:哪个widget要应用样式规则。
- 属性:一个键值对,属性名表示要设置哪种样式,属性值表示要设置的值。
QSS 设置方式
指定控件样式设置
写一段简单的代码演示一下。
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); ui->pushButton->setStyleSheet("QPushButton { color: #0000ff; }"); // 将按钮的字体设置为蓝色 }
可以看到只有一个按钮的字体被设置成蓝色了,所以这样设置只能对当前选择的控件和其子控件进行设置,什么是子控件呢,我们换一种方式。
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); this->setStyleSheet("QPushButton { color: #0000ff; }"); // 将按钮的字体设置为蓝色 }
这里使用this只设置了按钮的字体颜色,两个按钮都是this的子控件,所以都被设置成了蓝色,如果不是QPushButton,那就不会生效。
全局样式设置
全局的样式设置通过main.cpp文件中的 QApplication 的setStyleSheet 方法设置整个程序的全局样式。
int main(int argc, char *argv[]) { QApplication a(argc, argv); a.setStyleSheet("QPushButton { color: red; }"); Widget w; w.show(); return a.exec(); }
这样就设置了全局样式,如果此时再将第一个按钮的字体设置成蓝色,并且把字体也设置为50px会怎么样呢?
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); ui->pushButton->setStyleSheet("QPushButton { color: #0000ff; \ font-size: 50px; }"); }
此时这两个样式会叠加起来,这就是层叠性,CSS 为层叠样式表,QSS 也继承了层叠性。
而且上面的示例也表明了如果出现全局和指定样式冲突,还是局部的样式优先级更高。
文件加载样式表
上述的代码都是 QSS代码 和 C++代码 混在一起的,如果 QSS 样式代码很简单,那就无所谓了,那要是 QSS 样式代码很复杂,那代码的维护成本就很高,也可以把 QSS 代码单独放在一个文件中,从文件中加载样式表,如何操作呢,还是使用qrc文件。
创建好qrc文件后,就添加一个文件夹,里面创建一个文件,并修改一下文件名,后缀为qss。
创建好后就在文件中编辑一些控件的样式。
编写好后就返回main.cpp文件,通过QFile打开这个文件。
#include <QFile> QString loadQSS() { QFile file(":/QSS/style.qss"); file.open(QFile::ReadOnly); QString style = file.readAll(); file.close(); return style; } int main(int argc, char *argv[]) { QApplication a(argc, argv); // 设置全局样式 a.setStyleSheet(loadQSS()); Widget w; w.show(); return a.exec(); }
通过这样的方式也可以设置控件的样式,让 QSS代码 和 C++代码 进行了解耦,但是这种方式也仅供参考,下面还有一种方式。
Qt Designer 编辑样式
前面我们也简单提到过这种方式,在 Qt Designer 中,右击控件就可以编辑样式,然后就会把代码添加到ui文件中。
当我们编辑完qss代码后,左下角也可以帮我们简单检查这个代码有没有问题。
并且可以实时看到界面的变化,在ui文件中就会多了一段代码。
选择器
在这里给出 QSS 几种常用的选择器:
选择器 示例 说明 类型选择器 QPushButton { } 选择所有的 QPushButton控件 及其子控件。 类选择器 .QPushButton { } 选择所有的 QPushButton控件,不包括子类。 ID 选择器 #pushButton { } 选择 objectName 为 pushButton 的控件。 并集选择器 QPushButton, QLineEdit { } 选择这几种,改变这几种控件的样式。
简单演示一下。
第一个设置了所有的QPushButton及其子类控件的颜色和字体,后面单独设置了下面两个控件的颜色,冲突的还是局部样式的优先级更高,不冲突的样式就会叠加。
再看一下并集选择器。
而且并集选择中既可以写控件名,也可以写objectName。
子控件选择器
有些控件会包含一些子控件,比如 QSpinBox 的微调按钮和 QComboBox 的下拉框按钮。
这些控件也是可以改变样式的,通过子控件选择器::,可以针对上述子控件进行样式设置,可以在手册中的这里查找。
int main(int argc, char *argv[]) { QApplication a(argc, argv); QString style = "QComboBox::down-arrow { image: url(:/image/down.png)}"; style += "QSpinBox::down-button { image: url(:/image/down.png)}"; style += "QSpinBox::up-button { image: url(:/image/up.png)}"; a.setStyleSheet(style); Widget w; w.show(); return a.exec(); }
伪类选择器
伪类选择器是根据控件所处的某个状态被选择的,例如按钮被按下,输入框获取焦点,鼠标移动到某个控件上。
- 当状态具备时,控件被选中,样式生效。
- 当状态不具备时,控件不被选中,样式失效。
常用的伪类选择器:
伪类选择器 说明 :hover 鼠标放到控件上 :pressed鼠标左键按下时 :focus获取输入焦点时 :enabled元素处于可用状态时 :checked被勾选时 :read-only元素为只读状态时
这些状态可以使用 ! 来取反,比如 :!hover 为鼠标离开控件时,:!pressed就是鼠标松开时,想要知道更多的伪类选择器就要查看文档了。
现在就可以简单的设置一下。
设置好后,运行程序就可以发现,按钮初始时红色,当鼠标移动到控件上时变成了绿色,当按下不松开时变成了蓝色。
样式属性
QSS的属性非常多,也不需要刻意去记,用到去查文档就可以,下面我们主要提到一个术语为“盒模型”(Box Model)。
盒模型
在文档中也介绍了盒模型。
- Content 区域:存放控件内容,包含文件的文本、图标等。
- Border 框:控件的边框。
- Padding 区域:内边距,边框和内容的距离。
- Margin 区域:外边距,边框到控件geometry返回的矩形边界距离。
默认情况下,外边距、内边距和边框宽度都是0。也可以通过 QSS 来设置这些样式。
QSS 属性 说明 margin设置四个外边距 padding设置四个内边距 border-style设置边框样式 border-width边框的粗细 border-color边框的颜色 border相当于上面三个属性集合,顺序为:border-width border-style border-color
// margin 可以拆成4个属性 margin-top: margin-right: margin-bottom: margin-left: // 设置方式 margin: 10px; // 四个方向都是10px外边距 margin: 10px 20px; // 上下都是10px,左右都是20px margin: 10px 20px 30px 40px; // 上右下左(顺时针)方向依次设置 // padding 也是这样的
设置 Label 的边距为5px,样式为solid实线,颜色为红色。外边距的设置也是差不多的。
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); QPushButton* button = new QPushButton("按钮", this); button->setGeometry(0, 0, 100, 100); button->setStyleSheet("QPushButton{ border: 5px solid red; margin: 20px }"); }
控件样式
下面我们就来看一看常见的控件的样式。
按钮
这个按钮设置了font-size字体大小,border边框,还有border-radius切角矩形,这个值越大,角就越圆,还有背景颜色,颜色可以直接点击添加颜色选择,会直接生成一个rgb(255, 255, 255)样式的值。
复选框
属性 说明 ::indicator 选中 checkbox 中的对钩部分 :hover选中 鼠标进入 的状态 :pressed选中 鼠标按下 的状态 :checked选中 checkBox 被选中的状态 :unchecked选中 checkBox 未被选中的状态 width设置子控件宽度 height设置子控件高度 image设置子控件的图片
可以自己找一些图片,我们想要实现的功能是:黑色复选框为默认情况下的样式,蓝色复选框为鼠标进入的样式,红色复选框为鼠标点击时的样式。
QCheckBox{ font-size: 20px; } QCheckBox::indicator{ width: 20px; height: 20px; } QCheckBox::indicator:unchecked{ image: url(:/image/checkBox-unchecked.png); } QCheckBox::indicator:checked{ image: url(:/image/checkBox-checked.png) } QCheckBox::indicator:unchecked:hover{ image: url(:/image/checkBox-hover_unchecked.png) } QCheckBox::indicator:checked:hover{ image: url(:/image/checkBox-hover_checked.png) } QCheckBox::indicator:unchecked:pressed{ image: url(:/image/checkBox-pressed_unchecked.png) } QCheckBox::indicator:checked:pressed{ image: url(:/image/checkBox-pressed_checked.png) }
单选框
这里的操作和复选框差不多,都是在不同的选择器下有不同的样式。
QRadioButton{ font-size: 30px; } QRadioButton::indicator{ width: 30px; height: 30px; } QRadioButton::indicator:unchecked{ image: url(:/image/radioButton-unchecked.png) } QRadioButton::indicator:unchecked:hover{ image: url(:/image/radioButton-unchecked-hover.png) } QRadioButton::indicator:unchecked:pressed{ image: url(:/image/radioButton-unchecked-pressed.png) } QRadioButton::indicator:checked{ image: url(:/image/radioButton-checked.png) } QRadioButton::indicator:checked:hover{ image: url(:/image/radioButton-checked-hover.png) } QRadioButton::indicator:checked:pressed{ image: url(:/image/radioButton-checked-pressed.png) }
输入框
我们也可以根据上述内容改变输入框的样式。
属性 说明 border-width设置边框宽度 border-radius设置边框圆角 border-color设置边框颜色 border-style设置边框风格 padding设置内边距 color设置文字颜色 background设置背景颜色 selection-background-color设置选中文字的背景颜色 selection-color设置选中文字的文本颜色
QLineEdit{ padding-left: 20px; /*左边距*/ font: 16pt "黑体"; /*字体*/ border: 2px solid rgb(125, 182, 255); /*设置边框*/ border-radius: 20px; /*设置圆角*/ background: rgb(233, 255, 151); /*设置背景*/ color: rgb(125, 182, 255); /*设置字体颜色*/ selection-background-color: rgb(125, 182, 255); /*设置选中内容后的背景颜色*/ selection-color: rgb(233, 255, 151); /*设置选中字体的颜色*/ }
列表
属性 说明 ::item 选中 QListWidget 中具体的条目。 :hover 选中 鼠标悬停的条目 :selected 选中 被选中的条目 background 设置背景颜色 border 设置边框 qlineargradient设置渐变色
可以给 QListWidget 设置一些样式,这里就随便设置一下,与上述操作都差不多。
QListWidget::item:hover{ /* 当鼠标进入选项时的样式 */ background: rgb(125, 182, 255); color: rgb(233, 255, 151); } QListWidget::item:selected{ /* 当鼠标点击选项后的样式 */ background: rgb(116, 202, 255); color: rgb(233, 255, 151); } QListWidget{ font: 16pt "黑体"; border: 2px solid rgb(125, 182, 255); background-color: rgb(233, 255, 151); border-radius: 20px; padding: 10px 10px 10px 10px; color: rgb(125, 182, 255); }
下面再来介绍一下如何设置渐变色,使用的属性是qlineargradient,这个属性要填写六个参数:
- 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 进行渐变。
下面我们就可以给列表的背景和选项设置渐变色,但是专业的事还是要交给专业的人,配色这种事情还是应该交个专业的人,这里就随便选几个颜色设置一下。
QListWidget::item:hover{ /* 当鼠标进入选项时的样式 */ background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(233, 255, 151), stop:1 rgb(125, 182, 255)); color: rgb(233, 255, 151); } QListWidget::item:selected{ /* 当鼠标点击选项后的样式 */ background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(233, 255, 151), stop:1 rgb(125, 182, 255)); color: rgb(233, 255, 151); } QListWidget{ font: 16pt "黑体"; border: 2px solid rgb(125, 182, 255); border-radius: 20px; padding: 10px 10px 10px 10px; color: rgb(125, 182, 255); background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 rgb(233, 255, 151), stop:1 rgb(131, 255, 183)) }
菜单栏
Qt 也支持对菜单栏进行样式设置。
属性 说明 QMenuBar::item选中菜单栏的元素 QMenuBar::item:selected选中菜单栏中被选中的元素 QMenuBar::item:pressed选中菜单栏中鼠标点击的元素 QMenu::item选中菜单中的元素 QMenu::item:selected选中菜单中被选中的元素 QMenu::separator选中菜单中的分割线
先在菜单栏中加入一些内容。
之后就是设置一些样式,我也是仿照Qt Creater中的样式做的。
QMenuBar { background-color: rgb(220, 220, 220); } QMenuBar::item:selected { background-color: rgb(94, 95, 96); color: white; } QMenuBar::item:pressed { background-color: rgb(94, 95, 96); color: white; } QMenu::item:selected { border: 2px solid black; /* 为选中的菜单项添加一个框 */ } QMenu::item{ border: 2px solid transparent; /* 边框颜色设置为跟随父元素,文字就不会在选中的时候动了 */ }
登录界面
现在我们就可以设置一个界面优美的登录界面,先放上几个控件,使用垂直布局管理器,但是控件在垂直方向上的sizePolicy是Fixed(固定的),而且geometry也失效了,所以可以设置maximumSize和minimumSize设置一下高度,这样就是固定的了。
设置好后,我们可以为登录界面添加一个背景,但是在顶层窗口设置背景图是会失效的,所以就要给控件外面套上一个和窗口一样大小的 QFrame 控件,这个控件也是在左边的控件栏中可以找到的,之后把这个QFrame放大一下,把登录的控件拖进去。
下面就可以设置背景了,QFrame中有两个设置背景的方式,一个是background-image,另一个是border-image,后者可以跟随控件的大小发生变化,而前者就是固定的,所以我们使用后者就可以了。后面就是自己设计一个界面。
QFrame { border-image: url(:/image/login_background.jpg); } QLineEdit { border: none; padding: 0px 10; font-size: 20px; border-radius: 10px; } QCheckBox { font-size: 18px; } QPushButton { background-color: qlineargradient(x1:0 y1:0 x2:1 y2:0, stop:0 rgb(172, 187, 246), stop:1 rgb(239, 133, 143)); border-radius: 10px; font-size: 20px; color: white; } QPushButton:pressed { background-color: qlineargradient(x1:0 y1:0 x2:1 y2:0, stop:0 rgb(152, 167, 226), stop:1 rgb(219, 113, 123)); }
绘图
概念
Qt 已经内置了很多控件,但是不能保证现有的控件可以应对所有场景,Qt 提供了绘画相关的 API,可以在窗口中绘制任意图形,下面是绘画 API 核心类。
类 说明 QPainter“绘画者”,用来绘图的对象,提供了一系列draw方法,可以绘制各种图形。 QPaintDevice“画板”,描述 QPainter 把图形画到哪个对象上,QWidget 也是一种 QPaintDevice。 QPen“画笔”,描述 QPainter 画出来的线是什么样的。 QBrush“画刷”,描述 QPainter 填充一个区域是什么样的。
绘图 API 的使用一般不会再构造函数中,而是在 Qt 提供的 paintEvent 事件处理函数中调用。
关于 paintEvent 事件,它会在以下情况下被触发:
- 控件首次创建
- 控件被遮挡,再解除遮挡
- 窗口最小化,再恢复
- 控件大小发生变化时
- 主动调用 repaint() 或者 update() 方法
绘制形状
绘制线段
使用的是下面这个方法:
void drawLine (const QPoint &p1, const QPoint &p2); void drawLine (int x1, int y1, int x2, int y2);
第一个参数p1为绘制起点,p2为绘制终点;下面的就和设置渐变色是一样的。
事件的处理操作我们也知道了,就是重写父类的 paintEvent 方法。
void Widget::paintEvent(QPaintEvent *event) { // 绘制 QPainter painter(this); // 画横线线 painter.drawLine(QPoint(20, 20), QPoint(100, 20)); // 画竖线 painter.drawLine(20, 20, 20, 100); // 画斜线 painter.drawLine(20, 20, 100, 100); }
绘制矩形
下面是使用的方法,参数也很好理解。
void QPainter::drawRect (int x, int y, int width, int height);
void Widget::paintEvent(QPaintEvent *event) { // 绘制 QPainter painter(this); // 画矩形 painter.drawRect(20, 110, 200, 100); }
绘制圆形
下面是绘制圆形的方法:
void QPainter::drawEllipse (const QPoint ¢er, int rx, int ry);
第一个参数为中心点,最后两个参数为横坐标半径和纵坐标半径。
void Widget::paintEvent(QPaintEvent *event) { // 绘制 QPainter painter(this); // 画圆形 painter.drawEllipse(QPoint(120, 310), 100, 100); }
上面是后面两个参数是一样的,横纵的半径是一样的,如果不一样会怎么样呢?也都能猜出来,绘画出一个椭圆。
绘制文本
QPainter类 中还提供了可以绘制文字的方法,第一个参数为横坐标,表示文字最左侧的位置,第二个参数为 纵坐标,表示的是文字的“基线位置”(baseline),所白了就是英语本四线三格中,从下往上数第二条线。
还可以给painter设置一个font,这样在绘制文本的时候就可以使用设置的QFont。
除了设置字体外,还可以设置颜色。
设置画笔
QPainter 在绘制的时候是有一个默认画笔的,我们也可以自定义画笔,使用的就是QPen类,它可以设置线宽、颜色、样式、画刷等。
设置画笔颜⾊:QPen::QPen(const QColor &color) 画笔的颜⾊主要是通过 QColor 类设置 设置画笔宽度:void QPen::setWidth(int width) 设置画笔⻛格:void QPen::setStyle(Qt::PenStyle style)
下面是文档中关于画笔样式的部分。
void Widget::paintEvent(QPaintEvent *event) { // 绘制 QPainter painter(this); // 设置画笔 QPen pen(QColor(255, 0, 0)); // 设置成红色 // 设置线条粗细 pen.setWidth(5); // 设置线条风格 pen.setStyle(Qt::SolidLine); // 将自定义的pen设置进painter painter.setPen(pen); painter.drawEllipse(QPoint(400, 300), 100, 100); }
设置画刷
在 Qt 中,画刷是使用 QBrush类 来描述的,多用来填充,QBrush 定义了 QPainter 的填充模式,具有演示、颜色、渐变以及纹理等属性。
设置画刷主要通过 void QPen::setBrush(const QBrush &brush) ⽅法,其参数为画刷的格式
void Widget::paintEvent(QPaintEvent *event) { // 绘制 QPainter painter(this); // 设置画笔 QPen pen(QColor(255, 0, 0)); // 设置成红色 // 设置线条粗细 pen.setWidth(5); // 设置线条风格 pen.setStyle(Qt::DashLine); // 将自定义的pen设置进painter painter.setPen(pen); // 创建QBrush对象 QBrush brush; brush.setColor(QColor(0, 0, 255)); // 设置brush样式 brush.setStyle(Qt::SolidPattern); // 实心的样式 // 将自定义的brush设置进painter painter.setBrush(brush); painter.drawEllipse(QPoint(400, 300), 100, 100); }
绘制图片
Qt 提供了四个类来处理图像数据:QImage、QPixmap、QBitmap 和 QPicture,它们都是常用的绘图设备。
- QImage主要用来进行 I/O 处理,它对 I/O 处理操作进行了优化,而且可以用来直接访问和操作像素;
- QPixmap 主要用来在屏幕上显示图像,它对在屏幕上显示图像进行了优化;
- QBitmap 是 QPixmap 的子类,用来处理颜色深度为1的图像,即只能显示黑白两种颜色;
- QPicture 用来记录并重演 QPainter 命令。
绘制简单图片
我们依旧要重写paintEvent方法。
void Widget::paintEvent(QPaintEvent *event) { QPainter painter(this); QPixmap pixmap(":/image/login_background.jpg"); painter.drawPixmap(0, 0, 800, 600, pixmap); // 前两个参数为起点;中间两个参数为图片大小,可以设置这两个参数实现图片的缩放 }
旋转图片
图片的旋转使用的是 QPainter类 中的 rotate方法,默认以原点为中心进行旋转。想要改变旋转的中心可以使用translate方法。
void Widget::paintEvent(QPaintEvent *event) { QPainter painter(this); QPixmap pixmap(":/image/login_background.jpg"); // 旋转图片本质上是把 QPainter 对象进行旋转 painter.rotate(180); painter.translate(-800, -600); // 这个方法就是改变坐标的原点 painter.drawPixmap(0, 0, 800, 600, pixmap); }
通过上述操作,不管是平移还是旋转,还有放大缩小都是可以实现的。
Qt 到这里就要告一段落了,通过学习了 Qt 也让我收获了很多,所以继续学习吧。