如何在 Qt StyleSheet 中只覆盖一对 property:value

How to override just one property:value pair in Qt StyleSheet

我正在 OSX Mavericks 上编写新手 Qt5 代码,并且只想覆盖给定小部件的一对 property:value 样式表。

如果我运行下面的自包含演示代码:

#include <QApplication>
#include <QMainWindow>
#include <QtGui>
#include <QPushButton>

int
main( int argc, char *argv[] ) {
    QApplication app( argc, argv );

    QMainWindow* mw = new QMainWindow();

    QPushButton* AButton = new QPushButton( "A Button", mw );

    mw->show();

    return app.exec();
}

我得到了一个带有 Macintosh 风格默认设置的漂亮按钮——圆角、标准 OSX- 按下时为蓝色等:

但是,如果我将样式表设置为使背景按钮颜色为红色,似乎我在此过程中丢失了所有其他 OSX 样式默认值——不再有圆角、标准边距等:

#include <QApplication>
#include <QMainWindow>
#include <QtGui>
#include <QPushButton>

int
main( int argc, char *argv[] ) {
    QApplication app( argc, argv );

    QMainWindow* mw = new QMainWindow();

    QPushButton* AButton = new QPushButton( "A Button", mw );

    AButton->setStyleSheet("QPushButton { background-color: red; }");

    mw->show();

    return app.exec();
}

结果如下:

如何只覆盖一对 property:value 而同时保留小部件的其余样式元素,例如在上面的示例中,将背景颜色设置为红色,但保持所有边框圆角、边距等不变?

非常感谢

正如并行 post 中所阐明的那样,我最初的问题是基于混淆的前提。

在 Qt 中,所有小部件都通过 QStyle(). By default for Qt code on OSX (as in my first block of demonstration code in the original question), Qt widgets are styled by a subclass of QStyle() called QMacStyle() (actually now apparently a derivative of QProxyStyle(), per the page QMacStyle -- File Not Found) 设置样式。

setStyleSheet() 函数调用,就像我在原始问题中的第二个演示代码块中一样,创建了一个 QStyleSheetStyle() 实例,然后它会批量替换之前用于小部件的任何 QStyle - 在此以我一直希望保留的漂亮 QMacStyle 为例。人们可以在例如qtbase/src/widgets/kernel/qapplication.cppQApplication::setStyleSheet() 方法中。 QStyleSheetStyle() 本身在 qtbase/src/widgets/styles/qstylesheetstyle.cpp 中。作为琐事,QStyleSheetStyle() 派生自 QWindowsStyle(),因此在我的原始问题中修改后的按钮具有 windows-ish 外观。

简而言之,Qt StyleSheets 是在 QStyle() 之上实现的。调用 setStyleSheet() 隐含地决定您将使用 QStyleSheetStyle() 而不是 QMacStyle()。

要点是,在设计小部件样式时,您需要选择自己的方法,并权衡两种方式。您需要选择要使用的 QStyle()。如果您 运行 默认代码在 Mac 上,则您已选择从 QMacStyle() 开始。然后,您可以根据现有文档修改该 QStyle() 。如果您调用 setStyleSheet(),则您已隐式选择从 QStyleSheetStyle() 开始,默认情况下它具有 QWindowsStyle() 的外观。您随后应用的任何样式表 属性-values 都会修改该外观。

对于我上面提出的问题,有两种选择。一个是一个人可以完全通过 QStyle() 可用的机制进行任何和所有外观修改。其他地方记录了如何执行此操作。另一种选择是从头开始生成一个样式表,该样式表再现了相关小部件的 Mac 外观,然后使用所需的调整对其进行修改并通过 setStyleSheet() 应用它。

在一个理想的世界中,有人已经完成了该练习——即将 qtbase/src/widgets/styles/qmacstyle_mac.mm 消化到其等效样式表中——但是这项工作似乎很繁重,而且似乎没有人做过,至少不是 public posting。我想另一个遥远可行的选择是基于 QMacStyle 而不是 QWindowsStyle 重新实现 QStyleSheetStyle,然后以某种方式将结果整合回代码中(可能有一个 setStyleSheet( QStyle* baseStyle, QString styleSheet))。然而,这远远超出了 Qt 新手的技能,更不用说当前的任务了。

您在评论中的分析是对 QApplication::setStyleSheet() will completely replace the currently active style is incorrect. You are right that a the current style is replaced with QStyleSheetStyle. However, QStyleSheetStyle delegates its drawing to the original style, in your case the Mac style. If you look at the source of QStyleSheetStyle 的调用,您会在很多地方看到这一点,对 baseStyle()->drawControl().

的调用

这意味着风格sheet适用于任何风格。现在,它显然不适用于您的情况,所以发生了什么?如果样式 sheet 规则无法应用于基本样式,QStyleSheetStyle 将回退到 Windows 样式中进行绘制。这意味着某些样式 sheet 规则可以很好地工作,而其他样式会触发回退。您的规则触发了回退。

我认为没有记录哪些规则会触发回退。为此,我们需要查看源代码,在本例中为 QStyleSheetStyle::drawControl()。我们会在其中找到以下内容:

case CE_PushButton:
    if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
        if (rule.hasDrawable() || rule.hasBox() || rule.hasPosition() || rule.hasPalette() ||
                ((btn->features & QStyleOptionButton::HasMenu) && hasStyleRule(w, PseudoElement_PushButtonMenuIndicator))) {
            ParentStyle::drawControl(ce, opt, p, w);
            return;
        }
    }

ParentSyle 是后备 Windows 样式。您的规则 background-color 使 rule.hasPalette() return 为真,因此触发回退。