Russian Qt Forum
Март 28, 2024, 21:27 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Написание пользовательских делегатов для таблицы (python3 Qt4/Qt5)  (Прочитано 9240 раз)
gil9red
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 1805



Просмотр профиля WWW
« : Январь 23, 2017, 09:09 »

Я расскажу как они делаются. Хоть код будет на python, то разобраться в нем не составит труда -- т.к. в нем используется Qt и отличия будут только в синтаксисе, а он в python очень понятный. Попросили меня в теме приложить пример.
В конце можно будет скачать архив с скриптом.

Делегатами являются объекты, который стоят между данными таблицы и пользователем. Они обеспечивают взаимодействие и отображают данные.
Вообще, информации по делегатам в Qt очень много, кроме официальной документации, видел еще статьи на хабре.

Делегаты мы используем, если хотим сделать что-то нестандартное, например из официальной документации Star Delegate Example



Выглядит очень здорово Улыбающийся

А вот так будет выглядеть делегаты, которые я опишу в этом примере:




Теперь к делу.

Делегаты создаются наследование класса QStyledItemDelegate и переопределением определенных методов.
Для текущего примера будет переопределен метод paint и в нем реализована своя логика рисования. А рисовать будем то, что окажется в Qt::DecorationRole (это иконка в ячейке).

А чтобы делегат начал использоваться его нужно установить для этого у базового класса представлений QAbstractItemView есть методы: 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)
 

Вообще, этот код я брал и адаптировал из другой программы на с++, которую делал.


Весь код, для тех кто не может скачать:
Код
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()
 
 
« Последнее редактирование: Январь 23, 2017, 12:55 от gil9red » Записан

Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.102 секунд. Запросов: 22.