您的位置:首页 > 编程语言 > Qt开发

Qt stylesheet 详解

2015-12-19 11:34 369 查看


stylesheet

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

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



[title2]QWidget直接派生类的样式表不起作用[/title2]

典型的表述(之一)是,从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被绘制到窗口中。



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

遇到这种问题的,通常都是尝试给窗口这是背景图片,结果发现:所有的子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;



参考

http://doc.qt.nokia.com/4.7/stylesheet.html

http://doc.qt.nokia.com/4.7/stylesheet-syntax.html

http://labs.qt.nokia.com/2006/06/30/qt-42-technology-preview/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: