2025, Sep 24 19:16

Как настроить делегат и палитру редактора в QTableView PySide6

Почему в PySide6 редактор QTableView показывает чёрный текст на тёмном фоне при светлой теме ОС и как это исправить: делегат, палитра Base/Text/Highlight.

Редактирование текста в QTableView в окне с тёмным оформлением может выглядеть безупречно в режиме отображения и внезапно становиться нечитаемым, как только пользователь начинает вводить текст. Если в вашем приложении на PySide6 родительскому QDialog задан тёмный фон, а модель рисует текст ячеек белым, то при светлой теме ОС редактор, появляющийся в режиме редактирования, всё равно может показывать чёрный текст — на тёмном фоне его почти не видно. Ниже — почему так происходит и как это аккуратно исправить.

Исходная ситуация

Окно использует тёмный фон через stylesheet, заголовок таблицы прозрачный, модель рисует текст ячеек белым, элементы доступны для редактирования. В коде это выглядит так:

from PySide6 import QtCore, QtGui, QtWidgets

dlg = QtWidgets.QDialog()
dlg.setStyleSheet("background-color: #11375E;")

view = QtWidgets.QTableView(dlg)
view.setStyleSheet(
    "QHeaderView::section {\n"
    "background-color: transparent;\n"
    "border-style: hidden\n"
    "}"
)

class GridModel(QtCore.QAbstractTableModel):
    def data(self, ix, role):
        match role:
            case QtCore.Qt.ItemDataRole.ForegroundRole:
                return QtGui.QBrush(QtGui.QColorConstants.White)
        return super().data(ix, role)

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

В режиме отображения всё выглядит корректно и на тёмной, и на светлой системной теме. Проблема возникает только при редактировании: на светлой теме вводимый текст становится чёрным на тёмном фоне и его тяжело читать.

Что на самом деле происходит в режиме редактирования

Когда ячейка переходит в режим редактирования, представление не перекрашивает уже отрисованную ячейку. Вместо этого создаётся отдельный редактор — по умолчанию это безрамочный QLineEdit для строк — который размещается поверх ячейки. Редактор — обычный виджет и наследует палитру от родителя. Возвращаемый моделью ForegroundRole на внешний вид этого редактора не влияет. Точно так же QSS, применённый к представлению или заголовку, не обязан распространяться на отдельно созданный редактор.

Если палитра не согласована с вашим тёмным фоном, светлая системная тема даст чёрный текст редактирования на тёмной подложке. Чтобы сделать внешний вид редактора一致ным, палитрой нужно управлять явно.

Решение: делегат редактора и настройка его палитры

Самый прямой способ — настроить создаваемый редактор. Переопределите createEditor в QStyledItemDelegate, задайте нужные цвета и установите делегат на таблицу, чтобы он применялся повсюду.

from PySide6 import QtCore, QtGui, QtWidgets

class EditTintDelegate(QtWidgets.QStyledItemDelegate):
    def createEditor(self, host, opt, ix):
        editor_widget = super().createEditor(host, opt, ix)
        pal = editor_widget.palette()
        pal.setColor(QtGui.QPalette.Base, QtGui.QColor("#11375E"))
        pal.setColor(QtGui.QPalette.Text, QtCore.Qt.white)
        pal.setColor(QtGui.QPalette.Highlight, QtGui.QColor("#1b4f80"))
        pal.setColor(QtGui.QPalette.HighlightedText, QtCore.Qt.white)
        editor_widget.setPalette(pal)
        editor_widget.setStyleSheet("color: white;")
        return editor_widget

# apply to the whole table
view.setItemDelegate(EditTintDelegate(view))

Так встроенный редактор становится визуально согласованным с тёмным окном: вводимый текст белый, а выделение остаётся читабельным.

Почему это работает

Редактор — отдельный виджет, и прямое стилизование исключает сюрпризы, зависящие от темы. Задавая Base и Text, вы контролируете фон и цвет текста редактора. Параметры Highlight и HighlightedText отвечают за видимость выделения. Поскольку делегат создаёт редактор, изменения локализованы и последовательно применяются ко всем редактируемым ячейкам.

Когда настраивать палитру на уровне окна

Если весь диалог выполнен в тёмной гамме, разумно настроить палитру для корневого окна. Присвойте Window ваш фон и согласуйте Base с тем же цветом или даже QColorConstants.Transparent, а для WindowText, ButtonText и Text используйте белый. Так вы зададите единый базовый стиль для наследуемых виджетов. Этот подход решает не только вопрос с редактором таблицы и снижает зависимость от разрозненных правил QSS.

Выводы

При редактировании в QTableView создаётся реальный виджет-редактор, поэтому роли модели и стили представления не гарантируют читаемость текста при смене темы. Управляйте палитрой редактора через QStyledItemDelegate, чтобы получить предсказуемые цвета, и при необходимости определите палитру родительского окна для общей визуальной целостности. Так таблица останется удобочитаемой и в тёмной, и в светлой среде, без регрессий, зависящих от темы.

Статья основана на вопросе на StackOverflow от user2965433 и ответе Ahrimann Steiner.