This commit is contained in:
Roy Nieterau 2025-12-20 16:38:25 +05:00 committed by GitHub
commit 04cac8d26d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 119 additions and 8 deletions

View file

@ -133,8 +133,8 @@ class UnderlinesFolderDelegate(QtWidgets.QItemDelegate):
mode = QtGui.QIcon.Selected
if isinstance(icon, QtGui.QPixmap):
icon = QtGui.QIcon(icon)
option.decorationSize = icon.size() / icon.devicePixelRatio()
icon = QtGui.QIcon(icon)
elif isinstance(icon, QtGui.QColor):
pixmap = QtGui.QPixmap(option.decorationSize)

View file

@ -10,7 +10,9 @@ from ayon_core.tools.common_models import (
ProjectsModel,
HierarchyModel,
HierarchyExpectedSelection,
FolderItem
)
import ayon_api
from .models import RecursiveSortFilterProxyModel
from .views import TreeView
@ -26,6 +28,89 @@ FOLDER_PATH_ROLE = QtCore.Qt.UserRole + 3
FOLDER_TYPE_ROLE = QtCore.Qt.UserRole + 4
class ImageLoader(QtCore.QThread):
image_loaded = QtCore.Signal(QtGui.QStandardItem, QtGui.QImage)
def __init__(self, parent=None):
super().__init__(parent)
self._image_queue = collections.deque()
# Turn this into a 'timed' cache to allow refresh after a certain time
self._cache = {}
self._thread = QtCore.QThread()
def load_image(self,
item: QtGui.QStandardItem,
project_name: str,
entity_id: str):
# If image is in cache, do nothing
if (project_name, entity_id) in self._cache:
return
self._image_queue.append((item, project_name, entity_id))
if not self.isRunning():
self.start()
def get_image(self, project_name, entity_id):
return self._cache.get((project_name, entity_id), None)
def run(self):
# Process remaining queue
while self._image_queue:
item, project_name, entity_id = self._image_queue.popleft()
# Query thumbnail from server
# TODO: Get these from a cache instead of querying every time
response = ayon_api.raw_get(
f"projects/{project_name}/folders/{entity_id}/thumbnail")
image_bytes = response.content
# TODO: Skip if image is empty or not valid
# this does not work
if not image_bytes:
continue
# Get image format
# https://doc.qt.io/qt-6/qimagereader.html#supportedImageFormats
image_type = response.headers['content-type']
if image_type == "image/jpeg":
image_format = "JPG"
elif image_type == "image/png":
image_format = "PNG"
else:
raise TypeError(f"Unsupported image type: {image_type}")
# Load the image from the bytes
image = QtGui.QImage()
image.loadFromData(image_bytes, image_format)
# Not a valid image.
if image.isNull() or (image.width() == 1 and image.height() == 1):
self._cache[(project_name, entity_id)] = None
continue
# Scale down and maintain aspect ratio of the image
# but crop and center it to 16:9
icon_width = 112
icon_height = 63
image = image.scaled(icon_width, icon_height,
QtCore.Qt.KeepAspectRatioByExpanding,
QtCore.Qt.SmoothTransformation)
# Create a 32x32 square and center the image
square_image = QtGui.QImage(
icon_width, icon_height, QtGui.QImage.Format_ARGB32)
square_image.fill(QtCore.Qt.transparent)
painter = QtGui.QPainter(square_image)
x = (icon_width - image.width()) // 2
y = (icon_height - image.height()) // 2
painter.drawImage(x, y, image)
painter.end()
self._cache[(project_name, entity_id)] = square_image
self.image_loaded.emit(item, square_image)
class FoldersQtModel(QtGui.QStandardItemModel):
"""Folders model which cares about refresh of folders.
@ -53,6 +138,9 @@ class FoldersQtModel(QtGui.QStandardItemModel):
self._has_content = False
self._is_refreshing = False
self._image_loader = ImageLoader()
self._image_loader.image_loaded.connect(self._on_image_loaded)
@property
def is_refreshing(self):
"""Model is refreshing.
@ -236,7 +324,7 @@ class FoldersQtModel(QtGui.QStandardItemModel):
def _fill_item_data(
self,
item,
folder_item,
folder_item: FolderItem,
folder_type_item_by_name,
folder_type_icon_cache
):
@ -245,19 +333,42 @@ class FoldersQtModel(QtGui.QStandardItemModel):
Args:
item (QtGui.QStandardItem): Item to fill data.
folder_item (FolderItem): Folder item.
folder_type_item_by_name (dict): Folder type items by name.
folder_type_icon_cache (dict): Folder type icon cache.
"""
icon = self._get_folder_item_icon(
folder_item,
folder_type_item_by_name,
folder_type_icon_cache
)
item.setData(folder_item.entity_id, FOLDER_ID_ROLE)
item.setData(folder_item.name, FOLDER_NAME_ROLE)
item.setData(folder_item.path, FOLDER_PATH_ROLE)
item.setData(folder_item.folder_type, FOLDER_TYPE_ROLE)
item.setData(folder_item.label, QtCore.Qt.DisplayRole)
item.setData(icon, QtCore.Qt.DecorationRole)
project_name = self.get_project_name()
image = self._image_loader.get_image(project_name,
folder_item.entity_id)
if image:
pixmap = QtGui.QPixmap.fromImage(image)
#item.setIcon(QtGui.QIcon(pixmap))
item.setData(pixmap, QtCore.Qt.DecorationRole)
else:
icon = self._get_folder_item_icon(
folder_item,
folder_type_item_by_name,
folder_type_icon_cache
)
item.setData(icon, QtCore.Qt.DecorationRole)
self._image_loader.load_image(
item,
project_name=project_name,
entity_id=folder_item.entity_id)
def _on_image_loaded(self,
item: QtGui.QStandardItem,
image: QtGui.QImage):
pixmap = QtGui.QPixmap.fromImage(image)
#item.setIcon(QtGui.QIcon(pixmap))
item.setData(pixmap, QtCore.Qt.DecorationRole)
def _fill_items(self, folder_items_by_id, folder_type_items):
if not folder_items_by_id: