QTableWidget ItemDelegate 打破了选择样式

QTableWidget ItemDelegate breaks selection style

在下面的Python2.7、PyQt4例子中,我生成了2个QTableWidgets。 Table1 没有 ItemDelegate,table2 有 HTMLDelegate。

如果 table 具有焦点,则所选背景颜色有效,但当 table 失去焦点时,蓝色选择在 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)    

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

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

我建议你参考paint()方法的option的class来创建你自己的选项,它会自动使用最新的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:我建议你不要为对象使用太相似的名称:实际上我有时会找不到问题的根源,因为我经常在 "option" 和 [=34 之间混淆=]:这就是我更改它的原因,这对于可读性和调试目的来说也更好。