From 10fd26e086a2b4f6ede4e1a7fbd901d7fdb34cf3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 29 Apr 2022 14:11:47 +0200 Subject: [PATCH 01/26] moved enable review from creator into publish collector --- openpype/hosts/traypublisher/api/plugin.py | 29 ++++++----------- .../plugins/publish/collect_review_family.py | 31 +++++++++++++++++++ .../publish/collect_simple_instances.py | 9 +++--- .../project_settings/traypublisher.json | 1 - .../schema_project_traypublisher.json | 6 ---- 5 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 openpype/hosts/traypublisher/plugins/publish/collect_review_family.py diff --git a/openpype/hosts/traypublisher/api/plugin.py b/openpype/hosts/traypublisher/api/plugin.py index 813641a7d2..603f34ee29 100644 --- a/openpype/hosts/traypublisher/api/plugin.py +++ b/openpype/hosts/traypublisher/api/plugin.py @@ -2,10 +2,7 @@ from openpype.pipeline import ( Creator, CreatedInstance ) -from openpype.lib import ( - FileDef, - BoolDef, -) +from openpype.lib import FileDef from .pipeline import ( list_instances, @@ -43,7 +40,6 @@ class TrayPublishCreator(Creator): class SettingsCreator(TrayPublishCreator): create_allow_context_change = True - enable_review = False extensions = [] def collect_instances(self): @@ -67,19 +63,15 @@ class SettingsCreator(TrayPublishCreator): self._add_instance_to_context(new_instance) def get_instance_attr_defs(self): - output = [] - - file_def = FileDef( - "filepath", - folders=False, - extensions=self.extensions, - allow_sequences=self.allow_sequences, - label="Filepath", - ) - output.append(file_def) - if self.enable_review: - output.append(BoolDef("review", label="Review")) - return output + return [ + FileDef( + "filepath", + folders=False, + extensions=self.extensions, + allow_sequences=self.allow_sequences, + label="Filepath", + ) + ] @classmethod def from_settings(cls, item_data): @@ -97,7 +89,6 @@ class SettingsCreator(TrayPublishCreator): "icon": item_data["icon"], "description": item_data["description"], "detailed_description": item_data["detailed_description"], - "enable_review": item_data["enable_review"], "extensions": item_data["extensions"], "allow_sequences": item_data["allow_sequences"], "default_variants": item_data["default_variants"] diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_review_family.py b/openpype/hosts/traypublisher/plugins/publish/collect_review_family.py new file mode 100644 index 0000000000..965e251527 --- /dev/null +++ b/openpype/hosts/traypublisher/plugins/publish/collect_review_family.py @@ -0,0 +1,31 @@ +import pyblish.api +from openpype.lib import BoolDef +from openpype.pipeline import OpenPypePyblishPluginMixin + + +class CollectReviewFamily( + pyblish.api.InstancePlugin, OpenPypePyblishPluginMixin +): + """Add review family.""" + + label = "Collect Review Family" + order = pyblish.api.CollectorOrder - 0.49 + + hosts = ["traypublisher"] + families = [ + "image", + "render", + "plate", + "review" + ] + + def process(self, instance): + values = self.get_attr_values_from_data(instance.data) + if values.get("add_review_family"): + instance.data["families"].append("review") + + @classmethod + def get_attribute_defs(cls): + return [ + BoolDef("add_review_family", label="Review", default=True) + ] diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_simple_instances.py b/openpype/hosts/traypublisher/plugins/publish/collect_simple_instances.py index 5fc66084d6..9facd90a48 100644 --- a/openpype/hosts/traypublisher/plugins/publish/collect_simple_instances.py +++ b/openpype/hosts/traypublisher/plugins/publish/collect_simple_instances.py @@ -22,10 +22,6 @@ class CollectSettingsSimpleInstances(pyblish.api.InstancePlugin): repres = instance.data["representations"] creator_attributes = instance.data["creator_attributes"] - - if creator_attributes.get("review"): - instance.data["families"].append("review") - filepath_item = creator_attributes["filepath"] self.log.info(filepath_item) filepaths = [ @@ -34,6 +30,7 @@ class CollectSettingsSimpleInstances(pyblish.api.InstancePlugin): ] instance.data["sourceFilepaths"] = filepaths + instance.data["stagingDir"] = filepath_item["directory"] filenames = filepath_item["filenames"] ext = os.path.splitext(filenames[0])[-1] @@ -46,3 +43,7 @@ class CollectSettingsSimpleInstances(pyblish.api.InstancePlugin): "stagingDir": filepath_item["directory"], "files": filenames }) + + self.log.debug("Created Simple Settings instance {}".format( + instance.data + )) diff --git a/openpype/settings/defaults/project_settings/traypublisher.json b/openpype/settings/defaults/project_settings/traypublisher.json index 1b0ad67abb..0b54cfd39e 100644 --- a/openpype/settings/defaults/project_settings/traypublisher.json +++ b/openpype/settings/defaults/project_settings/traypublisher.json @@ -8,7 +8,6 @@ "default_variants": [ "Main" ], - "enable_review": false, "description": "Publish workfile backup", "detailed_description": "", "allow_sequences": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_traypublisher.json b/openpype/settings/entities/schemas/projects_schema/schema_project_traypublisher.json index 59c675d411..55c1b7b7d7 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_traypublisher.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_traypublisher.json @@ -45,12 +45,6 @@ "type": "text" } }, - { - "type": "boolean", - "key": "enable_review", - "label": "Enable review", - "tooltip": "Allow to create review from source file/s.\nFiles must be supported to be able create review." - }, { "type": "separator" }, From 1ea991f4f3d277e611587200bd93c007ae0fd660 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 29 Apr 2022 14:30:33 +0200 Subject: [PATCH 02/26] added right click callback for menu --- .../widgets/attribute_defs/files_widget.py | 69 ++++++++++++++----- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/openpype/widgets/attribute_defs/files_widget.py b/openpype/widgets/attribute_defs/files_widget.py index a3ee370bd3..5fe0302507 100644 --- a/openpype/widgets/attribute_defs/files_widget.py +++ b/openpype/widgets/attribute_defs/files_widget.py @@ -251,7 +251,7 @@ class FilesProxyModel(QtCore.QSortFilterProxyModel): class ItemWidget(QtWidgets.QWidget): - split_requested = QtCore.Signal(str) + context_menu_requested = QtCore.Signal(QtCore.QPoint) def __init__( self, item_id, label, pixmap_icon, is_sequence, multivalue, parent=None @@ -316,19 +316,9 @@ class ItemWidget(QtWidgets.QWidget): 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) + self.context_menu_requested.emit(point) class InViewButton(IconButton): @@ -339,6 +329,7 @@ class FilesView(QtWidgets.QListView): """View showing instances and their groups.""" remove_requested = QtCore.Signal() + context_menu_requested = QtCore.Signal(QtCore.QPoint) def __init__(self, *args, **kwargs): super(FilesView, self).__init__(*args, **kwargs) @@ -347,6 +338,7 @@ class FilesView(QtWidgets.QListView): self.setSelectionMode( QtWidgets.QAbstractItemView.ExtendedSelection ) + self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) remove_btn = InViewButton(self) pix_enabled = paint_image_with_color( @@ -361,6 +353,7 @@ class FilesView(QtWidgets.QListView): remove_btn.setEnabled(False) remove_btn.clicked.connect(self._on_remove_clicked) + self.customContextMenuRequested.connect(self._on_context_menu_request) self._remove_btn = remove_btn @@ -397,6 +390,12 @@ class FilesView(QtWidgets.QListView): selected_item_ids.add(instance_id) return selected_item_ids + def has_selected_sequence(self): + for index in self.selectionModel().selectedIndexes(): + if index.data(IS_SEQUENCE_ROLE): + return True + return False + def event(self, event): if event.type() == QtCore.QEvent.KeyPress: if ( @@ -408,6 +407,12 @@ class FilesView(QtWidgets.QListView): return super(FilesView, self).event(event) + def _on_context_menu_request(self, pos): + index = self.indexAt(pos) + if index.isValid(): + point = self.mapToGlobal(pos) + self.context_menu_requested.emit(point) + def _on_selection_change(self): self._remove_btn.setEnabled(self.has_selected_item_ids()) @@ -456,6 +461,9 @@ class FilesWidget(QtWidgets.QFrame): files_proxy_model.rowsInserted.connect(self._on_rows_inserted) files_proxy_model.rowsRemoved.connect(self._on_rows_removed) files_view.remove_requested.connect(self._on_remove_requested) + files_view.context_menu_requested.connect( + self._on_context_menu_requested + ) self._in_set_value = False self._single_item = single_item self._multivalue = False @@ -527,7 +535,9 @@ class FilesWidget(QtWidgets.QFrame): is_sequence, self._multivalue ) - widget.split_requested.connect(self._on_split_request) + widget.context_menu_requested.connect( + self._on_context_menu_requested + ) self._files_view.setIndexWidget(index, widget) self._files_proxy_model.setData( index, widget.sizeHint(), QtCore.Qt.SizeHintRole @@ -559,17 +569,22 @@ class FilesWidget(QtWidgets.QFrame): if not self._in_set_value: self.value_changed.emit() - def _on_split_request(self, item_id): + def _on_split_request(self): if self._multivalue: return - file_item = self._files_model.get_file_item_by_id(item_id) - if not file_item: + item_ids = self._files_view.get_selected_item_ids() + if not item_ids: return - new_items = file_item.split_sequence() - self._remove_item_by_ids([item_id]) - self._add_filepaths(new_items) + for item_id in item_ids: + file_item = self._files_model.get_file_item_by_id(item_id) + if not file_item: + return + + new_items = file_item.split_sequence() + self._add_filepaths(new_items) + self._remove_item_by_ids(item_ids) def _on_remove_requested(self): if self._multivalue: @@ -579,6 +594,22 @@ class FilesWidget(QtWidgets.QFrame): if items_to_delete: self._remove_item_by_ids(items_to_delete) + def _on_context_menu_requested(self, pos): + if self._multivalue: + return + + menu = QtWidgets.QMenu(self._files_view) + + remove_action = QtWidgets.QAction("Remove", menu) + remove_action.triggered.connect(self._on_remove_requested) + menu.addAction(remove_action) + + if self._files_view.has_selected_sequence(): + remove_action = QtWidgets.QAction("Split sequence", menu) + remove_action.triggered.connect(self._on_split_request) + + menu.popup(pos) + def sizeHint(self): # Get size hints of widget and visible widgets result = super(FilesWidget, self).sizeHint() From b74b309de19a2f7de6cb06fc6b6afa1ed4d5a624 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 29 Apr 2022 14:34:12 +0200 Subject: [PATCH 03/26] fixed split sequence action --- openpype/widgets/attribute_defs/files_widget.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/openpype/widgets/attribute_defs/files_widget.py b/openpype/widgets/attribute_defs/files_widget.py index 5fe0302507..924dbf7fe5 100644 --- a/openpype/widgets/attribute_defs/files_widget.py +++ b/openpype/widgets/attribute_defs/files_widget.py @@ -410,7 +410,7 @@ class FilesView(QtWidgets.QListView): def _on_context_menu_request(self, pos): index = self.indexAt(pos) if index.isValid(): - point = self.mapToGlobal(pos) + point = self.viewport().mapToGlobal(pos) self.context_menu_requested.emit(point) def _on_selection_change(self): @@ -600,14 +600,15 @@ class FilesWidget(QtWidgets.QFrame): menu = QtWidgets.QMenu(self._files_view) + if self._files_view.has_selected_sequence(): + split_action = QtWidgets.QAction("Split sequence", menu) + split_action.triggered.connect(self._on_split_request) + menu.addAction(split_action) + remove_action = QtWidgets.QAction("Remove", menu) remove_action.triggered.connect(self._on_remove_requested) menu.addAction(remove_action) - if self._files_view.has_selected_sequence(): - remove_action = QtWidgets.QAction("Split sequence", menu) - remove_action.triggered.connect(self._on_split_request) - menu.popup(pos) def sizeHint(self): From 0647a5dcf294e7a7c88f691c7b5114c49927b5fc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 29 Apr 2022 14:44:25 +0200 Subject: [PATCH 04/26] moved create button to right side --- openpype/tools/publisher/widgets/create_dialog.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index 243540f243..dd1e00b79b 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -282,9 +282,6 @@ class CreateDialog(QtWidgets.QDialog): subset_name_input = QtWidgets.QLineEdit(self) subset_name_input.setEnabled(False) - create_btn = QtWidgets.QPushButton("Create", self) - create_btn.setEnabled(False) - form_layout = QtWidgets.QFormLayout() form_layout.addRow("Variant:", variant_widget) form_layout.addRow("Subset:", subset_name_input) @@ -295,7 +292,6 @@ class CreateDialog(QtWidgets.QDialog): mid_layout.addWidget(QtWidgets.QLabel("Choose family:", self)) mid_layout.addWidget(creators_view, 1) mid_layout.addLayout(form_layout, 0) - mid_layout.addWidget(create_btn, 0) # ------------ # --- Creator short info and attr defs --- @@ -313,11 +309,22 @@ class CreateDialog(QtWidgets.QDialog): # Precreate attributes widget pre_create_widget = PreCreateWidget(creator_attrs_widget) + # Create button + create_btn_wrapper = QtWidgets.QWidget(creator_attrs_widget) + create_btn = QtWidgets.QPushButton("Create", create_btn_wrapper) + create_btn.setEnabled(False) + + create_btn_wrap_layout = QtWidgets.QHBoxLayout(create_btn_wrapper) + create_btn_wrap_layout.setContentsMargins(0, 0, 0, 0) + create_btn_wrap_layout.addStretch(1) + create_btn_wrap_layout.addWidget(create_btn, 0) + creator_attrs_layout = QtWidgets.QVBoxLayout(creator_attrs_widget) creator_attrs_layout.setContentsMargins(0, 0, 0, 0) creator_attrs_layout.addWidget(creator_short_desc_widget, 0) creator_attrs_layout.addWidget(separator_widget, 0) creator_attrs_layout.addWidget(pre_create_widget, 1) + creator_attrs_layout.addWidget(create_btn_wrapper, 0) # ------------------------------------- # --- Detailed information about creator --- From 7924affca56d4f68cc5b88ccc6af0509c169b5c7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 29 Apr 2022 14:45:55 +0200 Subject: [PATCH 05/26] make FileDef with vertical label as default --- openpype/lib/attribute_definitions.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index bfac9da5ce..4ea691ca08 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -560,11 +560,7 @@ class FileDef(AbtractAttrDef): # Change horizontal label is_label_horizontal = kwargs.get("is_label_horizontal") if is_label_horizontal is None: - if single_item: - is_label_horizontal = True - else: - is_label_horizontal = False - kwargs["is_label_horizontal"] = is_label_horizontal + kwargs["is_label_horizontal"] = False self.single_item = single_item self.folders = folders From 889f17d5ee3ee46dd702da1e6a989bf61556a6ab Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 29 Apr 2022 15:22:24 +0200 Subject: [PATCH 06/26] wrapped header widgets into single widget --- openpype/tools/utils/assets_widget.py | 55 +++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index 3d4efcdd4d..90e688073d 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -589,10 +589,12 @@ class AssetsWidget(QtWidgets.QWidget): view = AssetsView(self) view.setModel(proxy) + header_widget = QtWidgets.QWidget(self) + current_asset_icon = qtawesome.icon( "fa.arrow-down", color=get_default_tools_icon_color() ) - current_asset_btn = QtWidgets.QPushButton(self) + current_asset_btn = QtWidgets.QPushButton(header_widget) current_asset_btn.setIcon(current_asset_icon) current_asset_btn.setToolTip("Go to Asset from current Session") # Hide by default @@ -601,15 +603,16 @@ class AssetsWidget(QtWidgets.QWidget): refresh_icon = qtawesome.icon( "fa.refresh", color=get_default_tools_icon_color() ) - refresh_btn = QtWidgets.QPushButton(self) + refresh_btn = QtWidgets.QPushButton(header_widget) refresh_btn.setIcon(refresh_icon) refresh_btn.setToolTip("Refresh items") - filter_input = PlaceholderLineEdit(self) + filter_input = PlaceholderLineEdit(header_widget) filter_input.setPlaceholderText("Filter assets..") # Header - header_layout = QtWidgets.QHBoxLayout() + header_layout = QtWidgets.QHBoxLayout(header_widget) + header_layout.setContentsMargins(0, 0, 0, 0) header_layout.addWidget(filter_input) header_layout.addWidget(current_asset_btn) header_layout.addWidget(refresh_btn) @@ -617,9 +620,8 @@ class AssetsWidget(QtWidgets.QWidget): # Layout layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(4) - layout.addLayout(header_layout) - layout.addWidget(view) + layout.addWidget(header_widget, 0) + layout.addWidget(view, 1) # Signals/Slots filter_input.textChanged.connect(self._on_filter_text_change) @@ -630,6 +632,8 @@ class AssetsWidget(QtWidgets.QWidget): current_asset_btn.clicked.connect(self._on_current_asset_click) view.doubleClicked.connect(self.double_clicked) + self._header_widget = header_widget + self._filter_input = filter_input self._refresh_btn = refresh_btn self._current_asset_btn = current_asset_btn self._model = model @@ -637,8 +641,40 @@ class AssetsWidget(QtWidgets.QWidget): self._view = view self._last_project_name = None + self._last_btns_height = None + self.model_selection = {} + @property + def header_widget(self): + return self._header_widget + + def _check_btns_height(self): + """Make buttons to have same height as filter input field. + + There is not handled case when buttons are bigger then filter input. + """ + if self._filter_input.height() == self._last_btns_height: + return + + height = self._filter_input.height() + self._last_btns_height = height + + for widget in ( + self._refresh_btn, + self._current_asset_btn + ): + widget.setMinimumHeight(height) + widget.setMaximumHeight(height) + + def resizeEvent(self, event): + super(AssetsWidget, self).resizeEvent(event) + self._check_btns_height() + + def showEvent(self, event): + super(AssetsWidget, self).showEvent(event) + self._check_btns_height() + def _create_source_model(self): model = AssetModel(dbcon=self.dbcon, parent=self) model.refreshed.connect(self._on_model_refresh) @@ -669,6 +705,7 @@ class AssetsWidget(QtWidgets.QWidget): This separation gives ability to override this method and use it in differnt way. """ + self.set_current_session_asset() def set_current_session_asset(self): @@ -681,6 +718,7 @@ class AssetsWidget(QtWidgets.QWidget): Some tools may have their global refresh button or do not support refresh at all. """ + if visible is None: visible = not self._refresh_btn.isVisible() self._refresh_btn.setVisible(visible) @@ -690,6 +728,7 @@ class AssetsWidget(QtWidgets.QWidget): Not all tools support using of current context asset. """ + if visible is None: visible = not self._current_asset_btn.isVisible() self._current_asset_btn.setVisible(visible) @@ -723,6 +762,7 @@ class AssetsWidget(QtWidgets.QWidget): so if you're modifying model keep in mind that this method should be called when refresh is done. """ + self._proxy.sort(0) self._set_loading_state(loading=False, empty=not has_item) self.refreshed.emit() @@ -767,6 +807,7 @@ class SingleSelectAssetsWidget(AssetsWidget): Contain single selection specific api methods. """ + def get_selected_asset_id(self): """Currently selected asset id.""" selection_model = self._view.selectionModel() From b17a71fe1466c84d39b74228c066a0819a9b32c2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 29 Apr 2022 15:43:06 +0200 Subject: [PATCH 07/26] simplified btns height in assets widget --- openpype/tools/utils/assets_widget.py | 36 ++++++++------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index 90e688073d..d1df1193d2 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -617,6 +617,16 @@ class AssetsWidget(QtWidgets.QWidget): header_layout.addWidget(current_asset_btn) header_layout.addWidget(refresh_btn) + # Make header widgets expand vertically if there is a place + for widget in ( + current_asset_btn, + refresh_btn, + filter_input, + ): + size_policy = widget.sizePolicy() + size_policy.setVerticalPolicy(size_policy.MinimumExpanding) + widget.setSizePolicy(size_policy) + # Layout layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) @@ -649,32 +659,6 @@ class AssetsWidget(QtWidgets.QWidget): def header_widget(self): return self._header_widget - def _check_btns_height(self): - """Make buttons to have same height as filter input field. - - There is not handled case when buttons are bigger then filter input. - """ - if self._filter_input.height() == self._last_btns_height: - return - - height = self._filter_input.height() - self._last_btns_height = height - - for widget in ( - self._refresh_btn, - self._current_asset_btn - ): - widget.setMinimumHeight(height) - widget.setMaximumHeight(height) - - def resizeEvent(self, event): - super(AssetsWidget, self).resizeEvent(event) - self._check_btns_height() - - def showEvent(self, event): - super(AssetsWidget, self).showEvent(event) - self._check_btns_height() - def _create_source_model(self): model = AssetModel(dbcon=self.dbcon, parent=self) model.refreshed.connect(self._on_model_refresh) From 1bc4d6c66a554b2902372546178ddce1220f8638 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 29 Apr 2022 16:14:50 +0200 Subject: [PATCH 08/26] keep creators view label at same height as asset filtering --- .../tools/publisher/widgets/assets_widget.py | 24 +++++++++++++++++++ .../tools/publisher/widgets/create_dialog.py | 19 ++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/assets_widget.py b/openpype/tools/publisher/widgets/assets_widget.py index 984da59c77..c4d3cf8b1a 100644 --- a/openpype/tools/publisher/widgets/assets_widget.py +++ b/openpype/tools/publisher/widgets/assets_widget.py @@ -15,6 +15,7 @@ from openpype.tools.utils.assets_widget import ( class CreateDialogAssetsWidget(SingleSelectAssetsWidget): current_context_required = QtCore.Signal() + header_height_changed = QtCore.Signal(int) def __init__(self, controller, parent): self._controller = controller @@ -27,6 +28,27 @@ class CreateDialogAssetsWidget(SingleSelectAssetsWidget): self._last_selection = None self._enabled = None + self._last_filter_height = None + + def _check_header_height(self): + """Catch header height changes. + + Label on top of creaters should have same height so Creators view has + same offset. + """ + height = self.header_widget.height() + if height != self._last_filter_height: + self._last_filter_height = height + self.header_height_changed.emit(height) + + def resizeEvent(self, event): + super(CreateDialogAssetsWidget, self).resizeEvent(event) + self._check_header_height() + + def showEvent(self, event): + super(CreateDialogAssetsWidget, self).showEvent(event) + self._check_header_height() + def _on_current_asset_click(self): self.current_context_required.emit() @@ -71,6 +93,7 @@ class AssetsHierarchyModel(QtGui.QStandardItemModel): Uses controller to load asset hierarchy. All asset documents are stored by their parents. """ + def __init__(self, controller): super(AssetsHierarchyModel, self).__init__() self._controller = controller @@ -143,6 +166,7 @@ class AssetsHierarchyModel(QtGui.QStandardItemModel): class AssetsDialog(QtWidgets.QDialog): """Dialog to select asset for a context of instance.""" + def __init__(self, controller, parent): super(AssetsDialog, self).__init__(parent) self.setWindowTitle("Select asset") diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index dd1e00b79b..00a9ac785d 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -255,6 +255,14 @@ class CreateDialog(QtWidgets.QDialog): context_layout.addWidget(tasks_widget, 1) # --- Creators view --- + creators_header_widget = QtWidgets.QWidget(self) + header_label_widget = QtWidgets.QLabel( + "Choose family:", creators_header_widget + ) + creators_header_layout = QtWidgets.QHBoxLayout(creators_header_widget) + creators_header_layout.setContentsMargins(0, 0, 0, 0) + creators_header_layout.addWidget(header_label_widget, 1) + creators_view = QtWidgets.QListView(self) creators_model = QtGui.QStandardItemModel() creators_view.setModel(creators_model) @@ -289,7 +297,7 @@ class CreateDialog(QtWidgets.QDialog): mid_widget = QtWidgets.QWidget(self) mid_layout = QtWidgets.QVBoxLayout(mid_widget) mid_layout.setContentsMargins(0, 0, 0, 0) - mid_layout.addWidget(QtWidgets.QLabel("Choose family:", self)) + mid_layout.addWidget(creators_header_widget, 0) mid_layout.addWidget(creators_view, 1) mid_layout.addLayout(form_layout, 0) # ------------ @@ -362,6 +370,10 @@ class CreateDialog(QtWidgets.QDialog): help_btn.clicked.connect(self._on_help_btn) help_btn.resized.connect(self._on_help_btn_resize) + assets_widget.header_height_changed.connect( + self._on_asset_filter_height_change + ) + create_btn.clicked.connect(self._on_create) variant_widget.resized.connect(self._on_variant_widget_resize) variant_input.returnPressed.connect(self._on_create) @@ -394,6 +406,7 @@ class CreateDialog(QtWidgets.QDialog): self.variant_hints_menu = variant_hints_menu self.variant_hints_group = variant_hints_group + self._creators_header_widget = creators_header_widget self.creators_model = creators_model self.creators_view = creators_view self.create_btn = create_btn @@ -472,6 +485,10 @@ class CreateDialog(QtWidgets.QDialog): def _invalidate_prereq(self): self._prereq_timer.start() + def _on_asset_filter_height_change(self, height): + self._creators_header_widget.setMinimumHeight(height) + self._creators_header_widget.setMaximumHeight(height) + def _on_prereq_timer(self): prereq_available = True creator_btn_tooltips = [] From 48e887f2398a70ee9797599118c934b9dbe523f7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 29 Apr 2022 16:39:58 +0200 Subject: [PATCH 09/26] modified description heights --- .../tools/publisher/widgets/create_dialog.py | 78 +++++++++++++++---- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index 00a9ac785d..cc47e9f175 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -114,6 +114,8 @@ class CreateErrorMessageBox(ErrorMessageBox): # TODO add creator identifier/label to details class CreatorShortDescWidget(QtWidgets.QWidget): + height_changed = QtCore.Signal(int) + def __init__(self, parent=None): super(CreatorShortDescWidget, self).__init__(parent=parent) @@ -152,6 +154,22 @@ class CreatorShortDescWidget(QtWidgets.QWidget): self._family_label = family_label self._description_label = description_label + self._last_height = None + + def _check_height_change(self): + height = self.height() + if height != self._last_height: + self._last_height = height + self.height_changed.emit(height) + + def showEvent(self, event): + super(CreatorShortDescWidget, self).showEvent(event) + self._check_height_change() + + def resizeEvent(self, event): + super(CreatorShortDescWidget, self).resizeEvent(event) + self._check_height_change() + def set_plugin(self, plugin=None): if not plugin: self._icon_widget.set_icon_def(None) @@ -309,10 +327,10 @@ class CreateDialog(QtWidgets.QDialog): creator_attrs_widget ) - separator_widget = QtWidgets.QWidget(self) - separator_widget.setObjectName("Separator") - separator_widget.setMinimumHeight(2) - separator_widget.setMaximumHeight(2) + attr_separator_widget = QtWidgets.QWidget(self) + attr_separator_widget.setObjectName("Separator") + attr_separator_widget.setMinimumHeight(2) + attr_separator_widget.setMaximumHeight(2) # Precreate attributes widget pre_create_widget = PreCreateWidget(creator_attrs_widget) @@ -330,21 +348,41 @@ class CreateDialog(QtWidgets.QDialog): creator_attrs_layout = QtWidgets.QVBoxLayout(creator_attrs_widget) creator_attrs_layout.setContentsMargins(0, 0, 0, 0) creator_attrs_layout.addWidget(creator_short_desc_widget, 0) - creator_attrs_layout.addWidget(separator_widget, 0) + creator_attrs_layout.addWidget(attr_separator_widget, 0) creator_attrs_layout.addWidget(pre_create_widget, 1) creator_attrs_layout.addWidget(create_btn_wrapper, 0) # ------------------------------------- # --- Detailed information about creator --- # Detailed description of creator - detail_description_widget = QtWidgets.QTextEdit(self) - detail_description_widget.setObjectName("InfoText") - detail_description_widget.setTextInteractionFlags( + detail_description_widget = QtWidgets.QWidget(self) + + detail_placoholder_widget = QtWidgets.QWidget( + detail_description_widget + ) + detail_placoholder_widget.setAttribute( + QtCore.Qt.WA_TranslucentBackground + ) + + detail_description_input = QtWidgets.QTextEdit( + detail_description_widget + ) + detail_description_input.setObjectName("InfoText") + detail_description_input.setTextInteractionFlags( QtCore.Qt.TextBrowserInteraction ) - detail_description_widget.setVisible(False) - # ------------------------------------------- + detail_description_layout = QtWidgets.QVBoxLayout( + detail_description_widget + ) + detail_description_layout.setContentsMargins(0, 0, 0, 0) + detail_description_layout.setSpacing(0) + detail_description_layout.addWidget(detail_placoholder_widget, 0) + detail_description_layout.addWidget(detail_description_input, 1) + + detail_description_widget.setVisible(False) + + # ------------------------------------------- splitter_widget = QtWidgets.QSplitter(self) splitter_widget.addWidget(context_widget) splitter_widget.addWidget(mid_widget) @@ -359,6 +397,7 @@ class CreateDialog(QtWidgets.QDialog): layout.addWidget(splitter_widget, 1) # Floating help button + # - Create this button as last to be fully visible help_btn = HelpButton(self) prereq_timer = QtCore.QTimer() @@ -388,6 +427,9 @@ class CreateDialog(QtWidgets.QDialog): self._on_current_session_context_request ) tasks_widget.task_changed.connect(self._on_task_change) + creator_short_desc_widget.height_changed.connect( + self._on_description_height_change + ) controller.add_plugins_refresh_callback(self._on_plugins_refresh) @@ -413,7 +455,11 @@ class CreateDialog(QtWidgets.QDialog): self._creator_short_desc_widget = creator_short_desc_widget self._pre_create_widget = pre_create_widget + self._attr_separator_widget = attr_separator_widget + + self._detail_placoholder_widget = detail_placoholder_widget self._detail_description_widget = detail_description_widget + self._detail_description_input = detail_description_input self._help_btn = help_btn self._prereq_timer = prereq_timer @@ -619,6 +665,12 @@ class CreateDialog(QtWidgets.QDialog): if self._task_name: self._tasks_widget.select_task_name(self._task_name) + def _on_description_height_change(self): + # Use separator's 'y' position as height + height = self._attr_separator_widget.y() + self._detail_placoholder_widget.setMinimumHeight(height) + self._detail_placoholder_widget.setMaximumHeight(height) + def _on_creator_item_change(self, new_index, _old_index): identifier = None if new_index.isValid(): @@ -666,14 +718,14 @@ class CreateDialog(QtWidgets.QDialog): def _set_creator_detailed_text(self, creator): if not creator: - self._detail_description_widget.setPlainText("") + self._detail_description_input.setPlainText("") return detailed_description = creator.get_detail_description() or "" if commonmark: html = commonmark.commonmark(detailed_description) - self._detail_description_widget.setHtml(html) + self._detail_description_input.setHtml(html) else: - self._detail_description_widget.setMarkdown(detailed_description) + self._detail_description_input.setMarkdown(detailed_description) def _set_creator_by_identifier(self, identifier): creator = self.controller.manual_creators.get(identifier) From 542efb57bc5df0a6e966c3fa6ac265bce693a599 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 29 Apr 2022 16:51:19 +0200 Subject: [PATCH 10/26] change style of detailed description --- openpype/style/style.css | 8 ++++++++ openpype/tools/publisher/widgets/create_dialog.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/openpype/style/style.css b/openpype/style/style.css index ae04a433fb..4032732a78 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -856,6 +856,14 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { } /* New Create/Publish UI */ +#CreatorDetailedDescription { + padding-left: 5px; + padding-right: 5px; + padding-top: 5px; + background: transparent; + border: 1px solid {color:border}; +} + #CreateDialogHelpButton { background: rgba(255, 255, 255, 31); border-top-right-radius: 0; diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index cc47e9f175..8e1ab3502d 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -367,7 +367,7 @@ class CreateDialog(QtWidgets.QDialog): detail_description_input = QtWidgets.QTextEdit( detail_description_widget ) - detail_description_input.setObjectName("InfoText") + detail_description_input.setObjectName("CreatorDetailedDescription") detail_description_input.setTextInteractionFlags( QtCore.Qt.TextBrowserInteraction ) From 99096269b52db11ad2bb5f9040a221cafbee97d7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 29 Apr 2022 19:29:36 +0200 Subject: [PATCH 11/26] change separator size --- openpype/tools/publisher/widgets/create_dialog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index 8e1ab3502d..8c4f9d52a1 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -329,8 +329,8 @@ class CreateDialog(QtWidgets.QDialog): attr_separator_widget = QtWidgets.QWidget(self) attr_separator_widget.setObjectName("Separator") - attr_separator_widget.setMinimumHeight(2) - attr_separator_widget.setMaximumHeight(2) + attr_separator_widget.setMinimumHeight(1) + attr_separator_widget.setMaximumHeight(1) # Precreate attributes widget pre_create_widget = PreCreateWidget(creator_attrs_widget) From 833027f163c6a313db9ff26417b39ab3276fb600 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 29 Apr 2022 19:29:58 +0200 Subject: [PATCH 12/26] animate description showing --- .../tools/publisher/widgets/create_dialog.py | 137 ++++++++++++++++-- 1 file changed, 128 insertions(+), 9 deletions(-) diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index 8c4f9d52a1..10be47c517 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -404,8 +404,13 @@ class CreateDialog(QtWidgets.QDialog): prereq_timer.setInterval(50) prereq_timer.setSingleShot(True) + desc_width_anim_timer = QtCore.QTimer() + desc_width_anim_timer.setInterval(10) + prereq_timer.timeout.connect(self._on_prereq_timer) + desc_width_anim_timer.timeout.connect(self._on_desc_animation) + help_btn.clicked.connect(self._on_help_btn) help_btn.resized.connect(self._on_help_btn_resize) @@ -465,6 +470,15 @@ class CreateDialog(QtWidgets.QDialog): self._prereq_timer = prereq_timer self._first_show = True + # Description animation + self._description_size_policy = detail_description_widget.sizePolicy() + self._desc_width_anim_timer = desc_width_anim_timer + self._desc_widget_step = 0 + self._last_description_width = None + self._last_full_width = 0 + self._expected_description_width = 0 + self._other_widgets_widths = [] + def _emit_message(self, message): self._overlay_object.add_message(message) @@ -688,34 +702,139 @@ class CreateDialog(QtWidgets.QDialog): self._update_help_btn() def _on_help_btn(self): + if self._desc_width_anim_timer.isActive(): + return + final_size = self.size() cur_sizes = self._splitter_widget.sizes() - spacing = self._splitter_widget.handleWidth() + + if self._desc_widget_step == 0: + now_visible = self._detail_description_widget.isVisible() + else: + now_visible = self._desc_widget_step > 0 sizes = [] for idx, value in enumerate(cur_sizes): if idx < 3: sizes.append(value) - now_visible = self._detail_description_widget.isVisible() + self._last_full_width = final_size.width() + self._other_widgets_widths = list(sizes) + if now_visible: - width = final_size.width() - ( - spacing + self._detail_description_widget.width() - ) + cur_desc_width = self._detail_description_widget.width() + if cur_desc_width < 1: + cur_desc_width = 2 + step_size = int(cur_desc_width / 5) + if step_size < 1: + step_size = 1 + + step_size *= -1 + expected_width = 0 + desc_width = cur_desc_width - 1 + width = final_size.width() - 1 + min_max = desc_width + self._last_description_width = cur_desc_width else: - last_size = self._detail_description_widget.sizeHint().width() - width = final_size.width() + spacing + last_size - sizes.append(last_size) + self._detail_description_widget.setVisible(True) + handle = self._splitter_widget.handle(3) + desc_width = handle.sizeHint().width() + if self._last_description_width: + expected_width = self._last_description_width + else: + hint = self._detail_description_widget.sizeHint() + expected_width = hint.width() + + width = final_size.width() + desc_width + step_size = int(expected_width / 5) + if step_size < 1: + step_size = 1 + min_max = 0 + + self._detail_description_widget.setMinimumWidth(min_max) + self._detail_description_widget.setMaximumWidth(min_max) + self._expected_description_width = expected_width + self._desc_widget_step = step_size + + self._desc_width_anim_timer.start() + + sizes.append(desc_width) final_size.setWidth(width) - self._detail_description_widget.setVisible(not now_visible) self._splitter_widget.setSizes(sizes) self.resize(final_size) self._help_btn.set_expanded(not now_visible) + def _on_desc_animation(self): + current_width = self._detail_description_widget.width() + + desc_width = None + last_step = False + growing = self._desc_widget_step > 0 + + # Growing + if growing: + if current_width < self._expected_description_width: + desc_width = current_width + self._desc_widget_step + if desc_width >= self._expected_description_width: + desc_width = self._expected_description_width + last_step = True + + # Decreasing + elif self._desc_widget_step < 0: + if current_width > self._expected_description_width: + desc_width = current_width + self._desc_widget_step + if desc_width <= self._expected_description_width: + desc_width = self._expected_description_width + last_step = True + + if desc_width is None: + self._desc_widget_step = 0 + self._desc_width_anim_timer.stop() + return + + if last_step and not growing: + self._detail_description_widget.setVisible(False) + QtWidgets.QApplication.processEvents() + + width = self._last_full_width + handle_width = self._splitter_widget.handle(3).width() + if growing: + width += (handle_width + desc_width) + else: + width -= self._last_description_width + if last_step: + width -= handle_width + else: + width += desc_width + + if not last_step or growing: + self._detail_description_widget.setMaximumWidth(desc_width) + self._detail_description_widget.setMinimumWidth(desc_width) + + window_size = self.size() + window_size.setWidth(width) + self.resize(window_size) + if not last_step: + return + + self._desc_widget_step = 0 + self._desc_width_anim_timer.stop() + + if not growing: + return + + self._detail_description_widget.setSizePolicy( + self._description_size_policy + ) + + sizes = list(self._other_widgets_widths) + sizes.append(desc_width) + self._splitter_widget.setSizes(sizes) + def _set_creator_detailed_text(self, creator): if not creator: self._detail_description_input.setPlainText("") From e6326570f8770251658b531ff3b3bd64c37a99f2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 2 May 2022 18:35:58 +0200 Subject: [PATCH 13/26] help button is resized and has more content --- openpype/style/style.css | 7 +- .../tools/publisher/widgets/create_dialog.py | 115 +++++++++++++++--- 2 files changed, 101 insertions(+), 21 deletions(-) diff --git a/openpype/style/style.css b/openpype/style/style.css index 4032732a78..8eeae1a58a 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -866,16 +866,21 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { #CreateDialogHelpButton { background: rgba(255, 255, 255, 31); + border-top-left-radius: 0.2em; + border-bottom-left-radius: 0.2em; border-top-right-radius: 0; border-bottom-right-radius: 0; font-size: 10pt; font-weight: bold; - padding: 3px 3px 3px 3px; + padding: 0px; } #CreateDialogHelpButton:hover { background: rgba(255, 255, 255, 63); } +#CreateDialogHelpButton QWidget { + background: transparent; +} #PublishLogConsole { font-family: "Noto Sans Mono"; diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index 10be47c517..8b2710b165 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -3,6 +3,7 @@ import re import traceback import copy +import qtawesome try: import commonmark except Exception: @@ -15,7 +16,8 @@ from openpype.pipeline.create import ( ) from openpype.tools.utils import ( ErrorMessageBox, - MessageOverlayObject + MessageOverlayObject, + ClickableFrame, ) from .widgets import IconValuePixmapLabel @@ -186,13 +188,43 @@ class CreatorShortDescWidget(QtWidgets.QWidget): self._description_label.setText(description) -class HelpButton(QtWidgets.QPushButton): - resized = QtCore.Signal() +class HelpButton(ClickableFrame): + resized = QtCore.Signal(int) + question_mark_icon_name = "fa.question" + help_icon_name = "fa.question-circle" + hide_icon_name = "fa.angle-left" def __init__(self, *args, **kwargs): super(HelpButton, self).__init__(*args, **kwargs) self.setObjectName("CreateDialogHelpButton") + question_mark_label = QtWidgets.QLabel(self) + help_widget = QtWidgets.QWidget(self) + + help_question = QtWidgets.QLabel(help_widget) + help_label = QtWidgets.QLabel("Help", help_widget) + hide_icon = QtWidgets.QLabel(help_widget) + + help_layout = QtWidgets.QHBoxLayout(help_widget) + help_layout.setContentsMargins(0, 0, 5, 0) + help_layout.addWidget(help_question, 0) + help_layout.addWidget(help_label, 0) + help_layout.addStretch(1) + help_layout.addWidget(hide_icon, 0) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + layout.addWidget(question_mark_label, 0) + layout.addWidget(help_widget, 1) + + help_widget.setVisible(False) + + self._question_mark_label = question_mark_label + self._help_widget = help_widget + self._help_question = help_question + self._hide_icon = hide_icon + self._expanded = None self.set_expanded() @@ -202,27 +234,52 @@ class HelpButton(QtWidgets.QPushButton): return expanded = False self._expanded = expanded - if expanded: - text = "<" + self._help_widget.setVisible(expanded) + self._update_content() + + def _update_content(self): + width = self.get_icon_width() + if self._expanded: + question_mark_pix = QtGui.QPixmap(width, width) + question_mark_pix.fill(QtCore.Qt.transparent) + else: - text = "?" - self.setText(text) + question_mark_icon = qtawesome.icon( + self.question_mark_icon_name, color=QtCore.Qt.white + ) + question_mark_pix = question_mark_icon.pixmap(width, width) - self._update_size() + hide_icon = qtawesome.icon( + self.hide_icon_name, color=QtCore.Qt.white + ) + help_question_icon = qtawesome.icon( + self.help_icon_name, color=QtCore.Qt.white + ) + self._question_mark_label.setPixmap(question_mark_pix) + self._question_mark_label.setMaximumWidth(width) + self._hide_icon.setPixmap(hide_icon.pixmap(width, width)) + self._help_question.setPixmap(help_question_icon.pixmap(width, width)) - def _update_size(self): - new_size = self.minimumSizeHint() - if self.size() != new_size: - self.resize(new_size) - self.resized.emit() + def get_icon_width(self): + metrics = self.fontMetrics() + return metrics.height() + + def set_pos_and_size(self, pos_x, pos_y, width, height): + update_icon = self.height() != height + self.move(pos_x, pos_y) + self.resize(width, height) + + if update_icon: + self._update_content() + self.updateGeometry() def showEvent(self, event): super(HelpButton, self).showEvent(event) - self._update_size() + self.resized.emit(self.height()) def resizeEvent(self, event): super(HelpButton, self).resizeEvent(event) - self._update_size() + self.resized.emit(self.height()) class CreateDialog(QtWidgets.QDialog): @@ -692,14 +749,32 @@ class CreateDialog(QtWidgets.QDialog): self._set_creator_by_identifier(identifier) def _update_help_btn(self): - pos_x = self.width() - self._help_btn.width() - point = self._creator_short_desc_widget.rect().topRight() + short_desc_rect = self._creator_short_desc_widget.rect() + height = short_desc_rect.height() + + point = short_desc_rect.topRight() mapped_point = self._creator_short_desc_widget.mapTo(self, point) pos_y = mapped_point.y() - self._help_btn.move(max(0, pos_x), max(0, pos_y)) - def _on_help_btn_resize(self): - self._update_help_btn() + icon_width = self._help_btn.get_icon_width() + + pos_x = self.width() - icon_width + if self._detail_placoholder_widget.isVisible(): + pos_x -= ( + self._detail_placoholder_widget.width() + + self._splitter_widget.handle(3).width() + ) + + width = self.width() - pos_x + + self._help_btn.set_pos_and_size( + max(0, pos_x), max(0, pos_y), + width, height + ) + + def _on_help_btn_resize(self, height): + if self._creator_short_desc_widget.height() != height: + self._update_help_btn() def _on_help_btn(self): if self._desc_width_anim_timer.isActive(): From b7f859c0b00c41cb511f256160b4d17d7164e616 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 2 May 2022 19:27:27 +0200 Subject: [PATCH 14/26] fixed description min/max sizes --- .../tools/publisher/widgets/create_dialog.py | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index 8b2710b165..bcf87dea6a 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -492,6 +492,7 @@ class CreateDialog(QtWidgets.QDialog): creator_short_desc_widget.height_changed.connect( self._on_description_height_change ) + splitter_widget.splitterMoved.connect(self._on_splitter_move) controller.add_plugins_refresh_callback(self._on_plugins_refresh) @@ -534,6 +535,7 @@ class CreateDialog(QtWidgets.QDialog): self._last_description_width = None self._last_full_width = 0 self._expected_description_width = 0 + self._last_desc_max_width = None self._other_widgets_widths = [] def _emit_message(self, message): @@ -750,14 +752,18 @@ class CreateDialog(QtWidgets.QDialog): def _update_help_btn(self): short_desc_rect = self._creator_short_desc_widget.rect() - height = short_desc_rect.height() - point = short_desc_rect.topRight() + # point = short_desc_rect.topRight() + point = short_desc_rect.center() mapped_point = self._creator_short_desc_widget.mapTo(self, point) - pos_y = mapped_point.y() - + # pos_y = mapped_point.y() + center_pos_y = mapped_point.y() icon_width = self._help_btn.get_icon_width() + _height = int(icon_width * 2.5) + height = min(_height, short_desc_rect.height()) + pos_y = center_pos_y - int(height / 2) + pos_x = self.width() - icon_width if self._detail_placoholder_widget.isVisible(): pos_x -= ( @@ -776,6 +782,9 @@ class CreateDialog(QtWidgets.QDialog): if self._creator_short_desc_widget.height() != height: self._update_help_btn() + def _on_splitter_move(self, *args): + self._update_help_btn() + def _on_help_btn(self): if self._desc_width_anim_timer.isActive(): return @@ -827,6 +836,10 @@ class CreateDialog(QtWidgets.QDialog): step_size = 1 min_max = 0 + if self._last_desc_max_width is None: + self._last_desc_max_width = ( + self._detail_description_widget.maximumWidth() + ) self._detail_description_widget.setMinimumWidth(min_max) self._detail_description_widget.setMaximumWidth(min_max) self._expected_description_width = expected_width @@ -902,6 +915,10 @@ class CreateDialog(QtWidgets.QDialog): if not growing: return + self._detail_description_widget.setMinimumWidth(0) + self._detail_description_widget.setMaximumWidth( + self._last_desc_max_width + ) self._detail_description_widget.setSizePolicy( self._description_size_policy ) From dc94809957803a7ab57c027ff2b82b504fe78f38 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 3 May 2022 14:35:45 +0200 Subject: [PATCH 15/26] added icon to asset input dialog --- openpype/style/style.css | 19 ++++++++++++++- openpype/tools/publisher/widgets/widgets.py | 26 +++++++++++++++++---- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/openpype/style/style.css b/openpype/style/style.css index 8eeae1a58a..93b13916c1 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -1027,7 +1027,24 @@ VariantInputsWidget QToolButton { border-left: 1px solid {color:border}; } -#TasksCombobox[state="invalid"], #AssetNameInput[state="invalid"] { +#AssetNameInputButton { + background: {color:bg-inputs}; + border: 1px solid {color:border}; + border-bottom-left-radius: 0px; + border-top-left-radius: 0px; + padding: 0px; + qproperty-iconSize: 11px 11px; +} +#AssetNameInputButton:disabled { + background: {color:bg-inputs-disabled}; +} +#AssetNameInput { + border-bottom-right-radius: 0px; + border-top-right-radius: 0px; + border-right: none; +} + +#TasksCombobox[state="invalid"], #AssetNameInput[state="invalid"], #AssetNameInputButton[state="invalid"] { border-color: {color:publisher:error}; } diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 5ced469b59..1569e1a4c1 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -14,7 +14,8 @@ from openpype.tools.utils import ( PlaceholderLineEdit, IconButton, PixmapLabel, - BaseClickableFrame + BaseClickableFrame, + set_style_property, ) from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS from .assets_widget import AssetsDialog @@ -350,15 +351,32 @@ class AssetsField(BaseClickableFrame): name_input = ClickableLineEdit(self) name_input.setObjectName("AssetNameInput") + icon_name = "fa.window-maximize" + icon = qtawesome.icon(icon_name, color="white") + icon_btn = QtWidgets.QPushButton(self) + icon_btn.setIcon(icon) + icon_btn.setObjectName("AssetNameInputButton") + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) layout.addWidget(name_input, 1) + layout.addWidget(icon_btn, 0) + for widget in ( + name_input, + icon_btn + ): + size_policy = widget.sizePolicy() + size_policy.setVerticalPolicy(size_policy.MinimumExpanding) + widget.setSizePolicy(size_policy) name_input.clicked.connect(self._mouse_release_callback) + icon_btn.clicked.connect(self._mouse_release_callback) dialog.finished.connect(self._on_dialog_finish) self._dialog = dialog self._name_input = name_input + self._icon_btn = icon_btn self._origin_value = [] self._origin_selection = [] @@ -406,10 +424,8 @@ class AssetsField(BaseClickableFrame): self._set_state_property(state) def _set_state_property(self, state): - current_value = self._name_input.property("state") - if current_value != state: - self._name_input.setProperty("state", state) - self._name_input.style().polish(self._name_input) + set_style_property(self._name_input, "state", state) + set_style_property(self._icon_btn, "state", state) def is_valid(self): """Is asset valid.""" From b17d9d23497691a4cea6174c323a2bb3cf27e870 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 3 May 2022 15:22:32 +0200 Subject: [PATCH 16/26] fixed empty files def --- openpype/lib/attribute_definitions.py | 14 +++++++++++++- openpype/widgets/attribute_defs/files_widget.py | 6 ++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index 4ea691ca08..a1f7c1e0f4 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -316,6 +316,7 @@ class FileDefItem(object): self.is_sequence = False self.template = None self.frames = [] + self.is_empty = True self.set_filenames(filenames, frames, template) @@ -323,7 +324,9 @@ class FileDefItem(object): return json.dumps(self.to_dict()) def __repr__(self): - if self.is_sequence: + if self.is_empty: + filename = "< empty >" + elif self.is_sequence: filename = self.template else: filename = self.filenames[0] @@ -335,6 +338,9 @@ class FileDefItem(object): @property def label(self): + if self.is_empty: + return None + if not self.is_sequence: return self.filenames[0] @@ -386,6 +392,8 @@ class FileDefItem(object): @property def ext(self): + if self.is_empty: + return None _, ext = os.path.splitext(self.filenames[0]) if ext: return ext @@ -393,6 +401,9 @@ class FileDefItem(object): @property def is_dir(self): + if self.is_empty: + return False + # QUESTION a better way how to define folder (in init argument?) if self.ext: return False @@ -411,6 +422,7 @@ class FileDefItem(object): if is_sequence and not template: raise ValueError("Missing template for sequence") + self.is_empty = len(filenames) == 0 self.filenames = filenames self.template = template self.frames = frames diff --git a/openpype/widgets/attribute_defs/files_widget.py b/openpype/widgets/attribute_defs/files_widget.py index 924dbf7fe5..23cf8342b1 100644 --- a/openpype/widgets/attribute_defs/files_widget.py +++ b/openpype/widgets/attribute_defs/files_widget.py @@ -151,7 +151,7 @@ class FilesModel(QtGui.QStandardItemModel): item = QtGui.QStandardItem() item_id = str(uuid.uuid4()) item.setData(item_id, ITEM_ID_ROLE) - item.setData(file_item.label, ITEM_LABEL_ROLE) + item.setData(file_item.label or "< empty >", ITEM_LABEL_ROLE) item.setData(file_item.filenames, FILENAMES_ROLE) item.setData(file_item.directory, DIRPATH_ROLE) item.setData(icon_pixmap, ITEM_ICON_ROLE) @@ -512,7 +512,9 @@ class FilesWidget(QtWidgets.QFrame): return file_items if file_items: return file_items[0] - return FileDefItem.create_empty_item() + + empty_item = FileDefItem.create_empty_item() + return empty_item.to_dict() def set_filters(self, folders_allowed, exts_filter): self._files_proxy_model.set_allow_folders(folders_allowed) From 33c3d1bc4c8278690c522f1b74b1fe5a2705ed5d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 3 May 2022 15:23:22 +0200 Subject: [PATCH 17/26] resize properly families and subset names --- openpype/tools/publisher/widgets/widgets.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 1569e1a4c1..45c42e6558 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -908,11 +908,23 @@ class MultipleItemWidget(QtWidgets.QWidget): layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(view) + model.rowsInserted.connect(self._on_insert) + self._view = view self._model = model self._value = [] + def _on_insert(self): + self._update_size() + + def _update_size(self): + model = self._view.model() + if model.rowCount() == 0: + return + height = self._view.sizeHintForRow(0) + self.setMaximumHeight(height + (2 * self._view.spacing())) + def showEvent(self, event): super(MultipleItemWidget, self).showEvent(event) tmp_item = None @@ -920,13 +932,15 @@ class MultipleItemWidget(QtWidgets.QWidget): # Add temp item to be able calculate maximum height of widget tmp_item = QtGui.QStandardItem("tmp") self._model.appendRow(tmp_item) - - height = self._view.sizeHintForRow(0) - self.setMaximumHeight(height + (2 * self._view.spacing())) + self._update_size() if tmp_item is not None: self._model.clear() + def resizeEvent(self, event): + super(MultipleItemWidget, self).resizeEvent(event) + self._update_size() + def set_value(self, value=None): """Set value/s of currently selected instance.""" if value is None: From 9eb9c62318f0a00b4ceb5c21bd42c839bedaa9be Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 3 May 2022 15:55:38 +0200 Subject: [PATCH 18/26] different asset dialog size --- .../tools/publisher/widgets/assets_widget.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/openpype/tools/publisher/widgets/assets_widget.py b/openpype/tools/publisher/widgets/assets_widget.py index c4d3cf8b1a..46fdcc6526 100644 --- a/openpype/tools/publisher/widgets/assets_widget.py +++ b/openpype/tools/publisher/widgets/assets_widget.py @@ -220,9 +220,26 @@ class AssetsDialog(QtWidgets.QDialog): # - adds ability to call reset on multiple places without repeating self._soft_reset_enabled = True + self._first_show = True + self._default_height = 500 + + def _on_first_show(self): + center = self.rect().center() + size = self.size() + size.setHeight(self._default_height) + + self.resize(size) + new_pos = self.mapToGlobal(center) + new_pos.setX(new_pos.x() - int(self.width() / 2)) + new_pos.setY(new_pos.y() - int(self.height() / 2)) + self.move(new_pos) + def showEvent(self, event): """Refresh asset model on show.""" super(AssetsDialog, self).showEvent(event) + if self._first_show: + self._first_show = False + self._on_first_show() # Refresh on show self.reset(False) From 5ca7b53c11e9b9fe66bd05947d04edadd8efb0d4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 3 May 2022 15:56:01 +0200 Subject: [PATCH 19/26] fix styles --- openpype/style/style.css | 32 +++++++++++++++++---- openpype/tools/publisher/widgets/widgets.py | 6 +++- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/openpype/style/style.css b/openpype/style/style.css index 93b13916c1..d76d833be1 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -1027,24 +1027,44 @@ VariantInputsWidget QToolButton { border-left: 1px solid {color:border}; } -#AssetNameInputButton { +#AssetNameInputWidget { background: {color:bg-inputs}; border: 1px solid {color:border}; + border-radius: 0.3em; +} + +#AssetNameInputWidget QWidget { + background: transparent; +} + +#AssetNameInputButton { border-bottom-left-radius: 0px; border-top-left-radius: 0px; padding: 0px; qproperty-iconSize: 11px 11px; + border-left: 1px solid {color:border}; + border-right: none; + border-top: none; + border-bottom: none; } -#AssetNameInputButton:disabled { - background: {color:bg-inputs-disabled}; -} + #AssetNameInput { border-bottom-right-radius: 0px; border-top-right-radius: 0px; - border-right: none; + border: none; } -#TasksCombobox[state="invalid"], #AssetNameInput[state="invalid"], #AssetNameInputButton[state="invalid"] { +#AssetNameInputWidget:hover { + border-color: {color:border-hover}; +} +#AssetNameInputWidget:focus{ + border-color: {color:border-focus}; +} +#AssetNameInputWidget:disabled { + background: {color:bg-inputs-disabled}; +} + +#TasksCombobox[state="invalid"], #AssetNameInputWidget[state="invalid"], #AssetNameInputButton[state="invalid"] { border-color: {color:publisher:error}; } diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 45c42e6558..6c09128857 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -345,8 +345,11 @@ class AssetsField(BaseClickableFrame): def __init__(self, controller, parent): super(AssetsField, self).__init__(parent) + self.setObjectName("AssetNameInputWidget") - dialog = AssetsDialog(controller, self) + # Don't use 'self' for parent! + # - this widget has specific styles + dialog = AssetsDialog(controller, parent) name_input = ClickableLineEdit(self) name_input.setObjectName("AssetNameInput") @@ -363,6 +366,7 @@ class AssetsField(BaseClickableFrame): layout.addWidget(name_input, 1) layout.addWidget(icon_btn, 0) + # Make sure all widgets are vertically extended to highest widget for widget in ( name_input, icon_btn From c23b55f714614671a1c002ad2c5280a07d413676 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 3 May 2022 16:09:29 +0200 Subject: [PATCH 20/26] changed default sizes of create dialog --- .../tools/publisher/widgets/create_dialog.py | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index bcf87dea6a..09b2b9cb30 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -283,7 +283,7 @@ class HelpButton(ClickableFrame): class CreateDialog(QtWidgets.QDialog): - default_size = (900, 500) + default_size = (1000, 560) def __init__( self, controller, asset_name=None, task_name=None, parent=None @@ -354,7 +354,6 @@ class CreateDialog(QtWidgets.QDialog): variant_hints_menu = QtWidgets.QMenu(variant_widget) variant_hints_group = QtWidgets.QActionGroup(variant_hints_menu) - # variant_hints_btn.setMenu(variant_hints_menu) variant_layout = QtWidgets.QHBoxLayout(variant_widget) variant_layout.setContentsMargins(0, 0, 0, 0) @@ -1093,6 +1092,21 @@ class CreateDialog(QtWidgets.QDialog): self.variant_input.setProperty("state", state) self.variant_input.style().polish(self.variant_input) + def _on_first_show(self): + center = self.rect().center() + + width, height = self.default_size + self.resize(width, height) + part = int(width / 7) + self._splitter_widget.setSizes( + [part * 2, part * 2, width - (part * 4)] + ) + + new_pos = self.mapToGlobal(center) + new_pos.setX(new_pos.x() - int(self.width() / 2)) + new_pos.setY(new_pos.y() - int(self.height() / 2)) + self.move(new_pos) + def moveEvent(self, event): super(CreateDialog, self).moveEvent(event) self._last_pos = self.pos() @@ -1101,13 +1115,7 @@ class CreateDialog(QtWidgets.QDialog): super(CreateDialog, self).showEvent(event) if self._first_show: self._first_show = False - width, height = self.default_size - self.resize(width, height) - - third_size = int(width / 3) - self._splitter_widget.setSizes( - [third_size, third_size, width - (2 * third_size)] - ) + self._on_first_show() if self._last_pos is not None: self.move(self._last_pos) From d82480b1ee9d8558fd4281649b1a4c8a363fa516 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 3 May 2022 16:17:30 +0200 Subject: [PATCH 21/26] fix variant value changes --- openpype/tools/publisher/widgets/widgets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 6c09128857..169da717f7 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -862,6 +862,8 @@ class VariantInputWidget(PlaceholderLineEdit): self._ignore_value_change = True + self._has_value_changed = False + self._origin_value = list(variants) self._current_value = list(variants) From 06a1e484d61274a6646e6b3667e76d3525f2497e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 3 May 2022 16:53:59 +0200 Subject: [PATCH 22/26] fix property changes --- openpype/tools/publisher/widgets/widgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 169da717f7..63dd0ad198 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -428,6 +428,7 @@ class AssetsField(BaseClickableFrame): self._set_state_property(state) def _set_state_property(self, state): + set_style_property(self, "state", state) set_style_property(self._name_input, "state", state) set_style_property(self._icon_btn, "state", state) From 50e19bbfc075dac637e1cddc775d7cb092f54c8c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 3 May 2022 17:20:42 +0200 Subject: [PATCH 23/26] create attributes use same grid layout logic --- openpype/tools/publisher/widgets/widgets.py | 30 ++++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 63dd0ad198..7096b9fb50 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -1272,7 +1272,11 @@ class CreatorAttrsWidget(QtWidgets.QWidget): ) content_widget = QtWidgets.QWidget(self._scroll_area) - content_layout = QtWidgets.QFormLayout(content_widget) + content_layout = QtWidgets.QGridLayout(content_widget) + content_layout.setColumnStretch(0, 0) + content_layout.setColumnStretch(1, 1) + + row = 0 for attr_def, attr_instances, values in result: widget = create_widget_for_attr_def(attr_def, content_widget) if attr_def.is_value_def: @@ -1283,10 +1287,28 @@ class CreatorAttrsWidget(QtWidgets.QWidget): else: widget.set_value(values, True) - label = attr_def.label or attr_def.key - content_layout.addRow(label, widget) - widget.value_changed.connect(self._input_value_changed) + expand_cols = 2 + if attr_def.is_value_def and attr_def.is_label_horizontal: + expand_cols = 1 + col_num = 2 - expand_cols + + label = attr_def.label or attr_def.key + if label: + label_widget = QtWidgets.QLabel(label, self) + content_layout.addWidget( + label_widget, row, 0, 1, expand_cols + ) + if not attr_def.is_label_horizontal: + row += 1 + + content_layout.addWidget( + widget, row, col_num, 1, expand_cols + ) + + row += 1 + + widget.value_changed.connect(self._input_value_changed) self._attr_def_id_to_instances[attr_def.id] = attr_instances self._attr_def_id_to_attr_def[attr_def.id] = attr_def From 8db65d56cc16bce55d6588a627ef5656c23a71fe Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 3 May 2022 17:31:51 +0200 Subject: [PATCH 24/26] hound fix --- openpype/tools/publisher/widgets/create_dialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index 09b2b9cb30..9e357f3a56 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -774,7 +774,7 @@ class CreateDialog(QtWidgets.QDialog): self._help_btn.set_pos_and_size( max(0, pos_x), max(0, pos_y), - width, height + width, height ) def _on_help_btn_resize(self, height): From 289e35d68d9c7d5bd6a95a07100f7e82fcc59201 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 3 May 2022 17:33:08 +0200 Subject: [PATCH 25/26] added stretches --- openpype/widgets/attribute_defs/widgets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/widgets/attribute_defs/widgets.py b/openpype/widgets/attribute_defs/widgets.py index 875b69acb4..b6493b80a8 100644 --- a/openpype/widgets/attribute_defs/widgets.py +++ b/openpype/widgets/attribute_defs/widgets.py @@ -91,6 +91,8 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget): layout.deleteLater() new_layout = QtWidgets.QGridLayout() + new_layout.setColumnStretch(0, 0) + new_layout.setColumnStretch(1, 1) self.setLayout(new_layout) def set_attr_defs(self, attr_defs): From 7ff23fd4294532d670adffe179c8ea893db9feda Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 10 May 2022 12:06:08 +0200 Subject: [PATCH 26/26] fix extension collection --- .../traypublisher/plugins/publish/collect_simple_instances.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_simple_instances.py b/openpype/hosts/traypublisher/plugins/publish/collect_simple_instances.py index 9facd90a48..b2be43c701 100644 --- a/openpype/hosts/traypublisher/plugins/publish/collect_simple_instances.py +++ b/openpype/hosts/traypublisher/plugins/publish/collect_simple_instances.py @@ -33,7 +33,8 @@ class CollectSettingsSimpleInstances(pyblish.api.InstancePlugin): instance.data["stagingDir"] = filepath_item["directory"] filenames = filepath_item["filenames"] - ext = os.path.splitext(filenames[0])[-1] + _, ext = os.path.splitext(filenames[0]) + ext = ext[1:] if len(filenames) == 1: filenames = filenames[0]