Qt stylesheet 详解

stylesheet

本文不准备谈样式表的使用。因为Manual中介绍的很清楚了,给的例子也都很不错。再就是我本身对CSS语法不太熟,术语把握不好,qss用的不多。

不过,有两个问题新手似乎特容易迷惑,简单提一下:

QWidget直接派生类的样式表不起作用

典型的表述(之一)是,从QWidget派生一个窗口,使用stylesheet设置背景,在designer中可以看到效果,编译运行后,没有背景。

该怎么办呢?对此Manual中专门有强调,摘录如下:

If you subclass from QWidget, you need to provide a paintEvent for your custom QWidget as below:
 void CustomWidget::paintEvent(QPaintEvent *)
 {
     QStyleOption opt;
     opt.init(this);
     QPainter p(this);
     style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
 }

原因比较简单,QWidget的paintEvent()是空的,而样式表要通过paint被绘制到窗口中。

设置样式表后,其所有的子Widget也都跟着变了

遇到这种问题的,通常都是尝试给窗口这是背景图片,结果发现:所有的子widget也被单独设置背景图片。

这个主要是大家都不熟悉CSS用法导致的,设置样式表对哪些内容有效。是通过选择器进行控制的。

而如果不设置选择器,比如

w.setStyleSheet("background-image: url(:/dbzhang800.png);");

这其实是不符合CSS语法的,会被转换成

w.setStyleSheet("*{background-image: url(:/dbzhang800.png);}");

这样一来,使用了“通用选择器”,所有的子widget也就均被应用了该样式

stylesheet 与 QStyle

简单看看看点二者的异同。

使用

  • 可以对整个应用程序 或 某个widget设置 样式(QStyle)
    • 通过命令行 -style xxx
    • QApplication::setStyle()
    • QWidget::setStyle()
  • 也可以对整个应用程序 或 某个widget设置样式表(stylesheet)
    • 通过命令行 -stylesheet xxx
    • QApplication::setStyleSheet()
    • QWidget::setStyleSheet()

注意点:

  • 如果你对某个widget设置QStyle,那么该样式只对该widget有效,其子widget不受影响;但如果设置样式表,那么所有的子widget会继承该样式表。

关联

无论设置样式还是样式表,都会注意到一个QStyleSheetStyle存在。

当我们为应用程序或widget设置样式表时,Qt会将其样式设置为QStyleSheetStyle

void QWidget::setStyleSheet(const QString& styleSheet)
{
...
    d->extra->styleSheet = styleSheet;
...
    d->setStyle_helper(new QStyleSheetStyle(...), true);
...
}

而在有样式表存在的情况下,通过setStyle()设置的样式时:

void QWidget::setStyle(QStyle *style)
{
...
    d->setStyle_helper(new QStyleSheetStyle(style), true);
}

这时,如果通过style()获取样式,将不是setStyle()中参数指定的那个样式,而是这个QStyleSheetStyle的实例(当然,这二者是有关系的,代理关系)。

QStyleSheetStyle

这是一个私有类,但从名字上可以猜出:

  • 它是QStyle的派生类
  • 和 stylesheet 有关

首先:它是QStyle的派生类,同时构造函数接受另一个QStyle作为参数

class QStyleSheetStyle : public QWindowsStyle
{
    Q_OBJECT
public:
    QStyleSheetStyle(QStyle *baseStyle);
    ~QStyleSheetStyle();
...

其次:它要解析设置在应用程序、它的父对象、或者自身安装的样式表

QVector<QCss::StyleRule> QStyleSheetStyle::styleRules(const QWidget *w) const
{
...
    QStyleSheetStyleSelector styleSelector;
...
    //获取“默认”样式设置,在
    styleSelector.styleSheets += getDefaultStyleSheet();
...
    //获取应用程序的样式设置,注意:如果以file:///开头,则按照文件解析
    if (!qApp->styleSheet().isEmpty()) {
        StyleSheet appSs;
        QString ss = qApp->styleSheet();
        if (ss.startsWith(QLatin1String("file:///")))
                ss.remove(0, 8);
        parser.init(ss, qApp->styleSheet() != ss);
        parser.parse(&appSs)
        styleSelector.styleSheets += appSs;
    }
...
    //依次逐级向上查找各个父窗口安装的样式表,注意,如果样式表没有设置选择器,它会自动设置为 “*”
    QVector<QCss::StyleSheet> widgetSs;
    for (const QWidget *wid = w; wid; wid = parentWidget(wid)) {
        if (wid->styleSheet().isEmpty())
            continue;
        StyleSheet ss;
        parser.init(wid->styleSheet());
        if (!parser.parse(&ss)) {
            parser.init(QLatin1String("* {") + wid->styleSheet() + QLatin1Char('}'));
        parser.parse(&ss);
        widgetSs.append(ss);
    }
    styleSelector.styleSheets += widgetSs;
...
}

这堆东西看起来好繁琐啊,如果widget特别多会不会吃不消?呵呵,没什么问题了,Qt都进行了cache处理,只不过这儿为了清晰起见,没有贴出半点cache的代码。

stylesheet与parent

前面说了,当设置stylesheet时,样式表会传递到其子widget(这是前面代码setStyle_helper的第二个参数为true来进行的)。

但既然是传递,当然和父子关系有关了:

void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
{
...
   d->inheritStyle();
...
}

根据情况,设置新的样式,或者恢复原来的样式:

void QWidgetPrivate::inheritStyle()
{
...
    setStyle_helper(newStyle, true);
...
    setStyle_helper(origStyle, true);
...
}

当然,对一个含有子widget的widget设置样式表时,也会对各个child调用这个函数。

QCss::Parser

qss 的语法分析是由QCss::Parser完成的。而这个东西在Qt的另一个地方也被使用:当在QTextDocument/QTextEdit中使用html时,用来处理<style>标记的内容。

我对CSS不熟,对语法分析也不熟。暂时到此为止了。

QCss::StyleRule 

 x:hover, y:clicked > z:checked 
 { prop1: value1; prop2: value2; }  

QVector<QCss::Selector>  

 x:hover, y:clicked z:checked 

QVector<QCss::Declaration>  

 { prop1: value1; prop2: value2; }  

QCss::Declaration  

 prop1: value1;  

参考

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值