PyQt5 QTableView выделенный фон ячейки с делегатом - PullRequest
0 голосов
/ 02 февраля 2019

У меня есть приложение, которое использует комбинацию 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 для создания пользовательского интерфейса.Несмотря на то, что я указываю app.setStyle('fusion'), таблица имеет совершенно другой стиль и отличается от фона выделенной ячейки:

Qt Designer table

Я могуНе для меня жизнь выяснить, где он подбирает другой стиль.Он должен каким-то образом исходить от Qt Designer, но я посмотрел на файл .py, который создает Qt Designer, и не могу его найти.

Этот стиль (откуда бы он ни исходил), похоже, страдает от того жепроблема как стиль windows.На изображении выше делегат не используется.Ячейка в строке 2 / столбце 2 выбрана, и фон показывает.

Но если я добавлю делегат для отображения QPixmap в столбце 2, то фон не будет отображаться при выборе ячейки:

Qt Designer table with Delegate

(он выбран; поверьте мне на слово).

Я подумал, может быть, это тот случай, когда вы используете делегата дляотображая изображение, вы больше не можете получить фон в выбранной ячейке.Но вы, очевидно, можете.Это работает в одном случае, но не в других.

Если кто-то может пролить свет на это, я был бы признателен.(Я понимаю, что это долго; спасибо, что остались со мной).

1 Ответ

0 голосов
/ 04 февраля 2019

Я больше возился с этой проблемой, и я кое-что узнал о своем первоначальном вопросе.Оглядываясь назад, я думаю, что это было не так ясно, как могло бы быть (или, может быть, я просто все понимаю немного лучше).

Для начала, я никогда не должен был называть клетки "выбран ".На самом деле, у меня даже не установлен флаг 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(), который отображает растровое изображение.Поэтому paint() также должен нести ответственность за установку фона для ячеек в этом столбце, когда они активны.Он использует тот же backgroundBrush, который установлен initStyleOption().

Результат очень близок к тому, что я снимаю.Единственная ложка дегтя заключается в том, что все еще явно выполняется дополнительная стилизация, которая затрагивает все ячейки в представлении, кроме тех, которые в столбце 1 с пользовательским делегатом.Таким образом, они не выглядят совсем абсолютно одинаковыми, когда активны:

Column with Delegate Column without Delegate

(Этотонко, но есть некоторый градиент к фону ячейки в столбце 2, который отсутствует в столбце 1).

Теперь я знаю, что существуют "фабрики" стилей, которые применяют стиль всего виджета,Поскольку я использую Fusion, то, очевидно, отсюда и возникает дополнительный стиль.

Итак, теперь мой вопрос - где определен этот стиль, и могу ли я его контролировать?Если бы я мог видеть это, я мог бы сделать так, чтобы мой собственный стиль фона соответствовал этому.Еще лучше, если бы я мог изменить это, я мог бы заставить это соответствовать моему.

...