PyQt5 QTableView具有委托的选定单元格背景

时间:2019-02-02 05:16:54

标签: pyqt5 qtableview

我有一个使用QTableView / QAbstractTableModel组合的应用程序。对于视图,我定义了一个Delegate,它在表视图的一列中显示图像(从图像文件加载的QPixmap)。

基本上,问题在于,当选中具有“代表”的列中的单元格时,有时会显示背景,有时却不会。

到目前为止,这是我通过实验发现的,但我对此没有多大了解:

我有此相对短的测试程序:

from PyQt5 import QtCore, QtWidgets, QtGui
import sys


# ------------------------------------------------------------------------------
class TableModel(QtCore.QAbstractTableModel):

    def __init__(self, data = [[]], headers = None, parent = None):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self.__data = data

    def rowCount(self, parent):
        return len(self.__data)

    def columnCount(self, parent):
        return len(self.__data[0])

    def data(self, index, role):
        row = index.row()
        column = index.column()
        if role == QtCore.Qt.DisplayRole:
            value = self.__data[row][column]
            return value

    def flags(self, index):
        return QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsEditable|QtCore.Qt.ItemIsSelectable


# ------------------------------------------------------------------------------
class Delegate(QtWidgets.QStyledItemDelegate):

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

        if (index.column() == 0):
            image = QtGui.QImage('open.png')
            pixmap = QtGui.QPixmap.fromImage(image)

            x = option.rect.center().x() - pixmap.rect().width() / 2
            y = option.rect.center().y() - pixmap.rect().height() / 2
            painter.drawPixmap(x, y, pixmap)


# ------------------------------------------------------------------------------
if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    app.setStyle('fusion')

    tableView = QtWidgets.QTableView()
    tableView.setItemDelegateForColumn(0, Delegate())
    tableView.resize(550, 160)
    tableView.show()

    rowCount = 3
    columnCount = 4
    data = [
        [i for i in range(columnCount)]
        for j in range(rowCount)
    ]

    model = TableModel(data)
    tableView.setModel(model)

    sys.exit(app.exec_())

当我指定app.setStyle('fusion')__main__,我这是所期望的:当选择与代表所述列的单元格,单元格的背景是蓝色的,图像显示在它的前面:

fusion

但是,如果我更改为app.setStyle('windows'),即使通常它对选定的单元格使用相同的蓝色背景,当我移到第一列中的单元格时,背景也会消失:

windows

(您显然看不到它,但是选择了与第一个示例相同的单元格)。

那只是一段测试代码,我不完全理解。

在我正在编写的实际应用程序中,我正在使用Qt Designer创建UI。尽管我指定app.setStyle('fusion'),该表具有完全不同的造型,具有不同的外观,以所选择的单元格的背景:

Qt Designer table

我无法为自己的一生弄清楚它在挑起不同风格的地方。它必须来自Qt设计莫名其妙,但我已经看过了.py文件Qt设计造成的,我不能找到它。

此式(无论它来自)似乎从相同的问题windows风格的困扰。在上面的图像,存在在使用中没有代表。选择第2行/第2列中的单元格,并显示背景。

但是,如果我添加一个代表显示在第2栏第一个的QPixmap,则背景不当选择了单元显示:

Qt Designer table with Delegate

(已选中;请相信我)

我想也许这是事实,一旦你使用委托来显示图像,你可以不再获得所选单元格的背景。但是你显然可以。它在一种情况下有效,而在其他情况下无效。

如果有人可以阐明这一点,我将不胜感激。 (我知道这很长;感谢您的坚持)。

1 个答案:

答案 0 :(得分:0)

我一直在摆弄这个问题,而且我已经了解了一些有关原始问题的知识。回想起来,我认为它并没有想象的那么清晰(或者也许我对它的理解更好一些)。

对于初学者来说,我永远不应该将单元格称为“被选中” 。实际上,我什至没有为视图中的任何单元格设置Qt.ItemIsSelectable标志。我真正想做的是控制单元格处于活动状态(因为缺少更好的单词)时的背景-这意味着它是光标当前所在的单元格。

这可以通过在委托中覆盖initStyleOption()来完成。我的原始测试代码如下所示进行修改:

from PyQt5 import QtCore, QtWidgets, QtGui
import sys


# ------------------------------------------------------------------------------
class TableModel(QtCore.QAbstractTableModel):

    def __init__(self, data = [[]], headers = None, parent = None):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self.__data = data

    def rowCount(self, parent):
        return len(self.__data)

    def columnCount(self, parent):
        return len(self.__data[0])

    def data(self, index, role):
        row = index.row()
        column = index.column()
        if role == QtCore.Qt.DisplayRole:
            value = self.__data[row][column]
            return value
        if role == QtCore.Qt.BackgroundRole:
            return QtGui.QBrush(QtGui.QColor(255, 255, 255))

    def flags(self, index):
        return QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsEditable


# ------------------------------------------------------------------------------
class TableView(QtWidgets.QTableView):

    def __init__(self, parent=None):
        super().__init__(parent)


# ------------------------------------------------------------------------------
class Delegate(QtWidgets.QStyledItemDelegate):

    # <Modification>
    def initStyleOption(self, option, index):
        super().initStyleOption(option, index)
        if (
            index.row() == tableView.currentIndex().row() and
            index.column() == tableView.currentIndex().column()
        ):
            option.backgroundBrush = QtGui.QBrush(QtGui.QColor(232, 244, 252))

    def paint(self, painter, option, index):        
        if (index.column() == 0):

            # <Modification>
            if (
                index.row() == tableView.currentIndex().row() and
                index.column() == tableView.currentIndex().column()
            ):
                self.initStyleOption(option, index)
                painter.setPen(QtCore.Qt.NoPen)
                painter.setBrush(option.backgroundBrush)
                painter.drawRect(option.rect)

            image = QtGui.QImage('open.png')
            pixmap = QtGui.QPixmap.fromImage(image)
            x = option.rect.center().x() - pixmap.rect().width() / 2
            y = option.rect.center().y() - pixmap.rect().height() / 2
            painter.drawPixmap(x, y, pixmap)

        else:
            super().paint(painter, option, index)



# ------------------------------------------------------------------------------
if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    app.setStyle('fusion')

    tableView = TableView()
    tableView.resize(550, 160)
    tableView.setItemDelegate(Delegate())
    tableView.show()

    rowCount = 3
    columnCount = 4
    data = [
        [i for i in range(columnCount)]
        for j in range(rowCount)
    ]

    model = TableModel(data)
    tableView.setModel(model)

    sys.exit(app.exec_())

initStyleOption()设置单元格处于活动状态(当前)时的背景画笔。但是,正如我之前哀叹的那样,这不会出现在第一列中,该列具有一个带有显示像素图的自定义paint()方法的Delegate。因此,paint()还必须负责为该列中处于活动状态的单元格设置背景。它使用与backgroundBrush设置的相同的initStyleOption()

结果几乎是我要拍摄的。美中不足的是,显然还有其他样式在进行,这会影响视图中的所有单元格,但带有自定义委托的第1列中的单元格除外。因此,它们在活动时看起来并不完全相同:

Column with Delegate Column without Delegate

(这很细微,但第2列中的单元格背景有一些渐变,而第1列中没有该渐变)。

我现在知道有些样式“工厂”应用了小部件范围的样式。由于我使用的是Fusion,因此显然这就是多余样式的来源。

所以现在我的问题是-样式在哪里定义,我对此有任何控制权吗?如果可以看到,则可以使我的自定义背景样式与之匹配。更好的是,如果我可以修改它,可以使其与我的匹配。