QT 样式解析流程(转载)
创始人
2024-06-03 08:46:30
0

样式解析原始文章,此处私人备份
qt 不同版本可能有差异

  1. "QApplication::setStyleSheet()"设置样式表:
  2. 创建新的样式表。
  3. 设置新的样式。
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);}
}
  1. “QApplication::setStyle”——设置样式:
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。

  1. “QStyleSheetStyle::styleRules”——经过一系列调用到这个函数中,获取样式规则:
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中。

  1. 将字符串解析生成样式表StyleSheet(主要在qcssparser.cpp文件中):

在"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. 将解析的声明添加到样式规则中。

  1. (4-5的细化)根据样式选着器(QStyleSheetStyleSelector)得到样式规则"":
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

  1. 最后需要渲染规则,将得到的样式规则转换为绘制时需要的对象(字体、颜色、背景画刷、背景图片等等):
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文件:

相关内容

热门资讯

安卓系统把视频设为铃声,如何将... 你有没有想过,手机里的视频竟然能变成铃声?是的,你没听错,安卓系统竟然有这样的神奇功能!今天,就让我...
边锋杭麻圈安卓系统,边锋科技引... 你有没有听说过边锋杭麻圈安卓系统?这可是最近在游戏圈里火得一塌糊涂的存在哦!想象你正坐在家里,手握着...
ios系统能转移到安卓系统,轻... 你有没有想过,有一天你的手机从iOS系统跳转到安卓系统,会是怎样的体验呢?这可不是天方夜谭,随着科技...
安卓系统网络连接查看,安卓系统... 你有没有遇到过这种情况:手机里装了各种APP,可就是不知道哪个在偷偷消耗着你的流量?别急,今天就来教...
什么安卓系统优化的好,打造流畅... 你有没有发现,手机用久了,就像人一样,会变得有些“臃肿”呢?尤其是安卓系统,有时候感觉就像一个老态龙...
手机安卓系统ios系统怎么安装... 你有没有发现,现在手机里的软件真是五花八门,让人眼花缭乱?无论是安卓系统还是iOS系统,都能轻松安装...
按音量键报警安卓系统,安卓系统... 你有没有遇到过这种情况:手机突然发出刺耳的警报声,吓得你心跳加速,还以为发生了什么大事?别担心,今天...
安卓系统改win版系统怎么安装... 你有没有想过,把你的安卓手机换成Windows系统的电脑呢?想象那流畅的触控体验和强大的办公功能,是...
安卓系统如何备份到苹果,轻松实... 你是不是也有过这样的烦恼:手机里的照片、联系人、应用数据等等,突然间就消失得无影无踪?别担心,今天就...
修改安卓系统参数设置,安卓系统... 你有没有发现,你的安卓手机有时候就像一个不听话的小孩子,总是按照自己的节奏来,让你有点头疼?别急,今...
安卓手机gps定位系统,畅享智... 你有没有发现,现在不管走到哪里,手机都能帮你找到路?这都得归功于安卓手机的GPS定位系统。想象你站在...
华为不能安卓系统升级,探索创新... 你知道吗?最近有个大新闻在科技圈里炸开了锅,那就是华为的新款手机竟然不能升级安卓系统了!这可真是让人...
汽车加装安卓系统卡住,探究原因... 你有没有遇到过这样的尴尬情况:汽车加装了安卓系统,结果屏幕突然卡住了,就像被施了魔法一样,怎么也动弹...
电量壁纸安卓系统下载,打造个性... 手机电量告急,是不是又得赶紧找充电宝了?别急,今天就来给你安利一款超实用的电量壁纸,让你的安卓手机瞬...
iPhonex里面是安卓系统,... 你有没有想过,那个我们每天都离不开的iPhone,里面竟然可能是安卓系统?是的,你没听错,就是那个以...
ios系统比安卓系统好在哪里,... 你有没有想过,为什么有些人对iOS系统情有独钟,而有些人却对安卓系统爱不释手呢?今天,就让我带你从多...
安卓系统跟踪设置大小,跟踪设置... 你知道吗?现在智能手机几乎成了我们生活的必需品,而安卓系统作为全球最受欢迎的操作系统之一,它的跟踪设...
在线迎新系统下载安卓,轻松开启... 你有没有想过,开学季的到来,就像一场盛大的狂欢,而在这个狂欢中,有一个小助手,它默默地守护着你的入学...
安卓系统怎么申请微信号,一键申... 你有没有想过,在安卓手机上申请一个微信账号,竟然也能变得如此简单?没错,就是那个我们每天离不开的社交...
安卓手机系统里怎么清理,轻松优... 手机里的东西越来越多,是不是感觉安卓手机系统越来越慢了呢?别急,今天就来教你怎么清理安卓手机系统,让...