PyQt5:当列表失去焦点时设置 QListWidget 选择颜色

Posted

技术标签:

【中文标题】PyQt5:当列表失去焦点时设置 QListWidget 选择颜色【英文标题】:PyQt5: setting QListWidget selection color when the list loses focus 【发布时间】:2021-01-28 14:58:55 【问题描述】:

我编写了一个带有两个 QListWidgets 的小型 PyQt5 应用程序,如图所示。我已经设置了“融合”样式以获取具有背景颜色的组合框,并且作为一个不受欢迎的结果,我遇到了 QListWidget 选择颜色的问题:当它有焦点时,选择有蓝色背景,这非常好,但是会变亮列表失去焦点时的灰色背景(如左侧列表所示),使其难以阅读。

我基于 QTableWidgets 的类似 sn-ps 在小部件上尝试了不同的 CSS 样式组合,但没有成功。

知道如何更改此背景颜色吗?

编辑:鉴于提议的解决方案不起作用,我已经针对您的测试寻找可能的差异。这可能是由于使用了我从 How to display partially bold text in QListWidgetItem with QtCore.Qt.UserRole 获得的自定义 QStyledItemDelegate

from PyQt5 import QtCore, QtGui, QtWidgets


class htmlDelegate(QtWidgets.QStyledItemDelegate):
    '''
    The roles >= Qt::UserRole are not used by Qt by default so it can be used for any purpose,
    for example to save additional information, in this case it is not the solution.
    One possible solution is to use a delegate to render the HTML.
    (Extracted from https://***.com/questions/53569768/how-to-display-partially-bold-text-in-qlistwidgetitem-with-qtcore-qt-userrole)
    '''
    def __init__(self, parent=None):
        super(HTMLDelegate, self).__init__(parent)
        self.doc = QtGui.QTextDocument(self)

    def paint(self, painter, option, index):
        painter.save()
        options = QtWidgets.QStyleOptionViewItem(option)
        self.initStyleOption(options, index)
        self.doc.setHtml(options.text)
        options.text = ""
        style = QtWidgets.QApplication.style() if options.widget is None \
            else options.widget.style()
        style.drawControl(QtWidgets.QStyle.CE_ItemViewItem, options, painter)

        ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()
        if option.state & QtWidgets.QStyle.State_Selected:
            ctx.palette.setColor(QtGui.QPalette.Text, option.palette.color(
                QtGui.QPalette.Active, QtGui.QPalette.HighlightedText))
        else:
            ctx.palette.setColor(QtGui.QPalette.Text, option.palette.color(
                QtGui.QPalette.Active, QtGui.QPalette.Text))
        textRect = style.subElementRect(QtWidgets.QStyle.SE_ItemViewItemText, options, None)
        if index.column() != 0:
            textRect.adjust(5, 0, 0, 0)
        constant = 4
        margin = (option.rect.height() - options.fontMetrics.height()) // 2
        margin = margin - constant
        textRect.setTop(textRect.top() + margin)

        painter.translate(textRect.topLeft())
        painter.setClipRect(textRect.translated(-textRect.topLeft()))
        self.doc.documentLayout().draw(painter, ctx)
        painter.restore()

    def sizeHint(self, option, index):
        return QtCore.QSize(self.doc.idealWidth(), self.doc.size().height())

因此,我想我应该修改为 QPalette 设置颜色的部分。尽管如此,这里没有使用 QStyle::State_HasFocus,所以我不明白为什么它不起作用。现在知道如何解决了吗?

作为一个并行问题:QT 小部件及其子元素的所有 CSS 可能性的定义在哪里?我希望能够自己探索它,而不是在将来用这种简单的 CSS 代码问题来打扰 *** 用户:)

【问题讨论】:

您还可以在 if 语句中与& QtWidgets.QStyle.State_Active 进行比较,这样至少非活动的选定文本不会是白色的。我现在会尝试一些事情。同时这里是style sheets ref 【参考方案1】:

当涉及使用 QPalette 颜色作为参考的自定义绘图时,您不能依赖样式表:样式表覆盖样式,并且每当指定颜色时,调色板也会被忽略(至少对于状态/样式表中指定的属性)。

调色板对样式表一无所知,实际上恰恰相反:样式表使用调色板(实际上,您可以使用palette roles in stylesheets)。 无法以编程方式访问样式表中使用的颜色。

你有两种可能:

    在没有焦点时更改突出显示项目的背景颜色,在绘制项目之前
        if not option.state & QtWidgets.QStyle.State_HasFocus:
            options.palette.setColor(QtGui.QPalette.Highlight, QtCore.Qt.darkGray)
        style.drawControl(QtWidgets.QStyle.CE_ItemViewItem, options, painter)
        # ...
    仅当项目被选中并且有焦点时才设置文本颜色:
        if (option.state & QtWidgets.QStyle.State_Selected and 
            option.state & QtWidgets.QStyle.State_HasFocus):
                ctx.palette.setColor(QtGui.QPalette.Text, option.palette.color(
                    QtGui.QPalette.Active, QtGui.QPalette.HighlightedText))
        else:
            # ...

【讨论】:

但是这个解决方案解决的是文本颜色,而不是背景颜色,这是我的意图。当然,总比没有好,但是我不能更改失去焦点的选择的背景颜色,以便它匹配用于焦点选择的颜色吗? 第一个解决方案确实改变了背景颜色。我使用了QtCore.Qt.darkGray,但您可以使用任何您想要的颜色(Qt 标准颜色或 QColor)。在任何情况下,我都不建议您对焦点视图和非焦点视图都使用完全相同的相同颜色,因为这会造成混淆(用户必须能够看到小部件乍一看有焦点)。【参考方案2】:

可以在::item子控件、:selected伪状态、:!active伪状态中设置。

QListWidget::item:selected:!active 
    background: #17d;

测试示例:

import sys
from PyQt5.QtWidgets import *

if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyle('fusion')
    app.setStyleSheet('''
    QListWidget::item:selected:!active 
        background: lightBlue;
        color: black;
    ''')
    items = ['2020-11-27'] * 6
    x = QListWidget(); x.addItems(items)
    y = QListWidget(); y.addItems(items)
    window = QWidget()
    hbox = QHBoxLayout(window)
    hbox.addWidget(x); hbox.addWidget(y)
    window.show()
    sys.exit(app.exec_())

【讨论】:

不工作。还尝试单独为列表小部件设置它,而不是将其设置为整个应用程序,但没有成功。我能想到的与您的测试的唯一区别是我使用自定义 ItemDelegate 来显示 HTML。我已经编辑了我的问题。

以上是关于PyQt5:当列表失去焦点时设置 QListWidget 选择颜色的主要内容,如果未能解决你的问题,请参考以下文章

在 ForEach 循环中绑定时,如何阻止 SwiftUI TextField 失去焦点?

如何失去 QTextEdit 的选择焦点?

axure7.0如何实现光标在文本框中获得焦点时,文本框内容自动清空。失去焦点时显示默认文字,谢谢!

当滚动列表的时候,让input框失去焦点(移动端会收起键盘)

在 jQuery 自动完成下拉列表中选择选项时失去焦点

编辑值时输入失去焦点。使用 ngFor 和 ngModel。角5