From 1c7c0fef32180917772d24932dc11a1952740f92 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 26 Apr 2022 15:49:46 +0200 Subject: [PATCH] added splitting menu option to files item widget --- openpype/tools/resources/images/menu.png | Bin 0 -> 8472 bytes .../widgets/attribute_defs/files_widget.py | 87 ++++++++++++++++-- 2 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 openpype/tools/resources/images/menu.png diff --git a/openpype/tools/resources/images/menu.png b/openpype/tools/resources/images/menu.png new file mode 100644 index 0000000000000000000000000000000000000000..14a991f092fb9cf54363eccb16d1836704846ad8 GIT binary patch literal 8472 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 AivR!s literal 0 HcmV?d00001 diff --git a/openpype/widgets/attribute_defs/files_widget.py b/openpype/widgets/attribute_defs/files_widget.py index f694f2473f..5ea7fcc5eb 100644 --- a/openpype/widgets/attribute_defs/files_widget.py +++ b/openpype/widgets/attribute_defs/files_widget.py @@ -5,12 +5,12 @@ import uuid from Qt import QtWidgets, QtCore, QtGui from openpype.lib import FileDefItem -from openpype.tools.utils import paint_image_with_color -# TODO change imports -from openpype.tools.resources import ( - get_pixmap, - get_image, +from openpype.tools.utils import ( + paint_image_with_color, + ClickableLabel, ) +# TODO change imports +from openpype.tools.resources import get_image from openpype.tools.utils import ( IconButton, PixmapLabel @@ -22,7 +22,8 @@ ITEM_ICON_ROLE = QtCore.Qt.UserRole + 3 FILENAMES_ROLE = QtCore.Qt.UserRole + 4 DIRPATH_ROLE = QtCore.Qt.UserRole + 5 IS_DIR_ROLE = QtCore.Qt.UserRole + 6 -EXT_ROLE = QtCore.Qt.UserRole + 7 +IS_SEQUENCE_ROLE = QtCore.Qt.UserRole + 7 +EXT_ROLE = QtCore.Qt.UserRole + 8 class DropEmpty(QtWidgets.QWidget): @@ -148,6 +149,7 @@ class FilesModel(QtGui.QStandardItemModel): item.setData(icon_pixmap, ITEM_ICON_ROLE) item.setData(file_item.ext, EXT_ROLE) item.setData(file_item.is_dir, IS_DIR_ROLE) + item.setData(file_item.is_sequence, IS_SEQUENCE_ROLE) return item_id, item @@ -216,7 +218,9 @@ class FilesProxyModel(QtCore.QSortFilterProxyModel): class ItemWidget(QtWidgets.QWidget): - def __init__(self, item_id, label, pixmap_icon, parent=None): + split_requested = QtCore.Signal(str) + + def __init__(self, item_id, label, pixmap_icon, is_sequence, parent=None): self._item_id = item_id super(ItemWidget, self).__init__(parent) @@ -226,13 +230,67 @@ class ItemWidget(QtWidgets.QWidget): icon_widget = PixmapLabel(pixmap_icon, self) label_widget = QtWidgets.QLabel(label, self) + label_size_hint = label_widget.sizeHint() + height = label_size_hint.height() + actions_menu_pix = paint_image_with_color( + get_image(filename="menu.png"), QtCore.Qt.white + ) + + split_btn = ClickableLabel(self) + split_btn.setFixedSize(height, height) + split_btn.setPixmap(actions_menu_pix) + split_btn.setVisible(is_sequence) + layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(5, 5, 0, 5) + layout.setContentsMargins(5, 5, 5, 5) layout.addWidget(icon_widget, 0) layout.addWidget(label_widget, 1) + layout.addWidget(split_btn, 0) + + split_btn.clicked.connect(self._on_actions_clicked) self._icon_widget = icon_widget self._label_widget = label_widget + self._split_btn = split_btn + self._actions_menu_pix = actions_menu_pix + self._last_scaled_pix_height = None + + def _update_btn_size(self): + label_size_hint = self._label_widget.sizeHint() + height = label_size_hint.height() + if height == self._last_scaled_pix_height: + return + self._last_scaled_pix_height = height + self._split_btn.setFixedSize(height, height) + pix = self._actions_menu_pix.scaled( + height, height, + QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation + ) + self._split_btn.setPixmap(pix) + + def showEvent(self, event): + super(ItemWidget, self).showEvent(event) + self._update_btn_size() + + def resizeEvent(self, event): + super(ItemWidget, self).resizeEvent(event) + self._update_btn_size() + + def _on_actions_clicked(self): + menu = QtWidgets.QMenu(self._split_btn) + + action = QtWidgets.QAction("Split sequence", menu) + action.triggered.connect(self._on_split_sequence) + + menu.addAction(action) + + pos = self._split_btn.rect().bottomLeft() + point = self._split_btn.mapToGlobal(pos) + menu.popup(point) + + def _on_split_sequence(self): + self.split_requested.emit(self._item_id) class InViewButton(IconButton): @@ -404,8 +462,10 @@ class FilesWidget(QtWidgets.QFrame): continue label = index.data(ITEM_LABEL_ROLE) pixmap_icon = index.data(ITEM_ICON_ROLE) + is_sequence = index.data(IS_SEQUENCE_ROLE) - widget = ItemWidget(item_id, label, pixmap_icon) + widget = ItemWidget(item_id, label, pixmap_icon, is_sequence) + widget.split_requested.connect(self._on_split_request) self._files_view.setIndexWidget(index, widget) self._files_proxy_model.setData( index, widget.sizeHint(), QtCore.Qt.SizeHintRole @@ -437,6 +497,15 @@ class FilesWidget(QtWidgets.QFrame): if not self._in_set_value: self.value_changed.emit() + def _on_split_request(self, item_id): + file_item = self._files_model.get_file_item_by_id(item_id) + if not file_item: + return + + new_items = file_item.split_sequence() + self._remove_item_by_ids([item_id]) + self._add_filepaths(new_items) + def _on_remove_requested(self): items_to_delete = self._files_view.get_selected_item_ids() if items_to_delete: