如何在 Qt 中设置按钮背景颜色,甚至覆盖平台主题?

Posted

技术标签:

【中文标题】如何在 Qt 中设置按钮背景颜色,甚至覆盖平台主题?【英文标题】:How can I set button background colours in Qt, even overriding the platform theme? 【发布时间】:2019-09-07 15:42:19 【问题描述】:

我正在构建一个NES tile editor,我想为调色板按钮设置背景颜色。这很好用,

    def set_colour(self, colour_idx):
        self.colour_idx = colour_idx

        bgcolour = NES_PALETTE[colour_idx]
        qt_colour = QtGui.QColor(bgcolour)
        [r, g, b] = qt_colour.red(), qt_colour.green(), qt_colour.blue()
        luminance = (0.2126*r + 0.7152*g + 0.0722*b)/256
        textcolour = 'white' if luminance < 0.5 else 'black'

        self.setText(f"colour_idx:02X")
        self.setStyleSheet(f"color: textcolour; background-color: bgcolour;")

在大多数情况下,给我一个非常漂亮的结果。

问题是 Qt 不服从我的系统 GTK2 黑暗主题。当我尝试通过设置 QT_QPA_PLATFORMTHEME=gtk2 环境变量来实现它时,结果看起来像这样:

按钮的颜色似乎都被系统主题覆盖了。

我想也许我可以更加坚持,并通过调用 setPalette 而不是使用 CSS 来确保系统主题不会覆盖我的按钮颜色:

    def set_colour(self, colour_idx):
        self.colour_idx = colour_idx

        bgcolour = NES_PALETTE[colour_idx]
        qt_colour = QtGui.QColor(bgcolour)
        [r, g, b] = qt_colour.red(), qt_colour.green(), qt_colour.blue()
        luminance = (0.2126*r + 0.7152*g + 0.0722*b)/256
        textcolour = 'white' if luminance < 0.5 else 'black'

        self.setText(f"colour_idx:02X")
        pal = self.palette()
        pal.setColor(QtGui.QPalette.Button, qt_colour)
        self.setPalette(pal)
        self.setStyleSheet(f"color: textcolour;")

但这并没有什么不同。这是一个Qt错误吗?难道我做错了什么?我在想也许我需要制作自己的自定义小部件,这样它的颜色就不会被 Qt 主题覆盖,但我想知道是否还有其他解决方案。

编辑:我也尝试定义自己的paintEvent 方法,

    def paintEvent(self, event):
        super().paintEvent(event)
        self.setAutoFillBackground(True)

        bgcolour = NES_PALETTE[self.colour_idx]
        qt_colour = QtGui.QColor(bgcolour)
        palette = self.palette()
        palette.setColor(QtGui.QPalette.Background, qt_colour)
        self.setPalette(palette)

产生了这个结果:

是 GTK+ 是绘制按钮的问题吗?

【问题讨论】:

我没有答案,但是如果按钮后面的颜色确实反映了按钮的含义,那么颜色选择器很棒。 @DavidCulbreth 是的,当然,按钮的颜色显示了您要为瓷砖的 4 色调色板选择什么颜色。索引是 NES 能够显示的 6 位颜色索引,有一些重复(许多 NES 颜色索引映射到黑色)。 【参考方案1】:

除了在应用程序范围内设置样式(或者更好的是,实际需要的单个小部件,同时保持其他 GUI 元素的操作系统样式一致性)之外,只要您使用所有必需的参数,仍然可以使用样式表。对于按钮(我想是任何 QAbstractButton 后代),边框组件是强制性的,以确保应用样式表。

def setColor(self, qt_colour):
    r, g, b = qt_colour.red(), qt_colour.green(), qt_colour.blue()
    luminance = (0.2126*r + 0.7152*g + 0.0722*b)/256
    textcolour = 'white' if luminance < 0.5 else 'black'
    self.setStyleSheet('''QPushButton 
        color: ;
        background: ;
        border: 2px outset ;
        border-radius: 4px;
        
    QPushButton:pressed 
        border-style: inset;
    '''.format(textcolour, qt_colour.name(), qt_colour.darker().name()))

另外,请记住,QPalette 和样式表不能很好地配合使用。您必须选择使用调色板或样式表,因为同时使用它们可能会导致意外行为,这取决于样式和小部件类型。

除此之外,paintEvent 测试不起作用的原因是,当您使用super().paintEvent(event) 时,按钮已经完全绘制,之后您只需设置与您在以前的set_colour 每次按钮都需要“绘制”时,这种情况经常发生:当它第一次显示时,当它被任何其他窗口隐藏后显示时,当鼠标进入或退出它,等等... 由于显而易见的原因,不应该这样做:更改调色板已经需要重新绘制小部件,这将使 Qt 再次调用paintEvent。幸运的是,Qt 确保新的调色板在应用之前实际上与现有的不同(否则你会得到一个递归),但无论如何,paintEvent 并不是这样做的最佳位置。

【讨论】:

谢谢!这似乎也有效。您如何知道“只要您使用所有必需的参数,仍然可以使用样式表”?什么是“必需”参数,是否记录在案? 您不能同时组合 QSS 和 QStyle,因为 QSS 在内部通过消除您尝试使用的 QStyle 来创建基于 QSS 的 QStyle。 @JordiGutiérrezHermoso 我只是通过反复试验才发现的;据我所知,没有关于此的官方文档,因为它取决于小部件以及样式如何实现其绘图功能和指标。一些小部件有“更简单”的要求(通常是:边框、颜色和背景),您几乎可以肯定它们将始终受到尊重。唯一实际的问题是您还应该设置一些选择器;例如,通常强烈建议使用:disabled(以确保其视觉表示反映其启用状态)或:pressed 用于按钮。 @eyllanesc:根据我的测试,您可以将 QSS 和 QStyle 结合起来,因为 QSS 表现为某种“虚拟风格”,并且实际上是“应用于”当前样式:只要没有为小部件样式提供足够的“信息”,它就会自动回退到当前(默认、显式或命令行)样式集。您甚至可以设置样式表并在之后更改样式:只要 [未] 设置小部件表参数(该小部件/样式组合所需),它们就会根据样式应用。 @musicamante 如果您检查 Qt 代码(我在几个月前做过以了解 QSS 和 QStyle 如何干扰),您会发现 QSS 使用“样式表”创建样式,并且根据样式可以可以组合,但不能保证所有样式。因此,根据每种样式,您可以修改“QSS 样式”不会覆盖的一些小部分。【参考方案2】:

这似乎是原生样式的限制。他们使用像素图,因此他们的着色能力有限:

https://bugreports.qt.io/browse/QTBUG-11089

解决方法是在需要重新着色的小部件上强制使用非原生样式,如下所示:

PLASTIQUE = QtWidgets.QStyleFactory().create("Plastique") 
# Native styles sometimes don't allow recolouring the
# background, see https://bugreports.qt.io/browse/QTBUG-11089
self.setStyle(PLASTIQUE)

这产生了以下结果:

【讨论】:

对,平台主题并不总是遵循设置的全局(QApplication)QPalette 颜色。最可定制的内置样式(恕我直言)是 Fusion(这些天甚至默认包含 Plastique 吗?)。平台主题的另一个选择是使用样式表(然后实际上将 QStylesheetStyle 用于样式元素而不是平台样式)。另一个(不太实用的)选项是在单个 QWidget 上设置 QPalette,由于某种原因,这使得它们即使在平台主题上也遵循这些颜色。 @MaximPaperno 除非我做错了,否则我在原始问题描述中的单个小部件上尝试了样式表和 QPalettes,但它们都没有改变 GTK2 小部件的颜色。 请记住,样式也可以应用于单个小部件。这使您可以使应用程序看起来与操作系统范围的系统保持一致,并仅将备用样式应用于您需要的那些小部件。 那...正是我在这个答案中所做的,不是吗?

以上是关于如何在 Qt 中设置按钮背景颜色,甚至覆盖平台主题?的主要内容,如果未能解决你的问题,请参考以下文章

VC++如何改变按钮背景色

如何更改MFC按钮的背景色

Qt 5.1.1 中设置背景,背景为黑色,怎么解决?

如何在 Bootstrap 4 和 Sass 中设置自定义按钮文本颜色?

qt中怎样在stylesheet中设置按钮的背景色

如何在颤动中设置滚动条颜色?