Я расскажу как они делаются. Хоть код будет на python, то разобраться в нем не составит труда -- т.к. в нем используется Qt и отличия будут только в синтаксисе, а он в python очень понятный. Попросили меня в теме (http://www.prog.org.ru/index.php?topic=30916.msg229005#msg229005) приложить пример.
В конце можно будет скачать архив с скриптом.
Делегатами являются объекты, который стоят между данными таблицы и пользователем. Они обеспечивают взаимодействие и отображают данные.
Вообще, информации по делегатам в Qt очень много, кроме официальной документации, видел еще статьи на хабре.
Делегаты мы используем, если хотим сделать что-то нестандартное, например из официальной документации Star Delegate Example (http://doc.qt.io/qt-5/qtwidgets-itemviews-stardelegate-example.html)
(http://doc.qt.io/qt-5/images/stardelegate.png)
Выглядит очень здорово :)
А вот так будет выглядеть делегаты, которые я опишу в этом примере:
(https://github.com/gil9red/SimplePyScripts/raw/master/image%20on%20whole%20cell%20in%20QTableWidget/screenshot.png)
Теперь к делу.
Делегаты создаются наследование класса QStyledItemDelegate (http://doc.qt.io/qt-4.8/qstyleditemdelegate.html) и переопределением определенных методов.
Для текущего примера будет переопределен метод
paint и в нем реализована своя логика рисования. А рисовать будем то, что окажется в Qt::DecorationRole (http://doc.qt.io/qt-4.8/qt.html#ItemDataRole-enum) (это иконка в ячейке).
А чтобы делегат начал использоваться его нужно установить для этого у базового класса представлений QAbstractItemView (http://doc.qt.io/qt-4.8/qabstractitemview.html) есть методы: setIndexWidget, setItemDelegate, setItemDelegateForColumn и setItemDelegateForRow.
Первый -- установка делегата в выбранную ячейку, второй -- для всего представлений (список, таблица, дерево, и т.п.) и последнии -- для выбранных строк и столбцов.
Я опишу подробно делегат для второго столбца из примера (Delegate v1), потому что разница между приведенными делегатами почти нулевая.
Python
class MyDelegate_1(QStyledItemDelegate):
def paint(self, painter, option, index):
# Получение ссылки на изображение в иконке, если его нет, тогда вызывается родительский метод рисования
img = index.model().data(index, Qt.DecorationRole)
if img is None:
super().paint(painter, option, index)
return
# Получение размеров ячейки и растягивание иконки на размер ячейки
rect = option.rect
w, h = rect.size().width(), rect.size().height()
img = img.scaled(w, h, Qt.KeepAspectRatio, Qt.SmoothTransformation)
painter.drawPixmap(rect, img)
item_option = QStyleOptionViewItem(option)
self.initStyleOption(item_option, index)
# Обработка при выделении ячейки делегата
# Рисуем выделение полупрозрачным чтобы было видно нарисованное ранее
if item_option.state & QStyle.State_Selected:
# Получаем цвет, используемый при выделении ячеек
color = item_option.palette.color(QPalette.Highlight)
# Делаем его полупрозрачным
color.setAlpha(180)
# Сохранение состояния рисовальщика, изменение состояния, рисование и восстановление старого состояния
painter.save()
painter.setPen(Qt.NoPen)
painter.setBrush(color)
painter.drawRect(rect)
painter.restore()
# Если хотим что-то дорисовать (например текст)
# super().paint(painter, option, index)
Вообще, этот код я брал и адаптировал (https://github.com/gil9red/combustion/blob/14536c01978d2f1590a308505696a1e4755621ad/linedays_celldelegate.cpp#L54)из другой программы на с++, которую делал.
Весь код, для тех кто не может скачать:
Python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__author__ = 'ipetrash'
try:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
except:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
# Для отлова всех исключений, которые в слотах Qt могут "затеряться" и привести к тихому падению
def log_uncaught_exceptions(ex_cls, ex, tb):
text = '{}: {}:\n'.format(ex_cls.__name__, ex)
import traceback
text += ''.join(traceback.format_tb(tb))
print('Error: ', text)
QMessageBox.critical(None, 'Error', text)
quit()
import sys
sys.excepthook = log_uncaught_exceptions
def create_item(img):
item = QTableWidgetItem()
item.setData(Qt.DecorationRole, img)
return item
class MyDelegate_1(QStyledItemDelegate):
def paint(self, painter, option, index):
img = index.model().data(index, Qt.DecorationRole)
if img is None:
super().paint(painter, option, index)
return
rect = option.rect
w, h = rect.size().width(), rect.size().height()
img = img.scaled(w, h, Qt.KeepAspectRatio, Qt.SmoothTransformation)
painter.drawPixmap(rect, img)
item_option = QStyleOptionViewItem(option)
self.initStyleOption(item_option, index)
# Обработка при выделении ячейки делегата
# Рисуем выделение полупрозрачным чтобы было видно нарисованное ранее
if item_option.state & QStyle.State_Selected:
color = item_option.palette.color(QPalette.Highlight)
color.setAlpha(180)
painter.save()
painter.setPen(Qt.NoPen)
painter.setBrush(color)
painter.drawRect(rect)
painter.restore()
# Если хотим что-то дорисовать (например текст)
# super().paint(painter, option, index)
class MyDelegate_2(QStyledItemDelegate):
def paint(self, painter, option, index):
img = index.model().data(index, Qt.DecorationRole)
if img is None:
super().paint(painter, option, index)
return
rect = option.rect
x, y = rect.x(), rect.y()
painter.drawPixmap(x, y, img)
painter.drawPixmap(x + 16, y, img)
painter.drawPixmap(x + 32, y, img)
painter.drawPixmap(x + 48, y, img)
painter.drawPixmap(x + 64, y, img)
painter.drawPixmap(x + 80, y, img)
painter.drawPixmap(x, y + 16, img)
painter.drawPixmap(x + 16, y + 16, img)
item_option = QStyleOptionViewItem(option)
self.initStyleOption(item_option, index)
# Обработка при выделении ячейки делегата
# Рисуем выделение полупрозрачным чтобы было видно нарисованное ранее
if item_option.state & QStyle.State_Selected:
color = item_option.palette.color(QPalette.Highlight)
color.setAlpha(180)
painter.save()
painter.setPen(Qt.NoPen)
painter.setBrush(color)
painter.drawRect(rect)
painter.restore()
# # Если хотим что-то дорисовать (например текст)
# super().paint(painter, option, index)
if __name__ == '__main__':
app = QApplication([])
table = QTableWidget()
table.setSelectionBehavior(QTableView.SelectRows)
table.show()
table.resize(400, 200)
headers = ['Normal', 'Delegate v1', 'Delegate v1 (without img)', 'Delegate v2']
table.setColumnCount(len(headers))
table.setHorizontalHeaderLabels(headers)
table.setRowCount(3)
table.verticalHeader().hide()
pix_1 = QPixmap('favicon_google.png')
pix_2 = QPixmap('favicon_prog_org.png')
pix_3 = QPixmap('favicon_google_tr.png')
for col in range(table.columnCount()):
if col == 2:
table.setItem(1, col, QTableWidgetItem())
else:
table.setItem(0, col, create_item(pix_1))
table.setItem(1, col, create_item(pix_2))
table.setItem(2, col, create_item(pix_3))
delegate_1 = MyDelegate_1()
delegate_2 = MyDelegate_2()
table.setItemDelegateForColumn(1, delegate_1)
table.setItemDelegateForColumn(2, delegate_1)
table.setItemDelegateForColumn(3, delegate_2)
app.exec()