如何在Qt StyleSheet中只覆盖一个属性:值对

时间:2015-03-03 19:05:12

标签: qt qt5 qtstylesheets qpushbutton

我正在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 style override, with loss of other OSX defaults

如何在保留窗口小部件的其余样式元素的同时覆盖一个属性:值对,例如在上面的示例中,使背景颜色为红色,但保持所有边框的圆角,边距等相同?

非常感谢

2 个答案:

答案 0 :(得分:5)

您在评论中对QApplication::setStyleSheet()的调用将完全取代当前活动样式的分析不正确。你是正确的,当前的样式被QStyleSheetStyle取代。但是,QStyleSheetStyle将其绘图委托给原始样式,在您的情况下为Mac样式。如果您查看source of 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,从而触发回退。

答案 1 :(得分:3)

如同平行帖How to obtain entire Qt StyleSheet for QMacStyle中所阐述的那样,我原来的问题是基于混淆的前提。

在Qt中,所有小部件都通过QStyle()设置样式。默认情况下,对于OSX上的Qt代码(如我在原始问题中的第一个演示代码块),Qt小部件由QStyle()的子类称为QMacStyle()(实际上现在显然是{{3}的派生物) },每页QProxyStyle())。

setStyleSheet()函数调用,就像我在原始问题中的第二个演示代码块一样,创建了一个QStyleSheetStyle()的实例,然后批量替换事先为小部件使用的任何QStyle - 在这种情况下好看的QMacStyle我一直希望保留。人们可以在例如此处看到实现细节。 qtbase/src/widgets/kernel/qapplication.cpp方法中的QApplication::setStyleSheet()。 QStyleSheetStyle()本身位于qtbase/src/widgets/styles/qstylesheetstyle.cpp中。作为一个琐事点,QStyleSheetStyle()派生自QWindowsStyle(),因此在我原始问题中修改按钮的窗口外观。

简短的故事是Qt StyleSheets是在QStyle()之上实现的。调用setStyleSheet()会隐式地决定您将使用QStyleSheetStyle()而不是QMacStyle()。

带回家的消息是,在设计样式小部件时,您需要选择您的方法,无论采用何种方式进行权衡。您需要选择要使用的QStyle()。如果在Mac上运行默认代码,则选择从QMacStyle()开始。然后,您可以根据现有文档修改该QStyle()。如果调用setStyleSheet(),则隐式选择从QStyleSheetStyle()开始,默认情况下它具有QWindowsStyle()的外观。然后应用的任何样式表属性值都将修改该外观。

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

在一个理想的世界中,某人已经经历过这种练习 - 即将qtbase/src/widgets/styles/qmacstyle_mac.mm消化到其等效的样式表中 - 然而这项工作似乎很麻烦,似乎没有人做过,至少不公开发布。我想另一个远程可行的选择是基于QMacStyle而不是QWindowsStyle重新实现QStyleSheetStyle,然后以某种方式将结果重新集成到代码中(可能有一个setStyleSheet(QStyle * baseStyle,QString styleSheet))。然而,这远远超出了新手的Qt技能,更不用说当前的使命了。