样式解析原始文章,此处私人备份
qt 不同版本可能有差异
void QApplication::setStyleSheet(const QString& styleSheet)
{QApplicationPrivate::styleSheet = styleSheet;QStyleSheetStyle *proxy = qobject_cast(QApplicationPrivate::app_style);if (styleSheet.isEmpty()) { // application style sheet removedif (!proxy)return; // there was no stylesheet beforesetStyle(proxy->base);} else if (proxy) { // style sheet update, just repolishproxy->repolish(qApp);} else { // stylesheet set the first timeQStyleSheetStyle *newProxy = new QStyleSheetStyle(QApplicationPrivate::app_style);QApplicationPrivate::app_style->setParent(newProxy);setStyle(newProxy);}
}
void QApplication::setStyle(QStyle *style)
{if (!style || style == QApplicationPrivate::app_style)return;QWidgetList all = allWidgets();// clean up the old styleif (QApplicationPrivate::app_style) {if (QApplicationPrivate::is_app_running && !QApplicationPrivate::is_app_closing) {for (QWidgetList::ConstIterator it = all.constBegin(), cend = all.constEnd(); it != cend; ++it) {QWidget *w = *it;if (!(w->windowType() == Qt::Desktop) && // except desktopw->testAttribute(Qt::WA_WState_Polished)) { // has been polishedQApplicationPrivate::app_style->unpolish(w);}}}QApplicationPrivate::app_style->unpolish(qApp);}QStyle *old = QApplicationPrivate::app_style; // saveQApplicationPrivate::overrides_native_style =nativeStyleClassName() == QByteArray(style->metaObject()->className());#ifndef QT_NO_STYLE_STYLESHEETif (!QApplicationPrivate::styleSheet.isEmpty() && !qobject_cast(style)) {// we have a stylesheet already and a new style is being setQStyleSheetStyle *newProxy = new QStyleSheetStyle(style);style->setParent(newProxy);QApplicationPrivate::app_style = newProxy;} else
#endif // QT_NO_STYLE_STYLESHEETQApplicationPrivate::app_style = style;QApplicationPrivate::app_style->setParent(qApp); // take ownership// take care of possible palette requirements of certain gui// styles. Do it before polishing the application since the style// might call QApplication::setPalette() itselfif (QApplicationPrivate::set_pal) {QApplication::setPalette(*QApplicationPrivate::set_pal);} else if (QApplicationPrivate::sys_pal) {clearSystemPalette();initSystemPalette();QApplicationPrivate::initializeWidgetPaletteHash();QApplicationPrivate::initializeWidgetFontHash();QApplicationPrivate::setPalette_helper(*QApplicationPrivate::sys_pal, /*className=*/, /*clearWidgetPaletteHash=*/false);} else if (!QApplicationPrivate::sys_pal) {// Initialize the sys_pal if it hasn't happened yet...QApplicationPrivate::setSystemPalette(QApplicationPrivate::app_style->standardPalette());}// initialize the application with the new styleQApplicationPrivate::app_style->polish(qApp);// re-polish existing widgets if necessaryif (QApplicationPrivate::is_app_running && !QApplicationPrivate::is_app_closing) {for (QWidgetList::ConstIterator it = all.constBegin(), cend = all.constEnd(); it != cend; ++it) {QWidget *w = *it;if (w->windowType() != Qt::Desktop && w->testAttribute(Qt::WA_WState_Polished)) {if (w->style() == QApplicationPrivate::app_style)QApplicationPrivate::app_style->polish(w); // repolish
#ifndef QT_NO_STYLE_STYLESHEETelsew->setStyleSheet(w->styleSheet()); // touch
#endif}}for (QWidgetList::ConstIterator it = all.constBegin(), cend = all.constEnd(); it != cend; ++it) {QWidget *w = *it;if (w->windowType() != Qt::Desktop && !w->testAttribute(Qt::WA_SetStyle)) {QEvent e(QEvent::StyleChange);QApplication::sendEvent(w, &e);w->update();}}}#ifndef QT_NO_STYLE_STYLESHEETif (QStyleSheetStyle *oldProxy = qobject_cast(old)) {oldProxy->deref();} else
#endifif (old && old->parent() == qApp) {delete old;}if (QApplicationPrivate::focus_widget) {QFocusEvent in(QEvent::FocusIn, Qt::OtherFocusReason);QApplication::sendEvent(QApplicationPrivate::focus_widget->style(), &in);QApplicationPrivate::focus_widget->update();}
}
2-1. 获取所有的QWidget列表(在QWidget的构造函数中调用QWidgetPrivate::init函数,将当前QWidget加入到列表)。
2-2. 移除所有QWidget上旧的样式。
2-3. 设置所有QWidget新的样式。
2-4. 更新所有的QWidget。
QVector QStyleSheetStyle::styleRules(const QObject *obj) const
{QHash >::const_iterator cacheIt = styleSheetCaches->styleRulesCache.constFind(obj);if (cacheIt != styleSheetCaches->styleRulesCache.constEnd())return cacheIt.value();if (!initObject(obj)) {return QVector();}QStyleSheetStyleSelector styleSelector;StyleSheet defaultSs;QHash::const_iterator defaultCacheIt = styleSheetCaches->styleSheetCache.constFind(baseStyle());if (defaultCacheIt == styleSheetCaches->styleSheetCache.constEnd()) {defaultSs = getDefaultStyleSheet();QStyle *bs = baseStyle();styleSheetCaches->styleSheetCache.insert(bs, defaultSs);QObject::connect(bs, SIGNAL(destroyed(QObject*)), styleSheetCaches, SLOT(styleDestroyed(QObject*)), Qt::UniqueConnection);} else {defaultSs = defaultCacheIt.value();}styleSelector.styleSheets += defaultSs;if (!qApp->styleSheet().isEmpty()) {StyleSheet appSs;QHash::const_iterator appCacheIt = styleSheetCaches->styleSheetCache.constFind(qApp);if (appCacheIt == styleSheetCaches->styleSheetCache.constEnd()) {QString ss = qApp->styleSheet();if (ss.startsWith(QLatin1String("file:///")))ss.remove(0, 8);parser.init(ss, qApp->styleSheet() != ss);if (!parser.parse(&appSs))qWarning("Could not parse application stylesheet");appSs.origin = StyleSheetOrigin_Inline;appSs.depth = 1;styleSheetCaches->styleSheetCache.insert(qApp, appSs);} else {appSs = appCacheIt.value();}styleSelector.styleSheets += appSs;}QVector objectSs;for (const QObject *o = obj; o; o = parentObject(o)) {QString styleSheet = o->property("styleSheet").toString();if (styleSheet.isEmpty())continue;StyleSheet ss;QHash::const_iterator objCacheIt = styleSheetCaches->styleSheetCache.constFind(o);if (objCacheIt == styleSheetCaches->styleSheetCache.constEnd()) {parser.init(styleSheet);if (!parser.parse(&ss)) {parser.init(QLatin1String("* {") + styleSheet + QLatin1Char('}'));if (!parser.parse(&ss))qWarning("Could not parse stylesheet of object %p", o);}ss.origin = StyleSheetOrigin_Inline;styleSheetCaches->styleSheetCache.insert(o, ss);} else {ss = objCacheIt.value();}objectSs.append(ss);}for (int i = ; i < objectSs.count(); i++)objectSs[i].depth = objectSs.count() - i + ;styleSelector.styleSheets += objectSs;StyleSelector::NodePtr n;n.ptr = const_cast(obj);QVector rules = styleSelector.styleRulesForNode(n);styleSheetCaches->styleRulesCache.insert(obj, rules);return rules;
}
4-1. 初始化如果缓存中没有样式,先进行初始化。
4-2. 获取基本样式到@defaultSs中,并添加到@styleSelector.styleSheets
4-3. 在缓存中获取全局@qApp样式到@appSs中,并添加到@styleSelector.styleSheets。
4-3-1. 如果在缓存中没有找到全局@qApp样式,则获取@qApp样式表字符串进行解析生成StyleSheet
4-4. 不断遍历基类,从缓存中获取基类的样式到@objectSs中,并添加到@styleSelector.styleSheets。
4-4-1. 如果缓存没有找到样式,则利用字符串重新解析生成StyleSheet
4-5. 根据样式选着器(QStyleSheetStyleSelector)获取样式规添加到@rules。
4-6. 最后将对象(QObject)与样式规则(StyleRule)插入到缓存@styleSheetCaches->styleRulesCache中。
在"QStyleSheetStyle::styleRules"主要通过通过两个函数"Parser::init"和"Parser::parse"。
5-1. 初始化解析器:
void Parser::init(const QString &css, bool isFile)
{QString styleSheet = css;if (isFile) {QFile file(css);if (file.open(QFile::ReadOnly)) {sourcePath = QFileInfo(styleSheet).absolutePath() + QLatin1Char('/');QTextStream stream(&file);styleSheet = stream.readAll();} else {qWarning() << "QCss::Parser - Failed to load file " << css;styleSheet.clear();}} else {sourcePath.clear();}hasEscapeSequences = false;symbols.resize();symbols.reserve();Scanner::scan(Scanner::preprocess(styleSheet, &hasEscapeSequences), &symbols);index = ;errorIndex = -;
}
5-1-1. 将字符串进行预处理(这里主要是处理转义字符)。
5-1-2. 逐个扫描形成一个一个符号(与编译原理中的扫描程序一致,形成符号后,方便后面进一步处理)。
5-2. 解析样式表:
bool Parser::parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity)
{if (testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("charset"))) {if (!next(STRING)) return false;if (!next(SEMICOLON)) return false;}while (test(S) || test(CDO) || test(CDC)) {}while (testImport()) {ImportRule rule;if (!parseImport(&rule)) return false;styleSheet->importRules.append(rule);while (test(S) || test(CDO) || test(CDC)) {}}do {if (testMedia()) {MediaRule rule;if (!parseMedia(&rule)) return false;styleSheet->mediaRules.append(rule);} else if (testPage()) {PageRule rule;if (!parsePage(&rule)) return false;styleSheet->pageRules.append(rule);} else if (testRuleset()) {StyleRule rule;if (!parseRuleset(&rule)) return false;styleSheet->styleRules.append(rule);} else if (test(ATKEYWORD_SYM)) {if (!until(RBRACE)) return false;} else if (hasNext()) {return false;}while (test(S) || test(CDO) || test(CDC)) {}} while (hasNext());styleSheet->buildIndexes(nameCaseSensitivity);return true;
}
5-2-1. 遍历所有符号,根据符号的类型进行相应的处理(这里我们只关注样式规则"parseRuleset(&rule)")。
5-2-2. 解析样式规则:
bool Parser::parseRuleset(StyleRule *styleRule)
{Selector sel;if (!parseSelector(&sel)) return false;styleRule->selectors.append(sel);while (test(COMMA)) {skipSpace();Selector sel;if (!parseNextSelector(&sel)) return false;styleRule->selectors.append(sel);}skipSpace();if (!next(LBRACE)) return false;const int declarationStart = index;do {skipSpace();Declaration decl;const int rewind = index;if (!parseNextDeclaration(&decl)) {index = rewind;const bool foundSemicolon = until(SEMICOLON);const int semicolonIndex = index;index = declarationStart;const bool foundRBrace = until(RBRACE);if (foundSemicolon && semicolonIndex < index) {decl = Declaration();index = semicolonIndex - ;} else {skipSpace();return foundRBrace;}}if (!decl.isEmpty())styleRule->declarations.append(decl);} while (test(SEMICOLON));if (!next(RBRACE)) return false;skipSpace();return true;
}
5-2-2-1. 解析选择器(id选着器,class选着器,属性选着器等)。
5-2-2-2. 解析声明,包括属性名、属性值等。
5-2-2-3. 将解析的声明添加到样式规则中。
QVector StyleSelector::styleRulesForNode(NodePtr node){QVector rules;if (styleSheets.isEmpty())return rules;QMap weightedRules; // (spec, rule) that will be sorted below//prune using indexed stylesheetfor (int sheetIdx = ; sheetIdx < styleSheets.count(); ++sheetIdx) {const StyleSheet &styleSheet = styleSheets.at(sheetIdx);for (int i = 0; i < styleSheet.styleRules.count(); ++i) {matchRule(node, styleSheet.styleRules.at(i), styleSheet.origin, styleSheet.depth, &weightedRules);}if (!styleSheet.idIndex.isEmpty()) {QStringList ids = nodeIds(node);for (int i = 0; i < ids.count(); i++) {const QString &key = ids.at(i);QMultiHash::const_iterator it = styleSheet.idIndex.constFind(key);while (it != styleSheet.idIndex.constEnd() && it.key() == key) {matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);++it;}}}if (!styleSheet.nameIndex.isEmpty()) {QStringList names = nodeNames(node);for (int i = 0; i < names.count(); i++) {QString name = names.at(i);if (nameCaseSensitivity == Qt::CaseInsensitive)name = name.toLower();QMultiHash::const_iterator it = styleSheet.nameIndex.constFind(name);while (it != styleSheet.nameIndex.constEnd() && it.key() == name) {matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);++it;}}}if (!medium.isEmpty()) {for (int i = ; i < styleSheet.mediaRules.count(); ++i) {if (styleSheet.mediaRules.at(i).media.contains(medium, Qt::CaseInsensitive)) {for (int j = ; j < styleSheet.mediaRules.at(i).styleRules.count(); ++j) {matchRule(node, styleSheet.mediaRules.at(i).styleRules.at(j), styleSheet.origin,styleSheet.depth, &weightedRules);}}}}}rules.reserve(weightedRules.count());QMap::const_iterator it = weightedRules.constBegin();for ( ; it != weightedRules.constEnd() ; ++it)rules += *it;return rules;}
6-1. 遍历已有的样式规则(styleSheet.styleRules),如果匹配规则,将规则添加到@weightedRules中。
6-2. 遍历id样式(styleSheet.idIndex),获取当前对象的名称(objectName),对象名称相同则匹配规则,将匹配的规则添加到@weightedRules中。
6-3. 遍历class样式(styleSheet.nameIndex),获取当前对象的类名称(className)和基类的类名称,类名称相同则匹配规则,将规则添加到@weightedRules中。
6-4. 将@weightedRules中的样式规则添加到@rules,并返回@rules
QRenderRule::QRenderRule(const QVector &declarations, const QObject *object)
: features(), hasFont(false), pal(), b(), bg(), bd(), ou(), geo(), p(), img(), clipset()
{QPalette palette = QApplication::palette(); // ###: ideally widget's paletteValueExtractor v(declarations, palette)zhuafeatures = v.extractStyleFeatures();int w = -, h = -, minw = -, minh = -, maxw = -, maxh = -;if (v.extractGeometry(&w, &h, &minw, &minh, &maxw, &maxh))geo = new QStyleSheetGeometryData(w, h, minw, minh, maxw, maxh);int left = , top = , right = , bottom = ;Origin origin = Origin_Unknown;Qt::Alignment position = ;QCss::PositionMode mode = PositionMode_Unknown;Qt::Alignment textAlignment = ;if (v.extractPosition(&left, &top, &right, &bottom, &origin, &position, &mode, &textAlignment))p = new QStyleSheetPositionData(left, top, right, bottom, origin, position, mode, textAlignment);int margins[], paddings[], spacing = -;for (int i = ; i < ; i++)margins[i] = paddings[i] = ;if (v.extractBox(margins, paddings, &spacing))b = new QStyleSheetBoxData(margins, paddings, spacing);int borders[];QBrush colors[];QCss::BorderStyle styles[];QSize radii[];for (int i = ; i < ; i++) {borders[i] = ;styles[i] = BorderStyle_None;}if (v.extractBorder(borders, colors, styles, radii))bd = new QStyleSheetBorderData(borders, colors, styles, radii);int offsets[];for (int i = ; i < ; i++) {borders[i] = offsets[i] = ;styles[i] = BorderStyle_None;}if (v.extractOutline(borders, colors, styles, radii, offsets))ou = new QStyleSheetOutlineData(borders, colors, styles, radii, offsets);QBrush brush;QString uri;Repeat repeat = Repeat_XY;Qt::Alignment alignment = Qt::AlignTop | Qt::AlignLeft;Attachment attachment = Attachment_Scroll;origin = Origin_Padding;Origin clip = Origin_Border;if (v.extractBackground(&brush, &uri, &repeat, &alignment, &origin, &attachment, &clip))bg = new QStyleSheetBackgroundData(brush, QPixmap(uri), repeat, alignment, origin, attachment, clip);QBrush sfg, fg;QBrush sbg, abg;if (v.extractPalette(&fg, &sfg, &sbg, &abg))pal = new QStyleSheetPaletteData(fg, sfg, sbg, abg);QIcon icon;alignment = Qt::AlignCenter;QSize size;if (v.extractImage(&icon, &alignment, &size))img = new QStyleSheetImageData(icon, alignment, size);int adj = -;hasFont = v.extractFont(&font, &adj);#ifndef QT_NO_TOOLTIPif (object && qstrcmp(object->metaObject()->className(), "QTipLabel") == )palette = QToolTip::palette();
#endiffor (int i = ; i < declarations.count(); i++) {const Declaration& decl = declarations.at(i);if (decl.d->propertyId == BorderImage) {QString uri;QCss::TileMode horizStretch, vertStretch;int cuts[];decl.borderImageValue(&uri, cuts, &horizStretch, &vertStretch);if (uri.isEmpty() || uri == QLatin1String("none")) {if (bd && bd->bi)bd->bi->pixmap = QPixmap();} else {if (!bd)bd = new QStyleSheetBorderData;if (!bd->bi)bd->bi = new QStyleSheetBorderImageData;QStyleSheetBorderImageData *bi = bd->bi;bi->pixmap = QPixmap(uri);for (int i = ; i < ; i++)bi->cuts[i] = cuts[i];bi->horizStretch = horizStretch;bi->vertStretch = vertStretch;}} else if (decl.d->propertyId == QtBackgroundRole) {if (bg && bg->brush.style() != Qt::NoBrush)continue;int role = decl.d->values.at().variant.toInt();if (role >= Value_FirstColorRole && role <= Value_LastColorRole)defaultBackground = palette.color((QPalette::ColorRole)(role-Value_FirstColorRole));} else if (decl.d->property.startsWith(QLatin1String("qproperty-"), Qt::CaseInsensitive)) {// intentionally left blank...} else if (decl.d->propertyId == UnknownProperty) {bool knownStyleHint = false;for (int i = ; i < numKnownStyleHints; i++) {QLatin1String styleHint(knownStyleHints[i]);if (decl.d->property.compare(styleHint) == ) {QString hintName = QString(styleHint);QVariant hintValue;if (hintName.endsWith(QLatin1String("alignment"))) {hintValue = (int) decl.alignmentValue();} else if (hintName.endsWith(QLatin1String("color"))) {hintValue = (int) decl.colorValue().rgba();} else if (hintName.endsWith(QLatin1String("size"))) {hintValue = decl.sizeValue();} else if (hintName.endsWith(QLatin1String("icon"))) {hintValue = decl.iconValue();} else if (hintName == QLatin1String("button-layout")&& decl.d->values.count() != && decl.d->values.at().type == Value::String) {hintValue = subControlLayout(decl.d->values.at().variant.toString());} else {int integer;decl.intValue(&integer);hintValue = integer;}styleHints[decl.d->property] = hintValue;knownStyleHint = true;break;}}if (!knownStyleHint)qDebug("Unknown property %s", qPrintable(decl.d->property));}}if (const QWidget *widget = qobject_cast(object)) {QStyleSheetStyle *style = const_cast(globalStyleSheetStyle);if (!style)style = qobject_cast(widget->style());if (style)fixupBorder(style->nativeFrameWidth(widget));}if (hasBorder() && border()->hasBorderImage())defaultBackground = QBrush();
}
从样式规则中得到样式声明,然后根据样式声明去创建不同的绘制对象,为后面的绘制做准备。
绘制对象包括:位置(Geomeory、Position、Box)、边框(Border、Outline)、背景(BackgroundColor、BackgroundImage等)、字体(FontFamily、FontSize、FontStyle、FontWeight等)等等。
最后如果想查看QSS属性名称和值的列表,可以查看qtbase\src\gui\text\qcssparser.cpp文件:
下一篇:Linux命令之nano命令