Russian Qt Forum

Программирование => Python => Тема начата: Dr.robot от Январь 16, 2017, 16:52



Название: QTableWidgetItem
Отправлено: Dr.robot от Январь 16, 2017, 16:52
Здравствуйте у меня вопрос.
Добавляю картинку в ячейку таблицы следующим образом
Код
Python
   def on_clicked_btn(self, img):
       _item = QTableWidgetItem(QIcon(img), "")
       _item.setData(Qt.DecorationRole, QIcon(img))
       self.table.setItem(self.table.currentRow(), self.table.currentColumn(), _item)

Вставляется иконка и располагается с лева в ячейке, а мне нужно что бы картинка была на всю ячейку с оберткой DecorationRole.
Все перепробовал не знаю что делать.


Название: Re: QTableWidgetItem
Отправлено: gil9red от Январь 17, 2017, 08:33
Для этого используются делегаты. Они рисуют ячейки
Т.е. вам нужно будет создать делегат, в его методе рисования собственно и нарисовать вашу иконку.
Когда-то делал подобное (https://github.com/gil9red/combustion/blob/master/linedays_celldelegate.cpp) // В корне проекта можно будет увидеть скрины


Название: Re: QTableWidgetItem
Отправлено: Dr.robot от Январь 17, 2017, 11:23
Я хочу без делегата обойтись


Название: Re: QTableWidgetItem
Отправлено: gil9red от Январь 17, 2017, 12:08
Я хочу без делегата обойтись

Тогда пишите свою таблицу :)


Название: Re: QTableWidgetItem
Отправлено: Day от Январь 18, 2017, 11:31
Создаешь виджет (например QLabel *lab = new QLabel)
Заполняешь его, как хочешь (в том числе HTML, в который можно вставить картинку)
Код:
tablewidget->setCellWidget(row, column, lab);


Название: Re: QTableWidgetItem
Отправлено: Dr.robot от Январь 18, 2017, 12:39
А роль как задать?


Название: Re: QTableWidgetItem
Отправлено: Day от Январь 18, 2017, 13:07
А роль как задать?
Не знаю, не пробовал. Имхо, если ты помещаешь в ячейку виджет, то никакие роли уже не нужны. Впрочем, можешь сам поэкспериментировать, а нам потом расскажешь. Как я понимаю, вопрос в том, будет ли виджет в ячейке итемом, и будут ли работать для этой ячейке итемовские методы.
Но если упрешься в стенку, у тебя всегда в запасе ход - Использовать QTableView с делегатами.


Название: Re: QTableWidgetItem
Отправлено: Dr.robot от Январь 18, 2017, 13:17
Ну если по честному то я уже делал вот так, создавал свой TableWidgetItem
Код
Python
class TableWidgetItem(QtGui.QWidget):
   def __init__(self, icon, text):
       QtGui.QWidget.__init__(self)
       layout = QtGui.QHBoxLayout(self)
       layout.setContentsMargins(0, 0, 0, 0)
       layout.setSpacing(0)
       label = QtGui.QLabel()
       label.setPixmap(QtGui.QPixmap(icon))
       label.setAlignment(QtCore.Qt.AlignCenter)
       layout.addWidget(label)
И вставлял во так
Код
Python
   def on_clicked_btn(self, img):
       _item = TableWidgetItem(img, " ")
       self.table.setCellWidget(self.table.currentRow(), self.table.currentColumn(), _item)

Меня все устраивало но мне потребовалось сохранить таблицу а для этого мне нужно роль определить.
Вот я и уперся в стенку, с делегатами не работал и не знаю что это такое.


Название: Re: QTableWidgetItem
Отправлено: Dr.robot от Январь 18, 2017, 13:26
Получается QIcon можно задать роль а QPixmap нет


Название: Re: QTableWidgetItem
Отправлено: Day от Январь 18, 2017, 13:32
Цитировать
с делегатами не работал
Все равно когда-нибудь придется..:)


Название: Re: QTableWidgetItem
Отправлено: Dr.robot от Январь 18, 2017, 13:43
А есть какая нибудь литература с примерами желательно на питон?


Название: Re: QTableWidgetItem
Отправлено: Day от Январь 18, 2017, 13:49
На Qt М.Саммерфилд "Профессиональное программирование" с воином на обложке.
Про питон не знаю


Название: Re: QTableWidgetItem
Отправлено: GreatSnake от Январь 18, 2017, 14:22
Получается QIcon можно задать роль а QPixmap нет
С чего это ???
В модели всё равно хранится для DecorationRole QPixmap.

Меня все устраивало но мне потребовалось сохранить таблицу а для этого мне нужно роль определить.
Widget устанавливается во view, а роль задаётся в модели через QAbstractItemModel::setData(...).


Название: Re: QTableWidgetItem
Отправлено: Dr.robot от Январь 18, 2017, 15:06
Widget устанавливается во view, а роль задаётся в модели через QAbstractItemModel::setData(...).
Есть пример где это реализовано?, неужели никто картинки в таблицу не вставляет? =) Я имею ввиду просто вставить картинку с DecorationRole не иконку.


Название: Re: QTableWidgetItem
Отправлено: gil9red от Январь 18, 2017, 15:15
Widget устанавливается во view, а роль задаётся в модели через QAbstractItemModel::setData(...).
Есть пример где это реализовано?, неужели никто картинки в таблицу не вставляет? =) Я имею ввиду просто вставить картинку с DecorationRole не иконку.

Типа такого? ;D
(https://github.com/gil9red/combustion/raw/master/INFO/screenshot_2.png)


Название: Re: QTableWidgetItem
Отправлено: Dr.robot от Январь 18, 2017, 15:24
Типа такого? ;D

Вам смешно а я уже очень много времени потратил на это))))
Очень много информации на C++ но на питон очень мало.
(http://s019.radikal.ru/i604/1701/77/26f00c3bb563.png) (http://radikal.ru)
Вот что у меня если я просто использую Pixmap


Название: Re: QTableWidgetItem
Отправлено: gil9red от Январь 18, 2017, 16:11
Так ведь на с++ и питоне один и тот же Qt
самое большое различие -- способ коннекта, остальное -- различие языков, если вы пользуетесь IDE PyCharm, то вам проще писать код

Попробуйте найти пример делегата на с++ и просто перепишите код на питон.
Можете тут вопросы задавать, на питоне я вот уже почти 3 года что-нибудь для себя пишу смогу подсказать



Название: Re: QTableWidgetItem
Отправлено: gil9red от Январь 20, 2017, 13:36
Набросал я простенький пример  (https://github.com/gil9red/SimplePyScripts/tree/master/image%20on%20whole%20cell%20in%20QTableWidget)использования делегата для отрисовки.

Ну и весь код:
Код
Python
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()
 
 
 

Выглядит вот так (первый столбец обычный, остальные имеют делегаты):

(https://github.com/gil9red/SimplePyScripts/raw/master/image%20on%20whole%20cell%20in%20QTableWidget/screenshot.png)


Название: Re: QTableWidgetItem
Отправлено: Dr.robot от Январь 20, 2017, 16:14
Ого спасибо буду разбираться (жаль сегодня, завтра не могу), еще раз спасибо!
Если что не пойму или будут вопросы отпишусь.


Название: Re: QTableWidgetItem
Отправлено: Пантер от Январь 20, 2017, 16:21
gil9red, если есть время, закинь, пожалуйста, этот пример в раздел http://www.prog.org.ru/board_61_0.html
Думаю, полезно будет.


Название: Re: QTableWidgetItem
Отправлено: Dr.robot от Январь 22, 2017, 15:26
Все работает хорошо спасибо.
Только не работает выделение ячейки по щелчку мыши(ну это мелочи).
Хотя выдает ошибку,
img = img.scaled(w, h, Qt.KeepAspectRatio, Qt.SmoothTransformation)
AttributeError: 'NoneType' object has no attribute 'scaled'


Название: Re: QTableWidgetItem
Отправлено: gil9red от Январь 22, 2017, 19:42
Все работает хорошо спасибо.
Только не работает выделение ячейки по щелчку мыши(ну это мелочи).
Хотя выдает ошибку,
img = img.scaled(w, h, Qt.KeepAspectRatio, Qt.SmoothTransformation)
AttributeError: 'NoneType' object has no attribute 'scaled'

Если вы код не меняли все будет работать :)

img берется из Qt.DecorationRole:
Код
Python
img = index.model().data(index, Qt.DecorationRole)

Если делегат используется для ячеек без картинки в Qt.DecorationRole, то нужно тогда в делегат добавить проверку на наличие значения в img,
например если img is None, тогда вызываем родительский метод отрисовки super().paint(painter, option, index) и выходим из функции рисования



Название: Re: QTableWidgetItem
Отправлено: Dr.robot от Январь 22, 2017, 21:03
Изначально пустые ячейки
Сделал примерно как ты сказал
Код
Python
class MyDelegate(QStyledItemDelegate):
   def paint(self, painter, option, index):
       painter.save()
 
       rect = option.rect
       img = index.model().data(index, Qt.DecorationRole)
       w, h = rect.size().width(), rect.size().height()
       if img is None:
           super().paint(painter, option, index)
           #painter(exit())
       else:
           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(160)
 
           painter.setPen(Qt.NoPen)
           painter.setBrush(color)
           painter.drawRect(rect)
 
           painter.restore()
Уже меньше ругается  :)
Пишет QPainter::end: Painter ended with 380 saved states


Название: Re: QTableWidgetItem
Отправлено: gil9red от Январь 22, 2017, 23:24
Изначально пустые ячейки
Сделал примерно как ты сказал
Код
Python
class MyDelegate(QStyledItemDelegate):
   def paint(self, painter, option, index):
       painter.save()
 
       rect = option.rect
       img = index.model().data(index, Qt.DecorationRole)
       w, h = rect.size().width(), rect.size().height()
       if img is None:
           super().paint(painter, option, index)
           #painter(exit())
       else:
           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(160)
 
           painter.setPen(Qt.NoPen)
           painter.setBrush(color)
           painter.drawRect(rect)
 
           painter.restore()
Уже меньше ругается  :)
Пишет QPainter::end: Painter ended with 380 saved states

Я бы сделал так:
Код
Python
   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)
 


Обновил тот код и сообщение с примером (http://www.prog.org.ru/index.php?topic=30916.msg228998#msg228998)


Название: Re: QTableWidgetItem
Отправлено: Dr.robot от Январь 23, 2017, 01:00
Вот теперь все работает без ошибок и даже выделение работает!
Собственно и точка.
Спасибо огромнейшее!