From c9d255ce59719ed2282f6528c1bd32c8bbbc5df2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 31 Oct 2022 15:18:07 +0100 Subject: [PATCH] separated thumbnail painter widget and thumbnail widget to be able handle buttons overlay --- .../tools/publisher/widgets/create_widget.py | 4 + .../publisher/widgets/thumbnail_widget.py | 288 +++++++++++------- openpype/tools/publisher/widgets/widgets.py | 18 ++ 3 files changed, 198 insertions(+), 112 deletions(-) diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index 4540e70eb8..7bdac46273 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -272,6 +272,7 @@ class CreateWidget(QtWidgets.QWidget): ) tasks_widget.task_changed.connect(self._on_task_change) thumbnail_widget.thumbnail_created.connect(self._on_thumbnail_create) + thumbnail_widget.thumbnail_cleared.connect(self._on_thumbnail_clear) controller.event_system.add_callback( "plugins.refresh.finished", self._on_plugins_refresh @@ -504,6 +505,9 @@ class CreateWidget(QtWidgets.QWidget): self._last_thumbnail_path = thumbnail_path self._thumbnail_widget.set_current_thumbnails([thumbnail_path]) + def _on_thumbnail_clear(self): + self._last_thumbnail_path = None + def _on_current_session_context_request(self): self._assets_widget.set_current_session_asset() task_name = self.current_task_name diff --git a/openpype/tools/publisher/widgets/thumbnail_widget.py b/openpype/tools/publisher/widgets/thumbnail_widget.py index e119d640b4..b45d61623e 100644 --- a/openpype/tools/publisher/widgets/thumbnail_widget.py +++ b/openpype/tools/publisher/widgets/thumbnail_widget.py @@ -1,6 +1,5 @@ import os import uuid -import math from Qt import QtWidgets, QtCore, QtGui @@ -24,22 +23,15 @@ from openpype.tools.publisher.control import CardMessageTypes from .icons import get_image -class ThumbnailWidget(QtWidgets.QWidget): - """Instance thumbnail widget.""" - - thumbnail_created = QtCore.Signal(str) - +class ThumbnailPainterWidget(QtWidgets.QWidget): width_ratio = 3.0 height_ratio = 2.0 border_width = 1 - offset_sep = 4 max_thumbnails = 3 + offset_sep = 4 - def __init__(self, controller, parent): - # Missing implementation for thumbnail - # - widget kept to make a visial offset of global attr widget offset - super(ThumbnailWidget, self).__init__(parent) - self.setAcceptDrops(True) + def __init__(self, parent): + super(ThumbnailPainterWidget, self).__init__(parent) border_color = get_objected_colors("bg-buttons").get_qcolor() thumbnail_bg_color = get_objected_colors("border").get_qcolor() @@ -48,103 +40,22 @@ class ThumbnailWidget(QtWidgets.QWidget): default_image = get_image("thumbnail") default_pix = paint_image_with_color(default_image, border_color) - self._controller = controller - self._output_dir = controller.get_thumbnail_temp_dir_path() - self.border_color = border_color self.thumbnail_bg_color = thumbnail_bg_color self.overlay_color = overlay_color self._default_pix = default_pix + self._cached_pix = None self._current_pixes = None + self._has_pixes = False + + @property + def has_pixes(self): + return self._has_pixes + + def clear_cache(self): self._cached_pix = None - - self._review_extensions = set(IMAGE_EXTENSIONS) | set(VIDEO_EXTENSIONS) - - self._height = None - self._width = None - self._adapted_to_size = True - self._last_width = None - self._last_height = None - - def _get_filepath_from_event(self, event): - mime_data = event.mimeData() - if not mime_data.hasUrls(): - return None - - filepaths = [] - for url in mime_data.urls(): - filepath = url.toLocalFile() - if os.path.exists(filepath): - filepaths.append(filepath) - - if len(filepaths) == 1: - filepath = filepaths[0] - ext = os.path.splitext(filepath)[-1] - if ext in self._review_extensions: - return filepath - return None - - def dragEnterEvent(self, event): - filepath = self._get_filepath_from_event(event) - if filepath: - event.setDropAction(QtCore.Qt.CopyAction) - event.accept() - - def dragLeaveEvent(self, event): - event.accept() - - def dropEvent(self, event): - filepath = self._get_filepath_from_event(event) - if not filepath: - return - - output = export_thumbnail(filepath, self._output_dir) - if output: - self.thumbnail_created.emit(output) - else: - self._controller.emit_card_message( - "Couldn't convert the source for thumbnail", - CardMessageTypes.error - ) - - def set_adapted_to_hint(self, enabled): - self._adapted_to_size = enabled - if self._width is not None: - self.setMinimumHeight(0) - self._width = None - - if self._height is not None: - self.setMinimumWidth(0) - self._height = None - - def set_width(self, width): - if self._width == width: - return - - self._adapted_to_size = False - self._width = width - self._cached_pix = None - self.setMinimumHeight(int( - (width / self.width_ratio) * self.height_ratio - )) - if self._height is not None: - self.setMinimumWidth(0) - self._height = None - - def set_height(self, height): - if self._height == height: - return - - self._height = height - self._adapted_to_size = False - self._cached_pix = None - self.setMinimumWidth(int( - (height / self.height_ratio) * self.width_ratio - )) - if self._width is not None: - self.setMinimumHeight(0) - self._width = None + self.repaint() def set_current_thumbnails(self, thumbnail_paths=None): pixes = [] @@ -153,8 +64,17 @@ class ThumbnailWidget(QtWidgets.QWidget): pixes.append(QtGui.QPixmap(thumbnail_path)) self._current_pixes = pixes or None - self._cached_pix = None - self.repaint() + self._has_pixes = self._current_pixes is not None + self.clear_cache() + + def paintEvent(self, event): + if self._cached_pix is None: + self._cache_pix() + + painter = QtGui.QPainter() + painter.begin(self) + painter.drawPixmap(0, 0, self._cached_pix) + painter.end() def _cache_pix(self): rect = self.rect() @@ -276,14 +196,146 @@ class ThumbnailWidget(QtWidgets.QWidget): part_height = height / self.offset_sep return part_width, part_height - def paintEvent(self, event): - if self._cached_pix is None: - self._cache_pix() - painter = QtGui.QPainter() - painter.begin(self) - painter.drawPixmap(0, 0, self._cached_pix) - painter.end() +class ThumbnailWidget(QtWidgets.QWidget): + """Instance thumbnail widget.""" + + thumbnail_created = QtCore.Signal(str) + thumbnail_cleared = QtCore.Signal() + + def __init__(self, controller, parent): + # Missing implementation for thumbnail + # - widget kept to make a visial offset of global attr widget offset + super(ThumbnailWidget, self).__init__(parent) + self.setAcceptDrops(True) + + thumbnail_painter = ThumbnailPainterWidget(self) + + buttons_widget = QtWidgets.QWidget(self) + buttons_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + + clear_button = QtWidgets.QPushButton("x", buttons_widget) + + buttons_layout = QtWidgets.QHBoxLayout(buttons_widget) + buttons_layout.setContentsMargins(3, 3, 3, 3) + buttons_layout.addStretch(1) + buttons_layout.addWidget(clear_button, 0) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(thumbnail_painter) + + clear_button.clicked.connect(self._on_clear_clicked) + + self._controller = controller + self._output_dir = controller.get_thumbnail_temp_dir_path() + + self._review_extensions = set(IMAGE_EXTENSIONS) | set(VIDEO_EXTENSIONS) + + self._height = None + self._width = None + self._adapted_to_size = True + self._last_width = None + self._last_height = None + + self._buttons_widget = buttons_widget + self._thumbnail_painter = thumbnail_painter + + @property + def width_ratio(self): + return self._thumbnail_painter.width_ratio + + @property + def height_ratio(self): + return self._thumbnail_painter.height_ratio + + def _get_filepath_from_event(self, event): + mime_data = event.mimeData() + if not mime_data.hasUrls(): + return None + + filepaths = [] + for url in mime_data.urls(): + filepath = url.toLocalFile() + if os.path.exists(filepath): + filepaths.append(filepath) + + if len(filepaths) == 1: + filepath = filepaths[0] + ext = os.path.splitext(filepath)[-1] + if ext in self._review_extensions: + return filepath + return None + + def dragEnterEvent(self, event): + filepath = self._get_filepath_from_event(event) + if filepath: + event.setDropAction(QtCore.Qt.CopyAction) + event.accept() + + def dragLeaveEvent(self, event): + event.accept() + + def dropEvent(self, event): + filepath = self._get_filepath_from_event(event) + if not filepath: + return + + output = export_thumbnail(filepath, self._output_dir) + if output: + self.thumbnail_created.emit(output) + else: + self._controller.emit_card_message( + "Couldn't convert the source for thumbnail", + CardMessageTypes.error + ) + + def set_adapted_to_hint(self, enabled): + self._adapted_to_size = enabled + if self._width is not None: + self.setMinimumHeight(0) + self._width = None + + if self._height is not None: + self.setMinimumWidth(0) + self._height = None + + def set_width(self, width): + if self._width == width: + return + + self._adapted_to_size = False + self._width = width + self.setMinimumHeight(int( + (width / self.width_ratio) * self.height_ratio + )) + if self._height is not None: + self.setMinimumWidth(0) + self._height = None + self._thumbnail_painter.clear_cache() + + def set_height(self, height): + if self._height == height: + return + + self._height = height + self._adapted_to_size = False + self.setMinimumWidth(int( + (height / self.height_ratio) * self.width_ratio + )) + if self._width is not None: + self.setMinimumHeight(0) + self._width = None + + self._thumbnail_painter.clear_cache() + + def set_current_thumbnails(self, thumbnail_paths=None): + self._thumbnail_painter.set_current_thumbnails(thumbnail_paths) + self._update_buttons_position() + + def _on_clear_clicked(self): + self.set_current_thumbnails() + self.thumbnail_cleared.emit() def _adapt_to_size(self): if not self._adapted_to_size: @@ -296,15 +348,27 @@ class ThumbnailWidget(QtWidgets.QWidget): self._last_width = width self._last_height = height - self._cached_pix = None + self._thumbnail_painter.clear_cache() + + def _update_buttons_position(self): + self._buttons_widget.setVisible(self._thumbnail_painter.has_pixes) + size = self.size() + my_height = size.height() + height = self._buttons_widget.sizeHint().height() + self._buttons_widget.setGeometry( + 0, my_height - height, + size.width(), height + ) def resizeEvent(self, event): super(ThumbnailWidget, self).resizeEvent(event) self._adapt_to_size() + self._update_buttons_position() def showEvent(self, event): super(ThumbnailWidget, self).showEvent(event) self._adapt_to_size() + self._update_buttons_position() def _run_silent_subprocess(args): diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index c7b6965991..744c51ce07 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -1569,6 +1569,7 @@ class SubsetAttributesWidget(QtWidgets.QWidget): ) convert_btn.clicked.connect(self._on_convert_click) thumbnail_widget.thumbnail_created.connect(self._on_thumbnail_create) + thumbnail_widget.thumbnail_cleared.connect(self._on_thumbnail_clear) controller.event_system.add_callback( "instance.thumbnail.changed", self._on_thumbnail_changed @@ -1662,6 +1663,23 @@ class SubsetAttributesWidget(QtWidgets.QWidget): self._controller.set_thumbnail_paths_for_instances(mapping) + def _on_thumbnail_clear(self): + instance_ids = [ + instance.id + for instance in self._current_instances + ] + if self._context_selected: + instance_ids.append(None) + + if not instance_ids: + return + + mapping = { + instance_id: None + for instance_id in instance_ids + } + self._controller.set_thumbnail_paths_for_instances(mapping) + def _on_thumbnail_changed(self, event): self._update_thumbnails()