QT源码剖析之QSS样式表
Posted DC
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QT源码剖析之QSS样式表相关的知识,希望对你有一定的参考价值。
1. "QApplication::setStyleSheet()"设置样式表:
1. 创建新的样式表。
2. 设置新的样式。
void QApplication::setStyleSheet(const QString& styleSheet) { QApplicationPrivate::styleSheet = styleSheet; QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle*>(QApplicationPrivate::app_style); if (styleSheet.isEmpty()) { // application style sheet removed if (!proxy) return; // there was no stylesheet before setStyle(proxy->base); } else if (proxy) { // style sheet update, just repolish proxy->repolish(qApp); } else { // stylesheet set the first time QStyleSheetStyle *newProxy = new QStyleSheetStyle(QApplicationPrivate::app_style); QApplicationPrivate::app_style->setParent(newProxy); setStyle(newProxy); } }
2. "QApplication::setStyle"——设置样式:
void QApplication::setStyle(QStyle *style) { if (!style || style == QApplicationPrivate::app_style) return; QWidgetList all = allWidgets(); // clean up the old style if (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 desktop w->testAttribute(Qt::WA_WState_Polished)) { // has been polished QApplicationPrivate::app_style->unpolish(w); } } } QApplicationPrivate::app_style->unpolish(qApp); } QStyle *old = QApplicationPrivate::app_style; // save QApplicationPrivate::overrides_native_style = nativeStyleClassName() == QByteArray(style->metaObject()->className()); #ifndef QT_NO_STYLE_STYLESHEET if (!QApplicationPrivate::styleSheet.isEmpty() && !qobject_cast<QStyleSheetStyle *>(style)) { // we have a stylesheet already and a new style is being set QStyleSheetStyle *newProxy = new QStyleSheetStyle(style); style->setParent(newProxy); QApplicationPrivate::app_style = newProxy; } else #endif // QT_NO_STYLE_STYLESHEET QApplicationPrivate::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() itself if (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=*/0, /*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 style QApplicationPrivate::app_style->polish(qApp); // re-polish existing widgets if necessary 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 && w->testAttribute(Qt::WA_WState_Polished)) { if (w->style() == QApplicationPrivate::app_style) QApplicationPrivate::app_style->polish(w); // repolish #ifndef QT_NO_STYLE_STYLESHEET else w->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_STYLESHEET if (QStyleSheetStyle *oldProxy = qobject_cast<QStyleSheetStyle *>(old)) { oldProxy->deref(); } else #endif if (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。
4. "QStyleSheetStyle::styleRules"——经过一系列调用到这个函数中,获取样式规则:
QVector<QCss::StyleRule> QStyleSheetStyle::styleRules(const QObject *obj) const { QHash<const QObject *, QVector<StyleRule> >::const_iterator cacheIt = styleSheetCaches->styleRulesCache.constFind(obj); if (cacheIt != styleSheetCaches->styleRulesCache.constEnd()) return cacheIt.value(); if (!initObject(obj)) { return QVector<StyleRule>(); } QStyleSheetStyleSelector styleSelector; StyleSheet defaultSs; QHash<const void *, StyleSheet>::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 void *, StyleSheet>::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<QCss::StyleSheet> objectSs; for (const QObject *o = obj; o; o = parentObject(o)) { QString styleSheet = o->property("styleSheet").toString(); if (styleSheet.isEmpty()) continue; StyleSheet ss; QHash<const void *, StyleSheet>::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 = 0; i < objectSs.count(); i++) objectSs[i].depth = objectSs.count() - i + 2; styleSelector.styleSheets += objectSs; StyleSelector::NodePtr n; n.ptr = const_cast<QObject *>(obj); QVector<QCss::StyleRule> 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中。
5. 将字符串解析生成样式表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(0); symbols.reserve(8); Scanner::scan(Scanner::preprocess(styleSheet, &hasEscapeSequences), &symbols); index = 0; errorIndex = -1; }
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 - 1; } 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. 将解析的声明添加到样式规则中。
6. (4-5的细化)根据样式选着器(QStyleSheetStyleSelector)得到样式规则"":
1 QVector<StyleRule> StyleSelector::styleRulesForNode(NodePtr node) 2 { 3 QVector<StyleRule> rules; 4 if (styleSheets.isEmpty()) 5 return rules; 6 7 QMap<uint, StyleRule> weightedRules; // (spec, rule) that will be sorted below 8 9 //prune using indexed stylesheet 10 for (int sheetIdx = 0; sheetIdx < styleSheets.count(); ++sheetIdx) { 11 const StyleSheet &styleSheet = styleSheets.at(sheetIdx); 12 for (int i = 0; i < styleSheet.styleRules.count(); ++i) { 13 matchRule(node, styleSheet.styleRules.at(i), styleSheet.origin, styleSheet.depth, &weightedRules); 14 } 15 16 if (!styleSheet.idIndex.isEmpty()) { 17 QStringList ids = nodeIds(node); 18 for (int i = 0; i < ids.count(); i++) { 19 const QString &key = ids.at(i); 20 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.idIndex.constFind(key); 21 while (it != styleSheet.idIndex.constEnd() && it.key() == key) { 22 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules); 23 ++it; 24 } 25 } 26 } 27 if (!styleSheet.nameIndex.isEmpty()) { 28 QStringList names = nodeNames(node); 29 for (int i = 0; i < names.count(); i++) { 30 QString name = names.at(i); 31 if (nameCaseSensitivity == Qt::CaseInsensitive) 32 name = name.toLower(); 33 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.nameIndex.constFind(name); 34 while (it != styleSheet.nameIndex.constEnd() && it.key() == name) { 35 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules); 36 ++it; 37 } 38 } 39 } 40 if (!medium.isEmpty()) { 41 for (int i = 0; i < styleSheet.mediaRules.count(); ++i) { 42 if (styleSheet.mediaRules.at(i).media.contains(medium, Qt::CaseInsensitive)) { 43 for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.count(); ++j) { 44 matchRule(node, styleSheet.mediaRules.at(i).styleRules.at(j), styleSheet.origin, 45 styleSheet.depth, &weightedRules); 46 } 47 } 48 } 49 } 50 } 51 52 rules.reserve(weightedRules.count()); 53 QMap<uint, StyleRule>::const_iterator it = weightedRules.constBegin(); 54 for ( ; it != weightedRules.constEnd() ; ++it) 55 rules += *it; 56 57 return rules; 58 }
6-1. 遍历已有的样式规则(styleSheet.styleRules),如果匹配规则,将规则添加到@weightedRules中。
6-2. 遍历id样式(styleSheet.idIndex),获取当前对象的名称(objectName),对象名称相同则匹配规则,将匹配的规则添加到@weightedRules中。
6-3. 遍历class样式(styleSheet.nameIndex),获取当前对象的类名称(className)和基类的类名称,类名称相同则匹配规则,将规则添加到@weightedRules中。
6-4. 将@weightedRules中的样式规则添加到@rules,并返回@rules
7. 最后需要渲染规则,将得到的样式规则转换为绘制时需要的对象(字体、颜色、背景画刷、背景图片等等):
QRenderRule::QRenderRule(const QVector<Declaration> &declarations, const QObject *object) : features(0), hasFont(false), pal(0), b(0), bg(0), bd(0), ou(0), geo(0), p(0), img(0), clipset(0) { QPalette palette = QApplication::palette(); // ###: ideally widget‘s palette ValueExtractor v(declarations, palette)zhua features = v.extractStyleFeatures(); int w = -1, h = -1, minw = -1, minh = -1, maxw = -1, maxh = -1; if (v.extractGeometry(&w, &h, &minw, &minh, &maxw, &maxh)) geo = new QStyleSheetGeometryData(w, h, minw, minh, maxw, maxh); int left = 0, top = 0, right = 0, bottom = 0; Origin origin = Origin_Unknown; Qt::Alignment position = 0; QCss::PositionMode mode = PositionMode_Unknown; Qt::Alignment textAlignment = 0; if (v.extractPosition(&left, &top, &right, &bottom, &origin, &position, &mode, &textAlignment)) p = new QStyleSheetPositionData(left, top, right, bottom, origin, position, mode, textAlignment); int margins[4], paddings[4], spacing = -1; for (int i = 0; i < 4; i++) margins[i] = paddings[i] = 0; if (v.extractBox(margins, paddings, &spacing)) b = new QStyleSheetBoxData(margins, paddings, spacing); int borders[4]; QBrush colors[4]; QCss::BorderStyle styles[4]; QSize radii[4]; for (int i = 0; i < 4; i++) { borders[i] = 0; styles[i] = BorderStyle_None; } if (v.extractBorder(borders, colors, styles, radii)) bd = new QStyleSheetBorderData(borders, colors, styles, radii); int offsets[4]; for (int i = 0; i < 4; i++) { borders[i] = offsets[i] = 0; 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 = -255; hasFont = v.extractFont(&font, &adj); #ifndef QT_NO_TOOLTIP if (object && qstrcmp(object->metaObject()->className(), "QTipLabel") == 0) palette = QToolTip::palette(); #endif for (int i = 0; i < declarations.count(); i++) { const Declaration& decl = declarations.at(i); if (decl.d->propertyId == BorderImage) { QString uri; QCss::TileMode horizStretch, vertStretch; int cuts[4]; 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 = 0; i < 4; 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(0).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 = 0; i < numKnownStyleHints; i++) { QLatin1String styleHint(knownStyleHints[i]); if (decl.d->property.compare(styleHint) == 0) { 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() != 0 && decl.d->values.at(0).type == Value::String) { hintValue = subControlLayout(decl.d->values.at(0).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<const QWidget *>(object)) { QStyleSheetStyle *style = const_cast<QStyleSheetStyle *>(globalStyleSheetStyle); if (!style) style = qobject_cast<QStyleSheetStyle *>(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属性名称和值的列表,可以查看qcssparser.cpp文件:
static const QCssKnownValue properties[NumProperties - 1] = { { "-qt-background-role", QtBackgroundRole }, { "-qt-block-indent", QtBlockIndent }, { "-qt-list-indent", QtListIndent }, { "-qt-list-number-prefix", QtListNumberPrefix }, { "-qt-list-number-suffix", QtListNumberSuffix }, { "-qt-paragraph-type", QtParagraphType }, { "-qt-style-features", QtStyleFeatures }, { "-qt-table-type", QtTableType }, { "-qt-user-state", QtUserState }, { "alternate-background-color", QtAlternateBackground }, { "background", Background }, { "background-attachment", BackgroundAttachment }, { "background-clip", BackgroundClip }, { "background-color", BackgroundColor }, { "background-image", BackgroundImage }, { "background-origin", BackgroundOrigin }, { "background-position", BackgroundPosition }, { "background-repeat", BackgroundRepeat }, { "border", Border }, { "border-bottom", BorderBottom }, { "border-bottom-color", BorderBottomColor }, { "border-bottom-left-radius", BorderBottomLeftRadius }, { "border-bottom-right-radius", BorderBottomRightRadius }, { "border-bottom-style", BorderBottomStyle }, { "border-bottom-width", BorderBottomWidth }, { "border-color", BorderColor }, { "border-image", BorderImage }, { "border-left", BorderLeft }, { "border-left-color", BorderLeftColor }, { "border-left-style", BorderLeftStyle }, { "border-left-width", BorderLeftWidth }, { "border-radius", BorderRadius }, { "border-right", BorderRight }, { "border-right-color", BorderRightColor }, { "border-right-style", BorderRightStyle }, { "border-right-width", BorderRightWidth }, { "border-style", BorderStyles }, { "border-top", BorderTop }, { "border-top-color", BorderTopColor }, { "border-top-left-radius", BorderTopLeftRadius }, { "border-top-right-radius", BorderTopRightRadius }, { "border-top-style", BorderTopStyle }, { "border-top-width", BorderTopWidth }, { "border-width", BorderWidth }, { "bottom", Bottom }, { "color", Color }, { "float", Float }, { "font", Font }, { "font-family", FontFamily }, { "font-size", FontSize }, { "font-style", FontStyle }, { "font-variant", FontVariant }, { "font-weight", FontWeight }, { "height", Height }, { "image", QtImage }, { "image-position", QtImageAlignment }, { "left", Left }, { "line-height", LineHeight }, { "list-style", ListStyle }, { "list-style-type", ListStyleType }, { "margin" , Margin }, { "margin-bottom", MarginBottom }, { "margin-left", MarginLeft }, { "margin-right", MarginRight }, { "margin-top", MarginTop }, { "max-height", MaximumHeight }, { "max-width"以上是关于QT源码剖析之QSS样式表的主要内容,如果未能解决你的问题,请参考以下文章