Source code for arthropod_describer.image_list_delegate

import typing
import importlib.resources
import typing

import PySide2
from PySide2.QtCore import Qt, QRect, QSize, QPoint, Signal, QRectF, QMarginsF, QModelIndex
from PySide2.QtGui import QImage, QIcon, QPixmap, QBrush, QColor, QFont
from PySide2.QtWidgets import QStyledItemDelegate, QApplication, QStyleOptionButton, QStyle, QListView, \
    QStyleOptionViewItem, QAbstractItemView, QStyleOptionFrame

from arthropod_describer.thumbnail_storage import ThumbnailDelegate, ThumbnailStorage_


[docs]class ImageListDelegate(ThumbnailDelegate): tags_rect: QRect = QRect() tag_rects: typing.Dict[int, QRect] = {} def __init__(self, thumbnail_storage: ThumbnailStorage_, parent: typing.Optional[PySide2.QtCore.QObject] = None): super().__init__(thumbnail_storage, parent) self.mouse_pos: QPoint = QPoint() self.icon_size = QSize(20, 20) with importlib.resources.path('resources', 'refresh.png') as path: cw = QImage(str(path)).scaledToWidth(self.icon_size.width(), Qt.TransformationMode.SmoothTransformation) self.cw_icon = QIcon(QPixmap.fromImage(cw)) self.ccw_icon = QIcon(QPixmap.fromImage(cw.mirrored(False, True))) with importlib.resources.path('resources', 'resize.png') as path: rs = QImage(str(path)).scaledToWidth(self.icon_size.width(), Qt.SmoothTransformation) self.resize_icon = QIcon(QPixmap.fromImage(rs)) with importlib.resources.path('resources', 'floppy-disk.png') as path: self.save_img = QImage(str(path)).scaledToWidth(self.icon_size.width(), Qt.SmoothTransformation) self.save_icon = QIcon(QPixmap.fromImage(self.save_img)) def _paint(self, painter: PySide2.QtGui.QPainter, option: QStyleOptionViewItem, index: PySide2.QtCore.QModelIndex): painter.save() font_metrics = painter.fontMetrics() img_name = index.data(Qt.DisplayRole) quality_color = QColor(220, 220, 220) rect = option.rect bottom_bar = QRectF(0, rect.bottom() - 32, rect.width(), 32) tags: typing.Set[str] = index.data(Qt.UserRole + 9) if len(tags) == 0: tags = {'no tags'} tag_style = QFont.StyleItalic else: tag_style = QFont.StyleNormal tags_str = ', '.join(list(sorted(tags))) tags_str_bbox = font_metrics.boundingRect(tags_str) tags_str_width = tags_str_bbox.width() text_rect = QRectF(0, rect.bottom() - 32, rect.width() * 0.45, 32) img_name_elided = font_metrics.elidedText(img_name, Qt.ElideRight, text_rect.width()) text_bbox = font_metrics.boundingRect(img_name_elided) tags_rect_width = min(rect.width() * 0.45, tags_str_width * 1.2) tags_rect = QRectF(rect.width() - tags_rect_width, rect.bottom() - 32, tags_rect_width, 32) image_name_width = text_bbox.width() painter.setRenderHint(painter.SmoothPixmapTransform, True) quality_color = QColor(00, 00, 00, 200) if quality_color is None else quality_color painter.fillRect(bottom_bar, quality_color) painter.setPen(QColor(0, 0, 0, 255)) painter.drawText(text_rect, Qt.AlignLeft | Qt.AlignVCenter, img_name_elided) painter.drawLine(rect.bottomLeft(), rect.bottomRight()) if option.state & QStyle.State_Selected: color = option.palette.highlight().color() color.setAlpha(100) painter.fillRect(rect, color) tags_str_elided = font_metrics.elidedText(tags_str, Qt.ElideRight, tags_rect.width()) painter.setPen(QColor(80, 80, 80, 255)) left_margin_shift = -3 right_margin_shift = -2 tags_rect_frame = tags_rect.marginsRemoved(QMarginsF(0, 3, -right_margin_shift, 3)) tags_rect_frame = tags_rect_frame.marginsAdded(QMarginsF(-left_margin_shift, 0, 0, 0)) self.tag_rects[index.row()] = tags_rect_frame.toRect() painter.drawRect(tags_rect_frame) font = painter.font() font.setStyle(tag_style) painter.setFont(font) painter.drawText(tags_rect_frame, Qt.AlignCenter, tags_str_elided) opt = QStyleOptionFrame() opt.rect = QRect(text_rect.toRect()) opt.rect.x = opt.rect.x() + text_rect.width() * 0.5 opt.features = QStyleOptionFrame.None_ opt.state = QStyle.State_Enabled | QStyle.State_HasFocus style = QApplication.style() style.drawControl(QStyle.CE_ShapedFrame, opt, painter) painter.restore()
[docs] def paint(self, painter: PySide2.QtGui.QPainter, option: QStyleOptionViewItem, index: PySide2.QtCore.QModelIndex): super().paint(painter, option, index) approval_count, out_of = index.data(Qt.UserRole + 4) self._paint(painter, option, index) painter.save() painter.setBrush(QBrush(QColor(0, 150, 0))) for i in range(approval_count): painter.drawEllipse(QPoint(i * 12 + 10, option.rect.y() + 10), 5, 5) painter.setBrush(QBrush(QColor(200, 150, 0))) for i in range(approval_count, out_of): painter.drawEllipse(QPoint(i * 12 + 10, option.rect.y() + 10), 4, 4) if not index.data(Qt.UserRole + 5): painter.fillRect(option.rect, QBrush(QColor(200, 200, 200, 150))) if index.data(Qt.UserRole + 6): painter.restore() painter.save() rect = QRect(option.rect.bottomLeft() - QPoint(-5, self.icon_size.height() + 32 + 2), self.icon_size) #painter.drawImage(rect, self.save_img) style = QApplication.style() opt = QStyleOptionButton() #opt.rect = QRect(option.rect.topRight(), QSize(self.icon_size.width() + 12, self.icon_size.height() + 12)) opt.rect = QRect(QPoint(5, option.rect.top() + 160 - 28 - 32 - 2), QSize(28, 28)) opt.state = QStyle.State_Active | QStyle.State_Enabled #if opt.rect.contains(self.mouse_pos): # opt.state = opt.state | QStyle.State_MouseOver opt.icon = self.save_icon opt.iconSize = self.icon_size style.drawControl(QStyle.CE_PushButton, opt, painter) painter.restore()
#if False and option.state & QStyle.State_MouseOver > 0: # style = QApplication.style() # opt = QStyleOptionButton() # opt.rect = QRect(option.rect.topRight(), QSize(self.icon_size.width() + 12, self.icon_size.height() + 12)) # opt.rect = opt.rect.translated(-3 * opt.rect.width(), 4) # opt.state = QStyle.State_Active | QStyle.State_Enabled # if opt.rect.contains(self.mouse_pos): # opt.state = opt.state | QStyle.State_MouseOver # opt.icon = self.cw_icon # opt.iconSize = self.icon_size # style.drawControl(QStyle.CE_PushButton, opt, painter) # opt.rect = opt.rect.translated(self.icon_size.width() + 11, 0) # opt.state = QStyle.State_Active | QStyle.State_Enabled # if opt.rect.contains(self.mouse_pos): # opt.state = opt.state | QStyle.State_MouseOver # opt.icon = self.ccw_icon # style.drawControl(QStyle.CE_PushButton, opt, painter) # opt.rect = opt.rect.translated(self.icon_size.width() + 11, 0) # opt.state = QStyle.State_Active | QStyle.State_Enabled # if opt.rect.contains(self.mouse_pos): # opt.state = opt.state | QStyle.State_MouseOver # opt.icon = self.resize_icon # style.drawControl(QStyle.CE_PushButton, opt, painter)
[docs]class ImageListView(QListView): view_left = Signal() show_tag_ui = Signal(QModelIndex) hide_tag_ui = Signal(QModelIndex) def __init__(self, parent: typing.Optional[PySide2.QtWidgets.QWidget] = ...): super().__init__(parent) self.delegate: typing.Optional[ImageListDelegate] = None
[docs] def initialize(self, delegate: ImageListDelegate): self.setMouseTracking(True) self.delegate = delegate self.setItemDelegate(self.delegate)
[docs] def leaveEvent(self, event:PySide2.QtCore.QEvent): self.view_left.emit()
[docs] def mousePressEvent(self, event: PySide2.QtGui.QMouseEvent): if event.modifiers() & Qt.ControlModifier > 0: self.setSelectionMode(QAbstractItemView.ExtendedSelection) else: self.setSelectionMode(QAbstractItemView.SingleSelection) super().mousePressEvent(event)
[docs] def mouseMoveEvent(self, e: PySide2.QtGui.QMouseEvent): super(ImageListView, self).mouseMoveEvent(e) index = self.indexAt(e.pos()) if not index.isValid(): return if self.delegate.tag_rects[index.row()].contains(e.pos()): self.show_tag_ui.emit(index) else: self.hide_tag_ui.emit(index)