如何在Qt样式表中覆盖一个属性:值对

27

我正在OSX Mavericks上编写新手Qt5代码,并希望覆盖给定窗口小部件的StyleSheet中的一个属性:值对。

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


#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蓝色等:

QPushButton with OSX defaults

然而,如果我将样式表设置为使背景按钮颜色变为红色,似乎我在这个过程中失去了所有其他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();
}

这是结果:

QPushButton的样式覆盖,导致其他OSX默认值丢失

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

非常感谢


你可以尝试使用AButton->setStyleSheet(AButton->styleSheet() + "...");将新样式添加到现有样式中。 - leemes
谢谢,不过这似乎仍然会覆盖所有其他平台默认值(我使用了 AButton->setStyleSheet( AButton->styleSheet() + "QPushButton { background-color: red; }"); - lcikgl
1
啊等一下...你所说的不是被覆盖的CSS样式属性,而是系统样式QtWidget的样式引擎试图模仿的。据我所知,当你手动指定某些属性时,它无法模仿系统样式,并在样式引擎无法适应你指定的CSS属性时回退到"旧的Windows样式"。尝试使用调色板代替。看一下QWidget::setPalette()palette(),然后尝试为一些角色设置颜色(试试哪个用于按钮背景,我记不清了)。这将被样式引擎考虑进去。 - leemes
1
看起来QMacStyle忽略了QPalette::Button颜色来设置背景,但是你仍然可以使用QPalette::Text来设置文本颜色。 - Linville
2个回答

12

您在评论中分析的一种情况是错误的:调用QApplication::setStyleSheet()不会完全替换当前活动样式。您正确地指出,当前样式被QStyleSheetStyle替换了。但是,QStyleSheetStyle将其绘制委托给原始样式,在您的情况下是Mac样式。如果您查看QStyleSheetStyle的源代码,您会在许多地方看到它调用了baseStyle()->drawControl()方法。

这意味着样式表对任何样式都有效。但显然,在您的情况下它并没有工作,发生了什么?QStyleSheetStyle会回退到以Windows样式绘制,如果无法将样式表规则应用于基本样式。这意味着某些样式表规则可以很好地工作,而其他规则将触发回退。你的规则触发了回退。

我认为文档中没有说明哪些规则会引发回退。为此,我们需要查看源代码,例如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() 返回 true,从而触发回退。


7
如在并行帖子中所述,我的原始问题基于混乱的前提。在Qt中,所有小部件都通过QStyle()进行样式设置。对于OSX上的Qt代码(如原始问题中的第一个演示代码块)默认情况下,Qt小部件是由QStyle()的子类QMacStyle()进行样式设置的(实际上现在显然是QProxyStyle()的派生物,参见页面QMacStyle -- File Not Found)。
setStyleSheet()函数调用(如原始问题中的第二个演示代码块中)创建了一个QStyleSheetStyle()的实例,它会整体替换以前小部件使用的任何QStyle -- 在这种情况下是我一直希望保留的漂亮的QMacStyle。可以在例如qtbase/src/widgets/kernel/qapplication.cppQApplication::setStyleSheet()方法中看到其实现细节。QStyleSheetStyle()本身在qtbase/src/widgets/styles/qstylesheetstyle.cpp中。作为琐事的一点,QStyleSheetStyle()是派生自QWindowsStyle(),因此在原始问题中修改后的pushbutton具有windows风格的外观。
简短的故事是Qt样式表是基于QStyle()实现的。调用setStyleSheet()隐含地决定您将使用QStyleSheetStyle()而不是QMacStyle()。重要的信息是,在设置小部件样式时,您需要选择一种方法,两种方法都有权衡。您需要选择要使用的QStyle()。如果在Mac上运行默认代码,则选择从QMacStyle()开始。然后,可以根据现有文档修改该QStyle()。如果调用setStyleSheet(),则隐含地选择从QStyleSheetStyle()开始,默认情况下具有QWindowsStyle()的外观。然后应用任何样式表属性值将修改该外观。
对于我上面提出的问题,有两个选择。一个是人们可以完全通过QStyle()可用机制进行任何和所有外观修改。如何做到这一点在其他地方有记录。另一个选择是从头开始生成一个重现所讨论小部件的Mac外观的样式表,然后通过setStyleSheet()进行所需的调整和应用。
在理想的情况下,有人已经完成了这个练习 - 即将qtbase/src/widgets/styles/qmacstyle_mac.mm转化为样式表的等效形式 - 但是这项工作似乎很繁琐,并且似乎没有任何人做过它,至少没有公开发布。我想另一个可能性是基于QMacStyle重新实现QStyleSheetStyle而不是QWindowsStyle,然后以某种方式将结果集成回代码中(也许有一个setStyleSheet(QStyle* baseStyle, QString styleSheet))。但这远远超出了新手Qt技能的范围,更不用说当前任务了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接