第二十一章:QT样式与美化——QSS语法、选择器、伪状态、自定义控件样式、QPalette、QStyle

说实话,很多做QT开发的朋友,一开始都觉得样式美化是「花架子」。我当年也这么想——功能跑通不就行了?直到我第一次给一个工业软件做界面,客户说「你这界面看着像上世纪的产品」……嗯,从那以后,我再也不敢轻视QT的美化能力了。

这一章,咱们就聊聊QT里那些让你界面「脱胎换骨」的东西。说白了,就是怎么让控件变得好看、统一、有质感。

21.1 QSS是什么?跟CSS啥关系?

QSS,全称是Qt Style Sheets。你如果写过网页,看到CSS一定不陌生。QSS的语法、选择器、属性,几乎就是CSS的子集。我刚开始学的时候,甚至直接把网页上的CSS粘过来改两下就能用。

但注意,QSS不是万能的。它只支持一部分CSS属性,比如背景色、边框、字体这些。像flex布局、动画这些,就别想了。

核心思想:QSS让你用声明式的方式,描述控件长什么样。你不需要写一行绘制代码,就能让按钮变圆角、让列表变透明。

21.2 QSS语法基础

QSS的语法很简单:

选择器 {
    属性名: 属性值;
    属性名: 属性值;
}

举个例子:

QPushButton {
    background-color: #3498db;
    color: white;
    border-radius: 8px;
    padding: 6px 12px;
}

这段代码会让所有QPushButton变成蓝色背景、白色文字、圆角8像素。我在项目里经常用这种写法快速统一风格,省得每个按钮单独设样式。

小技巧:QSS里颜色可以用十六进制(#RRGGBB)、rgb(r,g,b)、或者颜色名字(如"red")。我个人习惯用十六进制,因为精确好记。

21.3 选择器——精准定位控件

选择器决定了你的样式「打在谁身上」。QSS支持好几种选择器,我挑最常用的说:

选择器类型 写法 作用范围
类型选择器 QPushButton 所有QPushButton实例
类选择器 .QPushButton QPushButton及其子类实例
ID选择器 #myButton objectName为"myButton"的控件
后代选择器 QDialog QPushButton QDialog内部的所有QPushButton
子选择器 QDialog > QPushButton QDialog的直接子控件QPushButton

我记得有一次,一个对话框里有两个按钮,一个「确定」一个「取消」。我想让「确定」变绿色,「取消」变红色。用ID选择器就特别方便:

#btnOk {
    background-color: #27ae60;
}
#btnCancel {
    background-color: #e74c3c;
}

你只需要在代码里给按钮设置objectName:

ui->btnOk->setObjectName("btnOk");
ui->btnCancel->setObjectName("btnCancel");

21.4 伪状态——控件的「情绪」

伪状态,说白了就是控件在不同交互状态下的样式。比如鼠标悬停、按下、禁用等等。写法是用冒号跟在选择器后面:

QPushButton:hover {
    background-color: #2980b9;
}
QPushButton:pressed {
    background-color: #1c6ea4;
}
QPushButton:disabled {
    background-color: #bdc3c7;
    color: #7f8c8d;
}

你想想看,一个按钮按下去没有任何反馈,用户会觉得很「死」。加上伪状态,交互感立马就出来了。

注意:伪状态可以组合使用,比如 QPushButton:hover:!enabled 表示「禁用但鼠标悬停」的状态。但别写太复杂,容易把自己绕晕。我曾经在一个项目里写了五层伪状态嵌套,后来自己都看不懂了……

21.5 自定义控件样式——从「能用」到「好看」

有时候,光改颜色和边框不够。比如你想做一个自定义的开关按钮、一个带图标的进度条。这时候就需要用到QSS的「子控件」和「盒模型」。

QSS把每个控件看作一个盒子,从外到内依次是:margin、border、padding、content。你可以分别控制每一层:

QProgressBar {
    border: 2px solid #3498db;
    border-radius: 6px;
    background-color: #ecf0f1;
    height: 20px;
    text-align: center;
}
QProgressBar::chunk {
    background-color: #3498db;
    border-radius: 4px;
}

这里 ::chunk 就是QProgressBar的子控件,代表进度条填充的那一块。通过分别设置父控件和子控件,你能做出各种花样的进度条。

我在做播放器项目时,就用这个技巧做了一个圆形的音量滑块,效果比默认的好看太多了。

21.6 QPalette——另一种调色方式

QSS虽然强大,但有些场景下用QPalette更方便。QPalette是QT提供的一套调色板,你不需要写样式字符串,直接设置颜色角色:

QPalette palette;
palette.setColor(QPalette::Window, QColor(240, 240, 240));
palette.setColor(QPalette::Button, QColor(52, 152, 219));
palette.setColor(QPalette::ButtonText, Qt::white);
this->setPalette(palette);

QPalette适合做全局的、统一的颜色调整。比如整个应用换主题色,用QPalette就比写一堆QSS要快得多。

我的建议:全局颜色用QPalette,局部精细样式用QSS。两者不冲突,可以混用。但注意QSS的优先级高于QPalette,如果同时设置了,QSS会覆盖调色板。

21.7 QStyle——深入底层的绘制引擎

QStyle是QT样式系统的底层机制。QSS和QPalette最终都是通过QStyle来绘制控件的。如果你需要完全自定义一个控件的绘制逻辑,可以继承QProxyStyle或QCommonStyle,重写绘制函数。

举个例子,我想让所有QPushButton在绘制时加一个渐变背景:

class MyStyle : public QProxyStyle {
public:
    void drawControl(ControlElement element, const QStyleOption* option,
                     QPainter* painter, const QWidget* widget) const override {
        if (element == CE_PushButton) {
            // 自定义绘制逻辑
            QLinearGradient gradient(0, 0, 0, option->rect.height());
            gradient.setColorAt(0, QColor(52, 152, 219));
            gradient.setColorAt(1, QColor(41, 128, 185));
            painter->fillRect(option->rect, gradient);
        } else {
            QProxyStyle::drawControl(element, option, painter, widget);
        }
    }
};

然后全局应用:

qApp->setStyle(new MyStyle);

说实话,QStyle这条路比较深,一般项目用不到。但如果你要做高度定制化的UI,比如游戏界面、专业工具,那就绕不开它了。

21.8 知识体系总览

下面这张图,帮你理清QT样式系统的整体脉络:

QT样式系统知识体系 QT样式系统 QSS样式表 QPalette调色板 QStyle引擎 QSS核心要素 • 选择器(类型/ID/后代) • 伪状态(hover/pressed) • 子控件(::chunk等) QPalette角色 • Window / WindowText • Button / ButtonText • Base / Text / Highlight QStyle扩展 • QProxyStyle继承 • drawControl重写 • 自定义绘制逻辑 优先级:QStyle > QSS > QPalette 实际开发中三者配合使用,各取所长 图:QT样式系统三大分支及核心内容

21.9 避坑指南

最后,分享几个我踩过的坑:

  • QSS不生效? 检查一下控件有没有设置objectName,ID选择器依赖这个。我曾经花了一小时找bug,结果发现是名字拼错了。
  • 伪状态不触发? 有些控件默认不支持某些伪状态,比如QLabel不支持hover。你需要先设置 setAttribute(Qt::WA_Hover, true)
  • QPalette改了没反应? 看看是不是有QSS覆盖了。QSS优先级高,会「屏蔽」调色板的设置。
  • 自定义QStyle太复杂? 先试试QSS能不能搞定。能用QSS解决的问题,别硬上QStyle。杀鸡不用牛刀。

嗯,这一章的内容就到这儿。QT样式系统说深也深,说浅也浅。关键是多动手试,多看看别人做的界面,慢慢就有感觉了。


公众号:蓝海资料掘金营,微信deep3321