【问题标题】:QTableWidget ItemDelegate breaks selection styleQTableWidget ItemDelegate 打破选择风格
【发布时间】:2019-11-30 13:26:41
【问题描述】:

在下面的 Python 2.7,PyQt4 示例中,我生成了 2 个 QTableWidget。 Table1 没有 ItemDelegate,而 table2 有 HTMLDelegate。

如果表格有焦点,则选定的背景颜色有效,但当表格失去焦点时,蓝色选择在 table2 上变为灰色。我希望 table2 选择在失去焦点时像 table1 一样工作。

如何在使用 itemdelegate 时无论焦点如何都保持蓝色选择外观?

import sip
sip.setapi('QVariant', 2)   

from PyQt4 import QtCore, QtGui
import random

from html import escape

words = [
    "Hello",
    "world",
    "Stack",
    "Overflow",
    "Hello world",
]

class HTMLDelegate(QtGui.QStyledItemDelegate):
    def __init__(self, parent=None):
        super(HTMLDelegate, self).__init__(parent)
        self.doc = QtGui.QTextDocument(self)

    def paint(self, painter, option, index):

        col = index.column()
        row = index.row()

        painter.save()

        options = QtGui.QStyleOptionViewItem(option)
        self.initStyleOption(options, index)

        text = index.data()

        self.doc.setHtml(text)

        options.text = ""
        style = (
            QtGui.QApplication.style()
        )
        style.drawControl(QtGui.QStyle.CE_ItemViewItem, options, painter)

        ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()

        if option.state & QtGui.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 = (options.rect)  

        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 QSize(self.doc.idealWidth(), self.doc.size().height())


class Widget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        hlay = QtGui.QHBoxLayout()
        lay = QtGui.QVBoxLayout(self)

        self.table1 = QtGui.QTableWidget(4, 2)
        self.table2 = QtGui.QTableWidget(4, 2)
        lay.addLayout(hlay)
        lay.addWidget(self.table1)
        lay.addWidget(self.table2)

        # define itemdelegate for table1, but not for table2
        self.table2.setItemDelegate(HTMLDelegate(self.table2))

        # fill table1
        for i in range(self.table1.rowCount()):
            for j in range(self.table1.columnCount()):
                it = QtGui.QTableWidgetItem(random.choice(words))
                self.table1.setItem(i, j, it)

        # fill table2
        for i in range(self.table2.rowCount()):
            for j in range(self.table2.columnCount()):
                it = QtGui.QTableWidgetItem(random.choice(words))
                self.table2.setItem(i, j, it)        

if __name__ == "__main__":
    import sys

    app = QtGui.QApplication(sys.argv)

    app.setStyle("Plastique")   # set style

    stylesheet = """
    QPushButton:hover, QComboBox:hover
    {
        background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #cbc9c5, stop: 1 #b9b7b5);
        border: 2px solid #78879b;
        border-radius: 3px;
    }
    QTableWidget::item:selected 
    {
        background: #0078d7;  
        color: white;
    }
    QTableWidget
    {
        font: 9pt "Segoe UI"; 
    }
    QHeaderView
    {
        font: 9pt "Segoe UI"; 
    }
    """    

    app.setStyleSheet(stylesheet)       

    myapp = Widget()     
    myapp.show()           

    rc = app.exec_()

    myapp.close()

    sys.exit(rc)    

【问题讨论】:

    标签: python python-2.7 pyqt pyqt4 qitemdelegate


    【解决方案1】:

    您正在使用QStyleOptionViewItem,它在Qt4 中是一个非常基础的类,而您需要的是QStyleOptionViewItemV4,它实现了很多有用的东西,包括装饰(如物品图标)及其定位,根据其他项目的项目位置,最重要的是,委托使用的小部件及其完整的 QStyle 绘画功能。

    style.drawControl 方法和大多数 QStyle 方法一样,也有一个 widget 参数;它通常会被忽略,但在这种情况下非常重要,尤其是当有样式表在起作用时。

    我建议你使用option的类的paint()方法作为参考来创建你自己的选项,它会自动使用最新的QStyleOption可用于查看项目,也将做一个可能的未来过渡到 Qt5 更容易。

    请记住,根据文档(这有点模糊),小部件属性仅适用于 QStyleOptionViewItem 版本 3,但根据我的测试,正确的背景绘制无论如何都会在该版本中失败。如果出于任何原因,您遇到了不提供 QStyleOptionViewItemV4 的非常旧版 Qt,恐怕您唯一的选择就是保留背景颜色参考某处(无法从代码中访问样式表颜色,并且与 QTableView Highlight 调色板角色不匹配)并自己手动绘制背景。

        def paint(self, painter, option, index):
            #...
            newOption = option.__class__(option)
            self.initStyleOption(newOption, index)
            #...
            style = newOption.widget.style() if newOption.widget else QtGui.QApplication.style()
            style.drawControl(QtGui.QStyle.CE_ItemViewItem, newOption, painter, newOption.widget)
            # ...
    

    PS:我建议您不要为对象使用过于相似的名称:实际上我在寻找问题根源时迷失了方向,因为我经常混淆“选项”和“选项s” : 这就是我改变它的原因,这对于可读性和调试目的也更好。

    【讨论】:

    • 我最初是从另一个伟大的堆栈溢出用户@ellaynesc 那里得到这段代码的...stackoverflow.com/q/57062107/259538
    • 是的,但该代码基于 Qt5,它实现了 QStyleOptionViewItemV4 使用的所有内容。如前所述,如果您在 Qt4 上使用普通的 QStyleOptionViewItem,您将面临您正在谈论的问题。
    • @ellaynesc 在stackoverflow.com/a/51615332/259538 的帖子中提供了 Qt5 和 Qt4 版本的源代码。我希望在 Qt4 中找到选择样式问题的解决方案,因为我的环境需要它,我目前无法迁移到 Qt5。
    • 抱歉,不记得那部分了。无论如何,我仍然不明白我的解决方案有什么问题......
    • 啊好吧 :-) 不客气!无论如何,您可以在小部件没有焦点时使用QTableWidget::item:selected:!active来控制所选项目的背景,但是仍然需要drawControl()的小部件参数和StyleOption的v4,您需要使用option.palette.color(QtGui.QPalette.Active if option.widget.hasFocus() else QtGui.QPalette.Inactive来确保使用正确的文本颜色。
    猜你喜欢
    • 2013-01-08
    • 2013-05-05
    • 2012-11-09
    • 1970-01-01
    • 2014-02-15
    • 1970-01-01
    • 1970-01-01
    • 2019-12-16
    • 2017-09-04
    相关资源
    最近更新 更多