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 |
| QVector<QCss::Selector> | x:hover,y:clickedz:checked |
| QVector<QCss::Declaration> | {prop1:value1;prop2:value2;} |
| QCss::Declaration | prop1:value1; |
本文深入探讨了在Qt中设置样式表时遇到的问题,特别是QWidget派生类样式表不起作用的原因及解决方案,同时介绍了CSS选择器在样式表应用中的作用。文章还对比了设置样式与样式表的不同之处,并解释了QStyleSheetStyle类的作用。
969

被折叠的 条评论
为什么被折叠?



