From 073a38726e3450409a9fd2bc7e6d789583c379e3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Sep 2022 15:00:49 +0200 Subject: [PATCH 01/72] footer widget is not part of subset widget --- openpype/tools/publisher/window.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 2a0e6e940a..3b504655d9 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -142,6 +142,9 @@ class PublisherWindow(QtWidgets.QDialog): subset_content_layout.addWidget(subset_attributes_wrap, 7) # Footer + footer_widget = QtWidgets.QWidget(self) + footer_bottom_widget = QtWidgets.QWidget(footer_widget) + comment_input = PlaceholderLineEdit(subset_frame) comment_input.setObjectName("PublishCommentInput") comment_input.setPlaceholderText( @@ -153,13 +156,17 @@ class PublisherWindow(QtWidgets.QDialog): validate_btn = ValidateBtn(subset_frame) publish_btn = PublishBtn(subset_frame) - footer_layout = QtWidgets.QHBoxLayout() - footer_layout.setContentsMargins(0, 0, 0, 0) - footer_layout.addWidget(comment_input, 1) - footer_layout.addWidget(reset_btn, 0) - footer_layout.addWidget(stop_btn, 0) - footer_layout.addWidget(validate_btn, 0) - footer_layout.addWidget(publish_btn, 0) + footer_bottom_layout = QtWidgets.QHBoxLayout(footer_bottom_widget) + footer_bottom_layout.setContentsMargins(0, 0, 0, 0) + footer_bottom_layout.addStretch(1) + footer_bottom_layout.addWidget(reset_btn, 0) + footer_bottom_layout.addWidget(stop_btn, 0) + footer_bottom_layout.addWidget(validate_btn, 0) + footer_bottom_layout.addWidget(publish_btn, 0) + + footer_layout = QtWidgets.QVBoxLayout(footer_widget) + footer_layout.addWidget(comment_input, 0) + footer_layout.addWidget(footer_bottom_widget, 0) # Subset frame layout subset_layout = QtWidgets.QVBoxLayout(subset_frame) @@ -167,10 +174,9 @@ class PublisherWindow(QtWidgets.QDialog): marings.setLeft(marings.left() * 2) marings.setRight(marings.right() * 2) marings.setTop(marings.top() * 2) - marings.setBottom(marings.bottom() * 2) + marings.setBottom(0) subset_layout.setContentsMargins(marings) subset_layout.addWidget(subset_content_widget, 1) - subset_layout.addLayout(footer_layout, 0) # Create publish frame publish_frame = PublishFrame(controller, content_stacked_widget) @@ -192,6 +198,7 @@ class PublisherWindow(QtWidgets.QDialog): main_layout.addWidget(header_widget, 0) main_layout.addWidget(line_widget, 0) main_layout.addWidget(content_stacked_widget, 1) + main_layout.addWidget(footer_widget, 0) creator_window = CreateDialog(controller, parent=self) From 46ea4561f31cc96537e3820c858385baa7e1b30b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Sep 2022 15:49:31 +0200 Subject: [PATCH 02/72] implemented tabs widget --- openpype/style/data.json | 1 + openpype/style/style.css | 23 +++++ openpype/tools/publisher/widgets/__init__.py | 6 ++ .../tools/publisher/widgets/tabs_widget.py | 83 +++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 openpype/tools/publisher/widgets/tabs_widget.py diff --git a/openpype/style/data.json b/openpype/style/data.json index adda49de23..b75aa98508 100644 --- a/openpype/style/data.json +++ b/openpype/style/data.json @@ -91,6 +91,7 @@ "error": "#AA5050", "success": "#458056", "warning": "#ffc671", + "tab-bg": "#16191d", "list-view-group": { "bg": "#434a56", "bg-hover": "rgba(168, 175, 189, 0.3)", diff --git a/openpype/style/style.css b/openpype/style/style.css index 72d12a9230..ab23dd621f 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -856,6 +856,29 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { } /* New Create/Publish UI */ +PublisherTabsWidget { + background: {color:publisher:tab-bg}; +} + +PublisherTabBtn { + border-radius: 0px; + background: {color:bg-inputs}; + font-size: 9pt; + font-weight: regular; + padding: 0.5em 1em 0.5em 1em; +} + +PublisherTabBtn:hover { + background: {color:bg-buttons}; +} + +PublisherTabBtn[active="1"] { + background: {color:bg}; +} +PublisherTabBtn[active="1"]:hover { + background: {color:bg}; +} + #CreatorDetailedDescription { padding-left: 5px; padding-right: 5px; diff --git a/openpype/tools/publisher/widgets/__init__.py b/openpype/tools/publisher/widgets/__init__.py index 55afc349ff..a09e1353ec 100644 --- a/openpype/tools/publisher/widgets/__init__.py +++ b/openpype/tools/publisher/widgets/__init__.py @@ -33,6 +33,10 @@ from .list_view_widgets import ( InstanceListView ) +from .tabs_widget import ( + PublisherTabsWidget +) + __all__ = ( "get_icon_path", @@ -57,4 +61,6 @@ __all__ = ( "InstanceCardView", "InstanceListView", + + "PublisherTabsWidget", ) diff --git a/openpype/tools/publisher/widgets/tabs_widget.py b/openpype/tools/publisher/widgets/tabs_widget.py new file mode 100644 index 0000000000..0e92a6fd8d --- /dev/null +++ b/openpype/tools/publisher/widgets/tabs_widget.py @@ -0,0 +1,83 @@ +from Qt import QtWidgets, QtCore +from openpype.tools.utils import set_style_property + + +class PublisherTabBtn(QtWidgets.QPushButton): + tab_clicked = QtCore.Signal(str) + + def __init__(self, identifier, label, parent): + super(PublisherTabBtn, self).__init__(label, parent) + self._identifier = identifier + self._active = False + + self.clicked.connect(self._on_click) + + def _on_click(self): + self.tab_clicked.emit(self.identifier) + + @property + def identifier(self): + return self._identifier + + def activate(self): + if self._active: + return + self._active = True + set_style_property(self, "active", "1") + + def deactivate(self): + if not self._active: + return + self._active = False + set_style_property(self, "active", "") + + +class PublisherTabsWidget(QtWidgets.QFrame): + tab_changed = QtCore.Signal(str, str) + + def __init__(self, parent=None): + super(PublisherTabsWidget, self).__init__(parent) + + btns_widget = QtWidgets.QWidget(self) + btns_layout = QtWidgets.QHBoxLayout(btns_widget) + btns_layout.setContentsMargins(0, 0, 0, 0) + btns_layout.setSpacing(0) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(btns_widget, 0) + layout.addStretch(1) + + self._btns_layout = btns_layout + + self._current_button = None + self._buttons_by_identifier = {} + + def add_tab(self, label, identifier): + button = PublisherTabBtn(identifier, label, self) + button.tab_clicked.connect(self._on_tab_click) + self._btns_layout.addWidget(button, 0) + self._buttons_by_identifier[identifier] = button + + if self._current_button is None: + self.set_current_tab(identifier) + + def set_current_tab(self, identifier): + if identifier == self._current_button: + return + + new_btn = self._buttons_by_identifier.get(identifier) + if new_btn is None: + return + + old_identifier = self._current_button + old_btn = self._buttons_by_identifier.get(old_identifier) + self._current_button = identifier + + if old_btn is not None: + old_btn.deactivate() + new_btn.activate() + self.tab_changed.emit(old_identifier, identifier) + + def _on_tab_click(self, identifier): + self.set_current_tab(identifier) From ae62357d9873fec68e55fbfc545c8b9a61409667 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Sep 2022 16:00:22 +0200 Subject: [PATCH 03/72] added tab to main window --- openpype/tools/publisher/window.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 3b504655d9..d02fe704ee 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -15,6 +15,8 @@ from .widgets import ( SubsetAttributesWidget, InstanceCardView, InstanceListView, + PublisherTabsWidget, + CreateDialog, StopBtn, @@ -78,9 +80,11 @@ class PublisherWindow(QtWidgets.QDialog): header_layout.addWidget(icon_label, 0) header_layout.addWidget(context_label, 1) - line_widget = QtWidgets.QWidget(self) - line_widget.setObjectName("Separator") - line_widget.setMinimumHeight(2) + tabs_widget = PublisherTabsWidget(self) + tabs_widget.add_tab("Create", "create") + tabs_widget.add_tab("Publish", "publish") + tabs_widget.add_tab("Report", "report") + tabs_widget.add_tab("Details", "details") # Content content_stacked_widget = QtWidgets.QWidget(self) @@ -196,12 +200,14 @@ class PublisherWindow(QtWidgets.QDialog): main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) main_layout.addWidget(header_widget, 0) - main_layout.addWidget(line_widget, 0) + main_layout.addWidget(tabs_widget, 0) main_layout.addWidget(content_stacked_widget, 1) main_layout.addWidget(footer_widget, 0) creator_window = CreateDialog(controller, parent=self) + tabs_widget.tab_changed.connect(self._on_tab_change) + create_btn.clicked.connect(self._on_create_clicked) delete_btn.clicked.connect(self._on_delete_clicked) change_view_btn.clicked.connect(self._on_change_view_clicked) @@ -318,6 +324,9 @@ class PublisherWindow(QtWidgets.QDialog): self._on_subset_change() + def _on_tab_change(self, prev_tab, new_tab): + print(prev_tab, new_tab) + def _on_create_clicked(self): self.creator_window.show() From 7a0ce610b1472332719ec19e3ca98badbf8ab8c6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Sep 2022 16:41:12 +0200 Subject: [PATCH 04/72] moved overview widget from window --- openpype/tools/publisher/widgets/__init__.py | 43 +-- .../publisher/widgets/overview_widget.py | 246 +++++++++++++ openpype/tools/publisher/window.py | 340 ++++-------------- openpype/tools/traypublisher/window.py | 2 +- 4 files changed, 328 insertions(+), 303 deletions(-) create mode 100644 openpype/tools/publisher/widgets/overview_widget.py diff --git a/openpype/tools/publisher/widgets/__init__.py b/openpype/tools/publisher/widgets/__init__.py index a09e1353ec..869f7adf9b 100644 --- a/openpype/tools/publisher/widgets/__init__.py +++ b/openpype/tools/publisher/widgets/__init__.py @@ -3,64 +3,31 @@ from .icons import ( get_pixmap, get_icon ) -from .border_label_widget import ( - BorderedLabelWidget -) from .widgets import ( - SubsetAttributesWidget, - StopBtn, ResetBtn, ValidateBtn, PublishBtn, - - CreateInstanceBtn, - RemoveInstanceBtn, - ChangeViewBtn ) -from .publish_widget import ( - PublishFrame -) -from .create_dialog import ( - CreateDialog -) - -from .card_view_widgets import ( - InstanceCardView -) - -from .list_view_widgets import ( - InstanceListView -) - -from .tabs_widget import ( - PublisherTabsWidget -) - +from .publish_widget import PublishFrame +from .create_dialog import CreateDialog +from .tabs_widget import PublisherTabsWidget +from .overview_widget import CreateOverviewWidget __all__ = ( "get_icon_path", "get_pixmap", "get_icon", - "SubsetAttributesWidget", - "BorderedLabelWidget", - "StopBtn", "ResetBtn", "ValidateBtn", "PublishBtn", - "CreateInstanceBtn", - "RemoveInstanceBtn", - "ChangeViewBtn", - "PublishFrame", "CreateDialog", - "InstanceCardView", - "InstanceListView", - "PublisherTabsWidget", + "CreateOverviewWidget", ) diff --git a/openpype/tools/publisher/widgets/overview_widget.py b/openpype/tools/publisher/widgets/overview_widget.py new file mode 100644 index 0000000000..abdd98ff7c --- /dev/null +++ b/openpype/tools/publisher/widgets/overview_widget.py @@ -0,0 +1,246 @@ +from Qt import QtWidgets, QtCore + +from .border_label_widget import BorderedLabelWidget + +from .card_view_widgets import InstanceCardView +from .list_view_widgets import InstanceListView +from .widgets import ( + SubsetAttributesWidget, + CreateInstanceBtn, + RemoveInstanceBtn, + ChangeViewBtn +) + + +class CreateOverviewWidget(QtWidgets.QFrame): + active_changed = QtCore.Signal() + instance_context_changed = QtCore.Signal() + create_requested = QtCore.Signal() + + def __init__(self, controller, parent): + super(CreateOverviewWidget, self).__init__(parent) + + self._controller = controller + self._refreshing_instances = False + + subset_views_widget = BorderedLabelWidget( + "Subsets to publish", self + ) + + subset_view_cards = InstanceCardView(controller, subset_views_widget) + subset_list_view = InstanceListView(controller, subset_views_widget) + + subset_views_layout = QtWidgets.QStackedLayout() + subset_views_layout.addWidget(subset_view_cards) + subset_views_layout.addWidget(subset_list_view) + + # Buttons at the bottom of subset view + create_btn = CreateInstanceBtn(self) + delete_btn = RemoveInstanceBtn(self) + change_view_btn = ChangeViewBtn(self) + + # Subset details widget + subset_attributes_wrap = BorderedLabelWidget( + "Publish options", self + ) + subset_attributes_widget = SubsetAttributesWidget( + controller, subset_attributes_wrap + ) + subset_attributes_wrap.set_center_widget(subset_attributes_widget) + + # Layout of buttons at the bottom of subset view + subset_view_btns_layout = QtWidgets.QHBoxLayout() + subset_view_btns_layout.setContentsMargins(0, 5, 0, 0) + subset_view_btns_layout.addWidget(create_btn) + subset_view_btns_layout.addSpacing(5) + subset_view_btns_layout.addWidget(delete_btn) + subset_view_btns_layout.addStretch(1) + subset_view_btns_layout.addWidget(change_view_btn) + + # Layout of view and buttons + # - widget 'subset_view_widget' is necessary + # - only layout won't be resized automatically to minimum size hint + # on child resize request! + subset_view_widget = QtWidgets.QWidget(subset_views_widget) + subset_view_layout = QtWidgets.QVBoxLayout(subset_view_widget) + subset_view_layout.setContentsMargins(0, 0, 0, 0) + subset_view_layout.addLayout(subset_views_layout, 1) + subset_view_layout.addLayout(subset_view_btns_layout, 0) + + subset_views_widget.set_center_widget(subset_view_widget) + + # Whole subset layout with attributes and details + subset_content_widget = QtWidgets.QWidget(self) + subset_content_layout = QtWidgets.QHBoxLayout(subset_content_widget) + subset_content_layout.setContentsMargins(0, 0, 0, 0) + subset_content_layout.addWidget(subset_views_widget, 3) + subset_content_layout.addWidget(subset_attributes_wrap, 7) + + # Subset frame layout + main_layout = QtWidgets.QVBoxLayout(self) + marings = main_layout.contentsMargins() + marings.setLeft(marings.left() * 2) + marings.setRight(marings.right() * 2) + marings.setTop(marings.top() * 2) + marings.setBottom(0) + main_layout.setContentsMargins(marings) + main_layout.addWidget(subset_content_widget, 1) + + create_btn.clicked.connect(self._on_create_clicked) + delete_btn.clicked.connect(self._on_delete_clicked) + change_view_btn.clicked.connect(self._on_change_view_clicked) + + # Selection changed + subset_list_view.selection_changed.connect( + self._on_subset_change + ) + subset_view_cards.selection_changed.connect( + self._on_subset_change + ) + # Active instances changed + subset_list_view.active_changed.connect( + self._on_active_changed + ) + subset_view_cards.active_changed.connect( + self._on_active_changed + ) + # Instance context has changed + subset_attributes_widget.instance_context_changed.connect( + self._on_instance_context_change + ) + + controller.add_publish_reset_callback(self._on_publish_reset) + controller.add_instances_refresh_callback(self._on_instances_refresh) + + self.subset_content_widget = subset_content_widget + + self.subset_view_cards = subset_view_cards + self.subset_list_view = subset_list_view + self.subset_views_layout = subset_views_layout + + self.delete_btn = delete_btn + + self.subset_attributes_widget = subset_attributes_widget + + def _on_create_clicked(self): + """Pass signal to parent widget which should care about changing state. + + We don't change anything here until the parent will care about it. + """ + + self.create_requested.emit() + + def _on_delete_clicked(self): + instances, _ = self.get_selected_items() + + # Ask user if he really wants to remove instances + dialog = QtWidgets.QMessageBox(self) + dialog.setIcon(QtWidgets.QMessageBox.Question) + dialog.setWindowTitle("Are you sure?") + if len(instances) > 1: + msg = ( + "Do you really want to remove {} instances?" + ).format(len(instances)) + else: + msg = ( + "Do you really want to remove the instance?" + ) + dialog.setText(msg) + dialog.setStandardButtons( + QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel + ) + dialog.setDefaultButton(QtWidgets.QMessageBox.Ok) + dialog.setEscapeButton(QtWidgets.QMessageBox.Cancel) + dialog.exec_() + # Skip if OK was not clicked + if dialog.result() == QtWidgets.QMessageBox.Ok: + self._controller.remove_instances(instances) + + def _on_change_view_clicked(self): + self._change_view_type() + + def _on_subset_change(self, *_args): + # Ignore changes if in middle of refreshing + if self._refreshing_instances: + return + + instances, context_selected = self.get_selected_items() + + # Disable delete button if nothing is selected + self.delete_btn.setEnabled(len(instances) > 0) + + self.subset_attributes_widget.set_current_instances( + instances, context_selected + ) + + def _on_active_changed(self): + if self._refreshing_instances: + return + self.active_changed.emit() + + def _on_instance_context_change(self): + current_idx = self.subset_views_layout.currentIndex() + for idx in range(self.subset_views_layout.count()): + if idx == current_idx: + continue + widget = self.subset_views_layout.widget(idx) + if widget.refreshed: + widget.set_refreshed(False) + + current_widget = self.subset_views_layout.widget(current_idx) + current_widget.refresh_instance_states() + + self.instance_context_changed.emit() + + def get_selected_items(self): + view = self.subset_views_layout.currentWidget() + return view.get_selected_items() + + def _change_view_type(self): + idx = self.subset_views_layout.currentIndex() + new_idx = (idx + 1) % self.subset_views_layout.count() + self.subset_views_layout.setCurrentIndex(new_idx) + + new_view = self.subset_views_layout.currentWidget() + if not new_view.refreshed: + new_view.refresh() + new_view.set_refreshed(True) + else: + new_view.refresh_instance_states() + + self._on_subset_change() + + def _refresh_instances(self): + if self._refreshing_instances: + return + + self._refreshing_instances = True + + for idx in range(self.subset_views_layout.count()): + widget = self.subset_views_layout.widget(idx) + widget.set_refreshed(False) + + view = self.subset_views_layout.currentWidget() + view.refresh() + view.set_refreshed(True) + + self._refreshing_instances = False + + # Force to change instance and refresh details + self._on_subset_change() + + def _on_publish_reset(self): + """Context in controller has been refreshed.""" + + self.subset_content_widget.setEnabled(self._controller.host_is_valid) + + def _on_instances_refresh(self): + """Controller refreshed instances.""" + + self._refresh_instances() + + # Give a change to process Resize Request + QtWidgets.QApplication.processEvents() + # Trigger update geometry of + widget = self.subset_views_layout.currentWidget() + widget.updateGeometry() diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index d02fe704ee..8df9f9bbf5 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -10,11 +10,9 @@ from openpype.tools.utils import ( ) from .control import PublisherController from .widgets import ( - BorderedLabelWidget, + CreateOverviewWidget, PublishFrame, - SubsetAttributesWidget, - InstanceCardView, - InstanceListView, + PublisherTabsWidget, CreateDialog, @@ -23,10 +21,6 @@ from .widgets import ( ResetBtn, ValidateBtn, PublishBtn, - - CreateInstanceBtn, - RemoveInstanceBtn, - ChangeViewBtn ) @@ -62,7 +56,6 @@ class PublisherWindow(QtWidgets.QDialog): self._reset_on_show = reset_on_show self._first_show = True - self._refreshing_instances = False controller = PublisherController() @@ -78,8 +71,10 @@ class PublisherWindow(QtWidgets.QDialog): header_layout.setContentsMargins(15, 15, 15, 15) header_layout.setSpacing(15) header_layout.addWidget(icon_label, 0) - header_layout.addWidget(context_label, 1) + header_layout.addWidget(context_label, 0) + header_layout.addStretch(1) + # Tabs widget under header tabs_widget = PublisherTabsWidget(self) tabs_widget.add_tab("Create", "create") tabs_widget.add_tab("Publish", "publish") @@ -89,76 +84,24 @@ class PublisherWindow(QtWidgets.QDialog): # Content content_stacked_widget = QtWidgets.QWidget(self) - # Subset widget - subset_frame = QtWidgets.QFrame(content_stacked_widget) - - subset_views_widget = BorderedLabelWidget( - "Subsets to publish", subset_frame + create_overview_widget = CreateOverviewWidget( + controller, content_stacked_widget ) - subset_view_cards = InstanceCardView(controller, subset_views_widget) - subset_list_view = InstanceListView(controller, subset_views_widget) - - subset_views_layout = QtWidgets.QStackedLayout() - subset_views_layout.addWidget(subset_view_cards) - subset_views_layout.addWidget(subset_list_view) - - # Buttons at the bottom of subset view - create_btn = CreateInstanceBtn(subset_frame) - delete_btn = RemoveInstanceBtn(subset_frame) - change_view_btn = ChangeViewBtn(subset_frame) - - # Subset details widget - subset_attributes_wrap = BorderedLabelWidget( - "Publish options", subset_frame - ) - subset_attributes_widget = SubsetAttributesWidget( - controller, subset_attributes_wrap - ) - subset_attributes_wrap.set_center_widget(subset_attributes_widget) - - # Layout of buttons at the bottom of subset view - subset_view_btns_layout = QtWidgets.QHBoxLayout() - subset_view_btns_layout.setContentsMargins(0, 5, 0, 0) - subset_view_btns_layout.addWidget(create_btn) - subset_view_btns_layout.addSpacing(5) - subset_view_btns_layout.addWidget(delete_btn) - subset_view_btns_layout.addStretch(1) - subset_view_btns_layout.addWidget(change_view_btn) - - # Layout of view and buttons - # - widget 'subset_view_widget' is necessary - # - only layout won't be resized automatically to minimum size hint - # on child resize request! - subset_view_widget = QtWidgets.QWidget(subset_views_widget) - subset_view_layout = QtWidgets.QVBoxLayout(subset_view_widget) - subset_view_layout.setContentsMargins(0, 0, 0, 0) - subset_view_layout.addLayout(subset_views_layout, 1) - subset_view_layout.addLayout(subset_view_btns_layout, 0) - - subset_views_widget.set_center_widget(subset_view_widget) - - # Whole subset layout with attributes and details - subset_content_widget = QtWidgets.QWidget(subset_frame) - subset_content_layout = QtWidgets.QHBoxLayout(subset_content_widget) - subset_content_layout.setContentsMargins(0, 0, 0, 0) - subset_content_layout.addWidget(subset_views_widget, 3) - subset_content_layout.addWidget(subset_attributes_wrap, 7) - # Footer footer_widget = QtWidgets.QWidget(self) footer_bottom_widget = QtWidgets.QWidget(footer_widget) - comment_input = PlaceholderLineEdit(subset_frame) + comment_input = PlaceholderLineEdit(footer_widget) comment_input.setObjectName("PublishCommentInput") comment_input.setPlaceholderText( "Attach a comment to your publish" ) - reset_btn = ResetBtn(subset_frame) - stop_btn = StopBtn(subset_frame) - validate_btn = ValidateBtn(subset_frame) - publish_btn = PublishBtn(subset_frame) + reset_btn = ResetBtn(footer_widget) + stop_btn = StopBtn(footer_widget) + validate_btn = ValidateBtn(footer_widget) + publish_btn = PublishBtn(footer_widget) footer_bottom_layout = QtWidgets.QHBoxLayout(footer_bottom_widget) footer_bottom_layout.setContentsMargins(0, 0, 0, 0) @@ -172,16 +115,6 @@ class PublisherWindow(QtWidgets.QDialog): footer_layout.addWidget(comment_input, 0) footer_layout.addWidget(footer_bottom_widget, 0) - # Subset frame layout - subset_layout = QtWidgets.QVBoxLayout(subset_frame) - marings = subset_layout.contentsMargins() - marings.setLeft(marings.left() * 2) - marings.setRight(marings.right() * 2) - marings.setTop(marings.top() * 2) - marings.setBottom(0) - subset_layout.setContentsMargins(marings) - subset_layout.addWidget(subset_content_widget, 1) - # Create publish frame publish_frame = PublishFrame(controller, content_stacked_widget) @@ -192,7 +125,7 @@ class PublisherWindow(QtWidgets.QDialog): content_stacked_layout.setStackingMode( QtWidgets.QStackedLayout.StackAll ) - content_stacked_layout.addWidget(subset_frame) + content_stacked_layout.addWidget(create_overview_widget) content_stacked_layout.addWidget(publish_frame) # Add main frame to this window @@ -207,37 +140,22 @@ class PublisherWindow(QtWidgets.QDialog): creator_window = CreateDialog(controller, parent=self) tabs_widget.tab_changed.connect(self._on_tab_change) - - create_btn.clicked.connect(self._on_create_clicked) - delete_btn.clicked.connect(self._on_delete_clicked) - change_view_btn.clicked.connect(self._on_change_view_clicked) + create_overview_widget.active_changed.connect( + self._on_context_or_active_change + ) + create_overview_widget.instance_context_changed.connect( + self._on_context_or_active_change + ) + create_overview_widget.create_requested.connect( + self._on_create_request + ) reset_btn.clicked.connect(self._on_reset_clicked) stop_btn.clicked.connect(self._on_stop_clicked) validate_btn.clicked.connect(self._on_validate_clicked) publish_btn.clicked.connect(self._on_publish_clicked) - # Selection changed - subset_list_view.selection_changed.connect( - self._on_subset_change - ) - subset_view_cards.selection_changed.connect( - self._on_subset_change - ) - # Active instances changed - subset_list_view.active_changed.connect( - self._on_active_changed - ) - subset_view_cards.active_changed.connect( - self._on_active_changed - ) - # Instance context has changed - subset_attributes_widget.instance_context_changed.connect( - self._on_instance_context_change - ) - controller.add_instances_refresh_callback(self._on_instances_refresh) - controller.add_publish_reset_callback(self._on_publish_reset) controller.add_publish_started_callback(self._on_publish_start) controller.add_publish_validated_callback(self._on_publish_validated) @@ -246,22 +164,15 @@ class PublisherWindow(QtWidgets.QDialog): # Store header for TrayPublisher self._header_layout = header_layout + self._tabs_widget = tabs_widget + self._content_stacked_widget = content_stacked_widget self.content_stacked_layout = content_stacked_layout + self._create_overview_widget = create_overview_widget self.publish_frame = publish_frame - self.subset_frame = subset_frame - self.subset_content_widget = subset_content_widget self.context_label = context_label - self.subset_view_cards = subset_view_cards - self.subset_list_view = subset_list_view - self.subset_views_layout = subset_views_layout - - self.delete_btn = delete_btn - - self.subset_attributes_widget = subset_attributes_widget - self.comment_input = comment_input self.stop_btn = stop_btn @@ -269,10 +180,14 @@ class PublisherWindow(QtWidgets.QDialog): self.validate_btn = validate_btn self.publish_btn = publish_btn - self.controller = controller + self._controller = controller self.creator_window = creator_window + @property + def controller(self): + return self._controller + def showEvent(self, event): super(PublisherWindow, self).showEvent(event) if self._first_show: @@ -283,88 +198,33 @@ class PublisherWindow(QtWidgets.QDialog): self.reset() def closeEvent(self, event): - self.controller.save_changes() + self._controller.save_changes() super(PublisherWindow, self).closeEvent(event) def reset(self): - self.controller.reset() + self._controller.reset() def set_context_label(self, label): self.context_label.setText(label) - def get_selected_items(self): - view = self.subset_views_layout.currentWidget() - return view.get_selected_items() - - def _on_instance_context_change(self): - current_idx = self.subset_views_layout.currentIndex() - for idx in range(self.subset_views_layout.count()): - if idx == current_idx: - continue - widget = self.subset_views_layout.widget(idx) - if widget.refreshed: - widget.set_refreshed(False) - - current_widget = self.subset_views_layout.widget(current_idx) - current_widget.refresh_instance_states() - - self._validate_create_instances() - - def _change_view_type(self): - idx = self.subset_views_layout.currentIndex() - new_idx = (idx + 1) % self.subset_views_layout.count() - self.subset_views_layout.setCurrentIndex(new_idx) - - new_view = self.subset_views_layout.currentWidget() - if not new_view.refreshed: - new_view.refresh() - new_view.set_refreshed(True) - else: - new_view.refresh_instance_states() - - self._on_subset_change() - def _on_tab_change(self, prev_tab, new_tab): print(prev_tab, new_tab) - def _on_create_clicked(self): - self.creator_window.show() + def _on_context_or_active_change(self): + self._validate_create_instances() - def _on_delete_clicked(self): - instances, _ = self.get_selected_items() + def _on_create_request(self): + self._go_to_create_tab() - # Ask user if he really wants to remove instances - dialog = QtWidgets.QMessageBox(self) - dialog.setIcon(QtWidgets.QMessageBox.Question) - dialog.setWindowTitle("Are you sure?") - if len(instances) > 1: - msg = ( - "Do you really want to remove {} instances?" - ).format(len(instances)) - else: - msg = ( - "Do you really want to remove the instance?" - ) - dialog.setText(msg) - dialog.setStandardButtons( - QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel - ) - dialog.setDefaultButton(QtWidgets.QMessageBox.Ok) - dialog.setEscapeButton(QtWidgets.QMessageBox.Cancel) - dialog.exec_() - # Skip if OK was not clicked - if dialog.result() == QtWidgets.QMessageBox.Ok: - self.controller.remove_instances(instances) - - def _on_change_view_clicked(self): - self._change_view_type() + def _go_to_create_tab(self): + self._tabs_widget.set_current_tab("create") def _set_publish_visibility(self, visible): if visible: widget = self.publish_frame publish_frame_visible = True else: - widget = self.subset_frame + widget = self._create_overview_widget publish_frame_visible = False self.content_stacked_layout.setCurrentWidget(widget) self._set_publish_frame_visible(publish_frame_visible) @@ -381,79 +241,27 @@ class PublisherWindow(QtWidgets.QDialog): self.creator_window.close() def _on_reset_clicked(self): - self.controller.reset() + self._controller.reset() def _on_stop_clicked(self): - self.controller.stop_publish() + self._controller.stop_publish() def _set_publish_comment(self): - if self.controller.publish_comment_is_set: + if self._controller.publish_comment_is_set: return comment = self.comment_input.text() - self.controller.set_comment(comment) + self._controller.set_comment(comment) def _on_validate_clicked(self): self._set_publish_comment() self._set_publish_visibility(True) - self.controller.validate() + self._controller.validate() def _on_publish_clicked(self): self._set_publish_comment() self._set_publish_visibility(True) - self.controller.publish() - - def _refresh_instances(self): - if self._refreshing_instances: - return - - self._refreshing_instances = True - - for idx in range(self.subset_views_layout.count()): - widget = self.subset_views_layout.widget(idx) - widget.set_refreshed(False) - - view = self.subset_views_layout.currentWidget() - view.refresh() - view.set_refreshed(True) - - self._refreshing_instances = False - - # Force to change instance and refresh details - self._on_subset_change() - - def _on_instances_refresh(self): - self._refresh_instances() - - self._validate_create_instances() - - context_title = self.controller.get_context_title() - self.set_context_label(context_title) - - # Give a change to process Resize Request - QtWidgets.QApplication.processEvents() - # Trigger update geometry of - widget = self.subset_views_layout.currentWidget() - widget.updateGeometry() - - def _on_subset_change(self, *_args): - # Ignore changes if in middle of refreshing - if self._refreshing_instances: - return - - instances, context_selected = self.get_selected_items() - - # Disable delete button if nothing is selected - self.delete_btn.setEnabled(len(instances) > 0) - - self.subset_attributes_widget.set_current_instances( - instances, context_selected - ) - - def _on_active_changed(self): - if self._refreshing_instances: - return - self._validate_create_instances() + self._controller.publish() def _set_footer_enabled(self, enabled): self.comment_input.setEnabled(enabled) @@ -467,30 +275,9 @@ class PublisherWindow(QtWidgets.QDialog): self.validate_btn.setEnabled(enabled) self.publish_btn.setEnabled(enabled) - def _validate_create_instances(self): - if not self.controller.host_is_valid: - self._set_footer_enabled(True) - return - - all_valid = None - for instance in self.controller.instances: - if not instance["active"]: - continue - - if not instance.has_valid_context: - all_valid = False - break - - if all_valid is None: - all_valid = True - - self._set_footer_enabled(bool(all_valid)) - def _on_publish_reset(self): self._set_publish_visibility(False) - self.subset_content_widget.setEnabled(self.controller.host_is_valid) - self._set_footer_enabled(False) def _on_publish_start(self): @@ -505,19 +292,44 @@ class PublisherWindow(QtWidgets.QDialog): def _on_publish_stop(self): self.reset_btn.setEnabled(True) self.stop_btn.setEnabled(False) - validate_enabled = not self.controller.publish_has_crashed - publish_enabled = not self.controller.publish_has_crashed + validate_enabled = not self._controller.publish_has_crashed + publish_enabled = not self._controller.publish_has_crashed if validate_enabled: - validate_enabled = not self.controller.publish_has_validated + validate_enabled = not self._controller.publish_has_validated if publish_enabled: if ( - self.controller.publish_has_validated - and self.controller.publish_has_validation_errors + self._controller.publish_has_validated + and self._controller.publish_has_validation_errors ): publish_enabled = False else: - publish_enabled = not self.controller.publish_has_finished + publish_enabled = not self._controller.publish_has_finished self.validate_btn.setEnabled(validate_enabled) self.publish_btn.setEnabled(publish_enabled) + + def _validate_create_instances(self): + if not self._controller.host_is_valid: + self._set_footer_enabled(True) + return + + all_valid = None + for instance in self._controller.instances: + if not instance["active"]: + continue + + if not instance.has_valid_context: + all_valid = False + break + + if all_valid is None: + all_valid = True + + self._set_footer_enabled(bool(all_valid)) + + def _on_instances_refresh(self): + self._validate_create_instances() + + context_title = self.controller.get_context_title() + self.set_context_label(context_title) diff --git a/openpype/tools/traypublisher/window.py b/openpype/tools/traypublisher/window.py index 930c27ca9c..128c0fef11 100644 --- a/openpype/tools/traypublisher/window.py +++ b/openpype/tools/traypublisher/window.py @@ -244,7 +244,7 @@ class TrayPublishWindow(PublisherWindow): self.reset() if not self.controller.instances: - self._on_create_clicked() + self._go_to_create_tab() def _on_tray_publish_save(self): self.controller.save_changes() From 05372493a3fcb21903ddc12a310ec258b6c9e20c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Sep 2022 17:45:35 +0200 Subject: [PATCH 05/72] added create widget to overview widget --- .../tools/publisher/widgets/assets_widget.py | 10 +- .../tools/publisher/widgets/create_widget.py | 986 ++++++++++++++++++ .../publisher/widgets/overview_widget.py | 74 +- .../tools/publisher/widgets/tasks_widget.py | 6 +- 4 files changed, 1045 insertions(+), 31 deletions(-) create mode 100644 openpype/tools/publisher/widgets/create_widget.py diff --git a/openpype/tools/publisher/widgets/assets_widget.py b/openpype/tools/publisher/widgets/assets_widget.py index 46fdcc6526..7a77c9e898 100644 --- a/openpype/tools/publisher/widgets/assets_widget.py +++ b/openpype/tools/publisher/widgets/assets_widget.py @@ -13,13 +13,13 @@ from openpype.tools.utils.assets_widget import ( ) -class CreateDialogAssetsWidget(SingleSelectAssetsWidget): +class CreateWidgetAssetsWidget(SingleSelectAssetsWidget): current_context_required = QtCore.Signal() header_height_changed = QtCore.Signal(int) def __init__(self, controller, parent): self._controller = controller - super(CreateDialogAssetsWidget, self).__init__(None, parent) + super(CreateWidgetAssetsWidget, self).__init__(None, parent) self.set_refresh_btn_visibility(False) self.set_current_asset_btn_visibility(False) @@ -42,11 +42,11 @@ class CreateDialogAssetsWidget(SingleSelectAssetsWidget): self.header_height_changed.emit(height) def resizeEvent(self, event): - super(CreateDialogAssetsWidget, self).resizeEvent(event) + super(CreateWidgetAssetsWidget, self).resizeEvent(event) self._check_header_height() def showEvent(self, event): - super(CreateDialogAssetsWidget, self).showEvent(event) + super(CreateWidgetAssetsWidget, self).showEvent(event) self._check_header_height() def _on_current_asset_click(self): @@ -63,7 +63,7 @@ class CreateDialogAssetsWidget(SingleSelectAssetsWidget): self.select_asset(self._last_selection) def _select_indexes(self, *args, **kwargs): - super(CreateDialogAssetsWidget, self)._select_indexes(*args, **kwargs) + super(CreateWidgetAssetsWidget, self)._select_indexes(*args, **kwargs) if self._enabled: return self._last_selection = self.get_selected_asset_id() diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py new file mode 100644 index 0000000000..a0b3db0409 --- /dev/null +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -0,0 +1,986 @@ +import sys +import re +import traceback +import copy + +import qtawesome +try: + import commonmark +except Exception: + commonmark = None +from Qt import QtWidgets, QtCore, QtGui + +from openpype.client import get_asset_by_name, get_subsets +from openpype.pipeline.create import ( + CreatorError, + SUBSET_NAME_ALLOWED_SYMBOLS, + TaskNotSetError, +) +from openpype.tools.utils import ( + ErrorMessageBox, + MessageOverlayObject, + ClickableFrame, +) + +from .widgets import IconValuePixmapLabel +from .assets_widget import CreateWidgetAssetsWidget +from .tasks_widget import CreateWidgetTasksWidget +from .precreate_widget import PreCreateWidget +from ..constants import ( + VARIANT_TOOLTIP, + CREATOR_IDENTIFIER_ROLE, + FAMILY_ROLE +) + +SEPARATORS = ("---separator---", "---") + + +class VariantInputsWidget(QtWidgets.QWidget): + resized = QtCore.Signal() + + def resizeEvent(self, event): + super(VariantInputsWidget, self).resizeEvent(event) + self.resized.emit() + + +class CreateErrorMessageBox(ErrorMessageBox): + def __init__( + self, + creator_label, + subset_name, + asset_name, + exc_msg, + formatted_traceback, + parent + ): + self._creator_label = creator_label + self._subset_name = subset_name + self._asset_name = asset_name + self._exc_msg = exc_msg + self._formatted_traceback = formatted_traceback + super(CreateErrorMessageBox, self).__init__("Creation failed", parent) + + def _create_top_widget(self, parent_widget): + label_widget = QtWidgets.QLabel(parent_widget) + label_widget.setText( + "Failed to create" + ) + return label_widget + + def _get_report_data(self): + report_message = ( + "{creator}: Failed to create Subset: \"{subset}\"" + " in Asset: \"{asset}\"" + "\n\nError: {message}" + ).format( + creator=self._creator_label, + subset=self._subset_name, + asset=self._asset_name, + message=self._exc_msg, + ) + if self._formatted_traceback: + report_message += "\n\n{}".format(self._formatted_traceback) + return [report_message] + + def _create_content(self, content_layout): + item_name_template = ( + "Creator: {}
" + "Subset: {}
" + "Asset: {}
" + ) + exc_msg_template = "{}" + + line = self._create_line() + content_layout.addWidget(line) + + item_name_widget = QtWidgets.QLabel(self) + item_name_widget.setText( + item_name_template.format( + self._creator_label, self._subset_name, self._asset_name + ) + ) + content_layout.addWidget(item_name_widget) + + message_label_widget = QtWidgets.QLabel(self) + message_label_widget.setText( + exc_msg_template.format(self.convert_text_for_html(self._exc_msg)) + ) + content_layout.addWidget(message_label_widget) + + if self._formatted_traceback: + line_widget = self._create_line() + tb_widget = self._create_traceback_widget( + self._formatted_traceback + ) + content_layout.addWidget(line_widget) + content_layout.addWidget(tb_widget) + + +# 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) + + # --- Short description widget --- + icon_widget = IconValuePixmapLabel(None, self) + icon_widget.setObjectName("FamilyIconLabel") + + # --- Short description inputs --- + short_desc_input_widget = QtWidgets.QWidget(self) + + family_label = QtWidgets.QLabel(short_desc_input_widget) + family_label.setAlignment( + QtCore.Qt.AlignBottom | QtCore.Qt.AlignLeft + ) + + description_label = QtWidgets.QLabel(short_desc_input_widget) + description_label.setAlignment( + QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft + ) + + short_desc_input_layout = QtWidgets.QVBoxLayout( + short_desc_input_widget + ) + short_desc_input_layout.setSpacing(0) + short_desc_input_layout.addWidget(family_label) + short_desc_input_layout.addWidget(description_label) + # -------------------------------- + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(icon_widget, 0) + layout.addWidget(short_desc_input_widget, 1) + # -------------------------------- + + self._icon_widget = icon_widget + 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) + self._family_label.setText("") + self._description_label.setText("") + return + + plugin_icon = plugin.get_icon() + description = plugin.get_description() or "" + + self._icon_widget.set_icon_def(plugin_icon) + self._family_label.setText("{}".format(plugin.family)) + self._family_label.setTextInteractionFlags(QtCore.Qt.NoTextInteraction) + self._description_label.setText(description) + + +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() + + def set_expanded(self, expanded=None): + if self._expanded is expanded: + if expanded is not None: + return + expanded = False + self._expanded = expanded + 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: + question_mark_icon = qtawesome.icon( + self.question_mark_icon_name, color=QtCore.Qt.white + ) + question_mark_pix = question_mark_icon.pixmap(width, width) + + 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 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.resized.emit(self.height()) + + def resizeEvent(self, event): + super(HelpButton, self).resizeEvent(event) + self.resized.emit(self.height()) + + +class CreateWidget(QtWidgets.QWidget): + def __init__(self, controller, parent=None): + super(CreateWidget, self).__init__(parent) + + self.setWindowTitle("Create new instance") + + self.controller = controller + + self._asset_name = self.dbcon.Session.get("AVALON_ASSET") + self._task_name = self.dbcon.Session.get("AVALON_TASK") + + self._asset_doc = None + self._subset_names = None + self._selected_creator = None + + self._prereq_available = False + + self._message_dialog = None + + name_pattern = "^[{}]*$".format(SUBSET_NAME_ALLOWED_SYMBOLS) + self._name_pattern = name_pattern + self._compiled_name_pattern = re.compile(name_pattern) + + overlay_object = MessageOverlayObject(self) + + context_widget = QtWidgets.QWidget(self) + + assets_widget = CreateWidgetAssetsWidget(controller, context_widget) + tasks_widget = CreateWidgetTasksWidget(controller, context_widget) + + context_layout = QtWidgets.QVBoxLayout(context_widget) + context_layout.setContentsMargins(0, 0, 0, 0) + context_layout.setSpacing(0) + context_layout.addWidget(assets_widget, 2) + 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_sort_model = QtCore.QSortFilterProxyModel() + creators_sort_model.setSourceModel(creators_model) + creators_view.setModel(creators_sort_model) + + variant_widget = VariantInputsWidget(self) + + variant_input = QtWidgets.QLineEdit(variant_widget) + variant_input.setObjectName("VariantInput") + variant_input.setToolTip(VARIANT_TOOLTIP) + + variant_hints_btn = QtWidgets.QToolButton(variant_widget) + variant_hints_btn.setArrowType(QtCore.Qt.DownArrow) + variant_hints_btn.setIconSize(QtCore.QSize(12, 12)) + + variant_hints_menu = QtWidgets.QMenu(variant_widget) + variant_hints_group = QtWidgets.QActionGroup(variant_hints_menu) + + variant_layout = QtWidgets.QHBoxLayout(variant_widget) + variant_layout.setContentsMargins(0, 0, 0, 0) + variant_layout.setSpacing(0) + variant_layout.addWidget(variant_input, 1) + variant_layout.addWidget(variant_hints_btn, 0, QtCore.Qt.AlignVCenter) + + subset_name_input = QtWidgets.QLineEdit(self) + subset_name_input.setEnabled(False) + + form_layout = QtWidgets.QFormLayout() + form_layout.addRow("Variant:", variant_widget) + form_layout.addRow("Subset:", subset_name_input) + + mid_widget = QtWidgets.QWidget(self) + mid_layout = QtWidgets.QVBoxLayout(mid_widget) + mid_layout.setContentsMargins(0, 0, 0, 0) + mid_layout.addWidget(creators_header_widget, 0) + mid_layout.addWidget(creators_view, 1) + mid_layout.addLayout(form_layout, 0) + # ------------ + + # --- Creator short info and attr defs --- + creator_attrs_widget = QtWidgets.QWidget(self) + + creator_short_desc_widget = CreatorShortDescWidget( + creator_attrs_widget + ) + + attr_separator_widget = QtWidgets.QWidget(self) + attr_separator_widget.setObjectName("Separator") + attr_separator_widget.setMinimumHeight(1) + attr_separator_widget.setMaximumHeight(1) + + # 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(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.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("CreatorDetailedDescription") + detail_description_input.setTextInteractionFlags( + QtCore.Qt.TextBrowserInteraction + ) + + 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) + splitter_widget.addWidget(creator_attrs_widget) + splitter_widget.addWidget(detail_description_widget) + splitter_widget.setStretchFactor(0, 1) + splitter_widget.setStretchFactor(1, 1) + splitter_widget.setStretchFactor(2, 1) + splitter_widget.setStretchFactor(3, 1) + + layout = QtWidgets.QHBoxLayout(self) + layout.addWidget(splitter_widget, 1) + + prereq_timer = QtCore.QTimer() + prereq_timer.setInterval(50) + prereq_timer.setSingleShot(True) + + prereq_timer.timeout.connect(self._invalidate_prereq) + + 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) + variant_input.textChanged.connect(self._on_variant_change) + creators_view.selectionModel().currentChanged.connect( + self._on_creator_item_change + ) + variant_hints_btn.clicked.connect(self._on_variant_btn_click) + variant_hints_menu.triggered.connect(self._on_variant_action) + assets_widget.selection_changed.connect(self._on_asset_change) + assets_widget.current_context_required.connect( + 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) + + self._overlay_object = overlay_object + + self._splitter_widget = splitter_widget + + self._context_widget = context_widget + self._assets_widget = assets_widget + self._tasks_widget = tasks_widget + + self.subset_name_input = subset_name_input + + self.variant_input = variant_input + self.variant_hints_btn = variant_hints_btn + 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_sort_model = creators_sort_model + self._creators_view = creators_view + self._create_btn = create_btn + + 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._prereq_timer = prereq_timer + self._first_show = True + + def _emit_message(self, message): + self._overlay_object.add_message(message) + + def _context_change_is_enabled(self): + return self._context_widget.isEnabled() + + def _get_asset_name(self): + asset_name = None + if self._context_change_is_enabled(): + asset_name = self._assets_widget.get_selected_asset_name() + + if asset_name is None: + asset_name = self._asset_name + return asset_name + + def _get_task_name(self): + task_name = None + if self._context_change_is_enabled(): + # Don't use selection of task if asset is not set + asset_name = self._assets_widget.get_selected_asset_name() + if asset_name: + task_name = self._tasks_widget.get_selected_task_name() + + if not task_name: + task_name = self._task_name + return task_name + + @property + def dbcon(self): + return self.controller.dbcon + + def _set_context_enabled(self, enabled): + self._assets_widget.set_enabled(enabled) + self._tasks_widget.set_enabled(enabled) + check_prereq = self._context_widget.isEnabled() != enabled + self._context_widget.setEnabled(enabled) + if check_prereq: + self._invalidate_prereq() + + def refresh(self): + # Get context before refresh to keep selection of asset and + # task widgets + asset_name = self._get_asset_name() + task_name = self._get_task_name() + + self._prereq_available = False + + # Disable context widget so refresh of asset will use context asset + # name + self._set_context_enabled(False) + + self._assets_widget.refresh() + + # Refresh data before update of creators + self._refresh_asset() + # Then refresh creators which may trigger callbacks using refreshed + # data + self._refresh_creators() + + self._assets_widget.set_current_asset_name(self._asset_name) + self._assets_widget.select_asset_by_name(asset_name) + self._tasks_widget.set_asset_name(asset_name) + self._tasks_widget.select_task_name(task_name) + + self._invalidate_prereq_deffered() + + def _invalidate_prereq_deffered(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 _invalidate_prereq(self): + prereq_available = True + creator_btn_tooltips = [] + + available_creators = self._creators_model.rowCount() > 0 + if available_creators != self._creators_view.isEnabled(): + self._creators_view.setEnabled(available_creators) + + if not available_creators: + prereq_available = False + creator_btn_tooltips.append("Creator is not selected") + + if self._context_change_is_enabled() and self._asset_doc is None: + # QUESTION how to handle invalid asset? + prereq_available = False + creator_btn_tooltips.append("Context is not selected") + + if prereq_available != self._prereq_available: + self._prereq_available = prereq_available + + self._create_btn.setEnabled(prereq_available) + + self.variant_input.setEnabled(prereq_available) + self.variant_hints_btn.setEnabled(prereq_available) + + tooltip = "" + if creator_btn_tooltips: + tooltip = "\n".join(creator_btn_tooltips) + self._create_btn.setToolTip(tooltip) + + self._on_variant_change() + + def _refresh_asset(self): + asset_name = self._get_asset_name() + + # Skip if asset did not change + if self._asset_doc and self._asset_doc["name"] == asset_name: + return + + # Make sure `_asset_doc` and `_subset_names` variables are reset + self._asset_doc = None + self._subset_names = None + if asset_name is None: + return + + project_name = self.dbcon.active_project() + asset_doc = get_asset_by_name(project_name, asset_name) + self._asset_doc = asset_doc + + if asset_doc: + asset_id = asset_doc["_id"] + subset_docs = get_subsets( + project_name, asset_ids=[asset_id], fields=["name"] + ) + self._subset_names = { + subset_doc["name"] + for subset_doc in subset_docs + } + + if not asset_doc: + self.subset_name_input.setText("< Asset is not set >") + + def _refresh_creators(self): + # Refresh creators and add their families to list + existing_items = {} + old_creators = set() + for row in range(self._creators_model.rowCount()): + item = self._creators_model.item(row, 0) + identifier = item.data(CREATOR_IDENTIFIER_ROLE) + existing_items[identifier] = item + old_creators.add(identifier) + + # Add new families + new_creators = set() + for identifier, creator in self.controller.manual_creators.items(): + # TODO add details about creator + new_creators.add(identifier) + if identifier in existing_items: + item = existing_items[identifier] + else: + item = QtGui.QStandardItem() + item.setFlags( + QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable + ) + self._creators_model.appendRow(item) + + label = creator.label or identifier + item.setData(label, QtCore.Qt.DisplayRole) + item.setData(identifier, CREATOR_IDENTIFIER_ROLE) + item.setData(creator.family, FAMILY_ROLE) + + # Remove families that are no more available + for identifier in (old_creators - new_creators): + item = existing_items[identifier] + self._creators_model.takeRow(item.row()) + + if self._creators_model.rowCount() < 1: + return + + self._creators_sort_model.sort(0) + # Make sure there is a selection + indexes = self._creators_view.selectedIndexes() + if not indexes: + index = self._creators_sort_model.index(0, 0) + self._creators_view.setCurrentIndex(index) + else: + index = indexes[0] + + identifier = index.data(CREATOR_IDENTIFIER_ROLE) + + self._set_creator_by_identifier(identifier) + + def _on_plugins_refresh(self): + # Trigger refresh only if is visible + self.refresh() + + def _on_asset_change(self): + self._refresh_asset() + + asset_name = self._assets_widget.get_selected_asset_name() + self._tasks_widget.set_asset_name(asset_name) + if self._context_change_is_enabled(): + self._invalidate_prereq_deffered() + + def _on_task_change(self): + if self._context_change_is_enabled(): + self._invalidate_prereq_deffered() + + def _on_current_session_context_request(self): + self._assets_widget.set_current_session_asset() + 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(): + identifier = new_index.data(CREATOR_IDENTIFIER_ROLE) + self._set_creator_by_identifier(identifier) + + def _set_creator_detailed_text(self, creator): + if not creator: + self._detail_description_input.setPlainText("") + return + detailed_description = creator.get_detail_description() or "" + if commonmark: + html = commonmark.commonmark(detailed_description) + self._detail_description_input.setHtml(html) + else: + self._detail_description_input.setMarkdown(detailed_description) + + def _set_creator_by_identifier(self, identifier): + creator = self.controller.manual_creators.get(identifier) + self._set_creator(creator) + + def _set_creator(self, creator): + self._creator_short_desc_widget.set_plugin(creator) + self._set_creator_detailed_text(creator) + self._pre_create_widget.set_plugin(creator) + + self._selected_creator = creator + + if not creator: + self._set_context_enabled(False) + return + + if ( + creator.create_allow_context_change + != self._context_change_is_enabled() + ): + self._set_context_enabled(creator.create_allow_context_change) + self._refresh_asset() + + default_variants = creator.get_default_variants() + if not default_variants: + default_variants = ["Main"] + + default_variant = creator.get_default_variant() + if not default_variant: + default_variant = default_variants[0] + + for action in tuple(self.variant_hints_menu.actions()): + self.variant_hints_menu.removeAction(action) + action.deleteLater() + + for variant in default_variants: + if variant in SEPARATORS: + self.variant_hints_menu.addSeparator() + elif variant: + self.variant_hints_menu.addAction(variant) + + variant_text = default_variant or "Main" + # Make sure subset name is updated to new plugin + if variant_text == self.variant_input.text(): + self._on_variant_change() + else: + self.variant_input.setText(variant_text) + + def _on_variant_widget_resize(self): + self.variant_hints_btn.setFixedHeight(self.variant_input.height()) + + def _on_variant_btn_click(self): + pos = self.variant_hints_btn.rect().bottomLeft() + point = self.variant_hints_btn.mapToGlobal(pos) + self.variant_hints_menu.popup(point) + + def _on_variant_action(self, action): + value = action.text() + if self.variant_input.text() != value: + self.variant_input.setText(value) + + def _on_variant_change(self, variant_value=None): + if not self._prereq_available: + return + + # This should probably never happen? + if not self._selected_creator: + if self.subset_name_input.text(): + self.subset_name_input.setText("") + return + + if variant_value is None: + variant_value = self.variant_input.text() + + if not self._compiled_name_pattern.match(variant_value): + self._create_btn.setEnabled(False) + self._set_variant_state_property("invalid") + self.subset_name_input.setText("< Invalid variant >") + return + + if not self._context_change_is_enabled(): + self._create_btn.setEnabled(True) + self._set_variant_state_property("") + self.subset_name_input.setText("< Valid variant >") + return + + project_name = self.controller.project_name + task_name = self._get_task_name() + + asset_doc = copy.deepcopy(self._asset_doc) + # Calculate subset name with Creator plugin + try: + subset_name = self._selected_creator.get_subset_name( + variant_value, task_name, asset_doc, project_name + ) + except TaskNotSetError: + self._create_btn.setEnabled(False) + self._set_variant_state_property("invalid") + self.subset_name_input.setText("< Missing task >") + return + + self.subset_name_input.setText(subset_name) + + self._create_btn.setEnabled(True) + self._validate_subset_name(subset_name, variant_value) + + def _validate_subset_name(self, subset_name, variant_value): + # Get all subsets of the current asset + if self._subset_names: + existing_subset_names = set(self._subset_names) + else: + existing_subset_names = set() + existing_subset_names_low = set( + _name.lower() + for _name in existing_subset_names + ) + + # Replace + compare_regex = re.compile(re.sub( + variant_value, "(.+)", subset_name, flags=re.IGNORECASE + )) + variant_hints = set() + if variant_value: + for _name in existing_subset_names: + _result = compare_regex.search(_name) + if _result: + variant_hints |= set(_result.groups()) + + # Remove previous hints from menu + for action in tuple(self.variant_hints_group.actions()): + self.variant_hints_group.removeAction(action) + self.variant_hints_menu.removeAction(action) + action.deleteLater() + + # Add separator if there are hints and menu already has actions + if variant_hints and self.variant_hints_menu.actions(): + self.variant_hints_menu.addSeparator() + + # Add hints to actions + for variant_hint in variant_hints: + action = self.variant_hints_menu.addAction(variant_hint) + self.variant_hints_group.addAction(action) + + # Indicate subset existence + if not variant_value: + property_value = "empty" + + elif subset_name.lower() in existing_subset_names_low: + # validate existence of subset name with lowered text + # - "renderMain" vs. "rendermain" mean same path item for + # windows + property_value = "exists" + else: + property_value = "new" + + self._set_variant_state_property(property_value) + + variant_is_valid = variant_value.strip() != "" + if variant_is_valid != self._create_btn.isEnabled(): + self._create_btn.setEnabled(variant_is_valid) + + def _set_variant_state_property(self, state): + current_value = self.variant_input.property("state") + if current_value != state: + self.variant_input.setProperty("state", state) + self.variant_input.style().polish(self.variant_input) + + def _on_first_show(self): + width = self.width() + part = int(width / 7) + self._splitter_widget.setSizes( + [part * 2, part * 2, width - (part * 4)] + ) + + def showEvent(self, event): + super(CreateWidget, self).showEvent(event) + if self._first_show: + self._first_show = False + self._on_first_show() + + def _on_create(self): + indexes = self._creators_view.selectedIndexes() + if not indexes or len(indexes) > 1: + return + + if not self._create_btn.isEnabled(): + return + + index = indexes[0] + creator_label = index.data(QtCore.Qt.DisplayRole) + creator_identifier = index.data(CREATOR_IDENTIFIER_ROLE) + family = index.data(FAMILY_ROLE) + variant = self.variant_input.text() + # Care about subset name only if context change is enabled + subset_name = None + asset_name = None + task_name = None + if self._context_change_is_enabled(): + subset_name = self.subset_name_input.text() + asset_name = self._get_asset_name() + task_name = self._get_task_name() + + pre_create_data = self._pre_create_widget.current_value() + # Where to define these data? + # - what data show be stored? + instance_data = { + "asset": asset_name, + "task": task_name, + "variant": variant, + "family": family + } + + error_msg = None + formatted_traceback = None + try: + self.controller.create( + creator_identifier, + subset_name, + instance_data, + pre_create_data + ) + + except CreatorError as exc: + error_msg = str(exc) + + # Use bare except because some hosts raise their exceptions that + # do not inherit from python's `BaseException` + except: + exc_type, exc_value, exc_traceback = sys.exc_info() + formatted_traceback = "".join(traceback.format_exception( + exc_type, exc_value, exc_traceback + )) + error_msg = str(exc_value) + + if error_msg is None: + self._set_creator(self._selected_creator) + self._emit_message("Creation finished...") + else: + box = CreateErrorMessageBox( + creator_label, + subset_name, + asset_name, + error_msg, + formatted_traceback, + parent=self + ) + box.show() + # Store dialog so is not garbage collected before is shown + self._message_dialog = box diff --git a/openpype/tools/publisher/widgets/overview_widget.py b/openpype/tools/publisher/widgets/overview_widget.py index abdd98ff7c..ddc976d458 100644 --- a/openpype/tools/publisher/widgets/overview_widget.py +++ b/openpype/tools/publisher/widgets/overview_widget.py @@ -10,6 +10,7 @@ from .widgets import ( RemoveInstanceBtn, ChangeViewBtn ) +from .create_widget import CreateWidget class CreateOverviewWidget(QtWidgets.QFrame): @@ -20,9 +21,13 @@ class CreateOverviewWidget(QtWidgets.QFrame): def __init__(self, controller, parent): super(CreateOverviewWidget, self).__init__(parent) - self._controller = controller self._refreshing_instances = False + self._controller = controller + create_widget = CreateWidget(controller, self) + + # --- Created Subsets/Instances --- + # Common widget for creation and overview subset_views_widget = BorderedLabelWidget( "Subsets to publish", self ) @@ -39,6 +44,7 @@ class CreateOverviewWidget(QtWidgets.QFrame): delete_btn = RemoveInstanceBtn(self) change_view_btn = ChangeViewBtn(self) + # --- Overview --- # Subset details widget subset_attributes_wrap = BorderedLabelWidget( "Publish options", self @@ -73,6 +79,7 @@ class CreateOverviewWidget(QtWidgets.QFrame): subset_content_widget = QtWidgets.QWidget(self) subset_content_layout = QtWidgets.QHBoxLayout(subset_content_widget) subset_content_layout.setContentsMargins(0, 0, 0, 0) + subset_content_layout.addWidget(create_widget, 7) subset_content_layout.addWidget(subset_views_widget, 3) subset_content_layout.addWidget(subset_attributes_wrap, 7) @@ -86,6 +93,7 @@ class CreateOverviewWidget(QtWidgets.QFrame): main_layout.setContentsMargins(marings) main_layout.addWidget(subset_content_widget, 1) + # --- Calbacks for instances/subsets view --- create_btn.clicked.connect(self._on_create_clicked) delete_btn.clicked.connect(self._on_delete_clicked) change_view_btn.clicked.connect(self._on_change_view_clicked) @@ -109,18 +117,38 @@ class CreateOverviewWidget(QtWidgets.QFrame): self._on_instance_context_change ) + # --- Controller callbacks --- controller.add_publish_reset_callback(self._on_publish_reset) controller.add_instances_refresh_callback(self._on_instances_refresh) - self.subset_content_widget = subset_content_widget + self._subset_content_widget = subset_content_widget - self.subset_view_cards = subset_view_cards - self.subset_list_view = subset_list_view - self.subset_views_layout = subset_views_layout + self._subset_view_cards = subset_view_cards + self._subset_list_view = subset_list_view + self._subset_views_layout = subset_views_layout - self.delete_btn = delete_btn + self._delete_btn = delete_btn - self.subset_attributes_widget = subset_attributes_widget + self._subset_attributes_widget = subset_attributes_widget + self._create_widget = create_widget + self._subset_attributes_wrap = subset_attributes_wrap + + # Start in create mode + self._current_state = "create" + subset_attributes_wrap.setVisible(False) + + def set_state(self, old_state, new_state): + if new_state == self._current_state: + return + + self._current_state = new_state + + self._create_widget.setVisible( + self._current_state == "create" + ) + self._subset_attributes_wrap.setVisible( + self._current_state == "publish" + ) def _on_create_clicked(self): """Pass signal to parent widget which should care about changing state. @@ -167,9 +195,9 @@ class CreateOverviewWidget(QtWidgets.QFrame): instances, context_selected = self.get_selected_items() # Disable delete button if nothing is selected - self.delete_btn.setEnabled(len(instances) > 0) + self._delete_btn.setEnabled(len(instances) > 0) - self.subset_attributes_widget.set_current_instances( + self._subset_attributes_widget.set_current_instances( instances, context_selected ) @@ -179,29 +207,29 @@ class CreateOverviewWidget(QtWidgets.QFrame): self.active_changed.emit() def _on_instance_context_change(self): - current_idx = self.subset_views_layout.currentIndex() - for idx in range(self.subset_views_layout.count()): + current_idx = self._subset_views_layout.currentIndex() + for idx in range(self._subset_views_layout.count()): if idx == current_idx: continue - widget = self.subset_views_layout.widget(idx) + widget = self._subset_views_layout.widget(idx) if widget.refreshed: widget.set_refreshed(False) - current_widget = self.subset_views_layout.widget(current_idx) + current_widget = self._subset_views_layout.widget(current_idx) current_widget.refresh_instance_states() self.instance_context_changed.emit() def get_selected_items(self): - view = self.subset_views_layout.currentWidget() + view = self._subset_views_layout.currentWidget() return view.get_selected_items() def _change_view_type(self): - idx = self.subset_views_layout.currentIndex() - new_idx = (idx + 1) % self.subset_views_layout.count() - self.subset_views_layout.setCurrentIndex(new_idx) + idx = self._subset_views_layout.currentIndex() + new_idx = (idx + 1) % self._subset_views_layout.count() + self._subset_views_layout.setCurrentIndex(new_idx) - new_view = self.subset_views_layout.currentWidget() + new_view = self._subset_views_layout.currentWidget() if not new_view.refreshed: new_view.refresh() new_view.set_refreshed(True) @@ -216,11 +244,11 @@ class CreateOverviewWidget(QtWidgets.QFrame): self._refreshing_instances = True - for idx in range(self.subset_views_layout.count()): - widget = self.subset_views_layout.widget(idx) + for idx in range(self._subset_views_layout.count()): + widget = self._subset_views_layout.widget(idx) widget.set_refreshed(False) - view = self.subset_views_layout.currentWidget() + view = self._subset_views_layout.currentWidget() view.refresh() view.set_refreshed(True) @@ -232,7 +260,7 @@ class CreateOverviewWidget(QtWidgets.QFrame): def _on_publish_reset(self): """Context in controller has been refreshed.""" - self.subset_content_widget.setEnabled(self._controller.host_is_valid) + self._subset_content_widget.setEnabled(self._controller.host_is_valid) def _on_instances_refresh(self): """Controller refreshed instances.""" @@ -242,5 +270,5 @@ class CreateOverviewWidget(QtWidgets.QFrame): # Give a change to process Resize Request QtWidgets.QApplication.processEvents() # Trigger update geometry of - widget = self.subset_views_layout.currentWidget() + widget = self._subset_views_layout.currentWidget() widget.updateGeometry() diff --git a/openpype/tools/publisher/widgets/tasks_widget.py b/openpype/tools/publisher/widgets/tasks_widget.py index aa239f6334..f31fffb9ea 100644 --- a/openpype/tools/publisher/widgets/tasks_widget.py +++ b/openpype/tools/publisher/widgets/tasks_widget.py @@ -141,10 +141,10 @@ class TasksModel(QtGui.QStandardItemModel): return super(TasksModel, self).headerData(section, orientation, role) -class CreateDialogTasksWidget(TasksWidget): +class CreateWidgetTasksWidget(TasksWidget): def __init__(self, controller, parent): self._controller = controller - super(CreateDialogTasksWidget, self).__init__(None, parent) + super(CreateWidgetTasksWidget, self).__init__(None, parent) self._enabled = None @@ -164,7 +164,7 @@ class CreateDialogTasksWidget(TasksWidget): self.task_changed.emit() def select_task_name(self, task_name): - super(CreateDialogTasksWidget, self).select_task_name(task_name) + super(CreateWidgetTasksWidget, self).select_task_name(task_name) if not self._enabled: current = self.get_selected_task_name() if current: From b6312fe3692850dc1a821e9c79d83425078d5ba7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Sep 2022 17:45:43 +0200 Subject: [PATCH 06/72] changed style of disable button --- openpype/style/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/style/style.css b/openpype/style/style.css index ab23dd621f..1d112fa575 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -868,6 +868,10 @@ PublisherTabBtn { padding: 0.5em 1em 0.5em 1em; } +PublisherTabBtn:disabled { + background: {color:bg-inputs}; +} + PublisherTabBtn:hover { background: {color:bg-buttons}; } From 22dab5ddefb2908d3eefcbd8a46cbb77a204ed4c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Sep 2022 17:46:20 +0200 Subject: [PATCH 07/72] removed created dialog --- openpype/tools/publisher/widgets/__init__.py | 3 - .../tools/publisher/widgets/create_dialog.py | 1222 ----------------- .../tools/publisher/widgets/tabs_widget.py | 9 + openpype/tools/publisher/window.py | 36 +- 4 files changed, 22 insertions(+), 1248 deletions(-) delete mode 100644 openpype/tools/publisher/widgets/create_dialog.py diff --git a/openpype/tools/publisher/widgets/__init__.py b/openpype/tools/publisher/widgets/__init__.py index 869f7adf9b..1d0ed0633b 100644 --- a/openpype/tools/publisher/widgets/__init__.py +++ b/openpype/tools/publisher/widgets/__init__.py @@ -10,7 +10,6 @@ from .widgets import ( PublishBtn, ) from .publish_widget import PublishFrame -from .create_dialog import CreateDialog from .tabs_widget import PublisherTabsWidget from .overview_widget import CreateOverviewWidget @@ -26,8 +25,6 @@ __all__ = ( "PublishFrame", - "CreateDialog", - "PublisherTabsWidget", "CreateOverviewWidget", ) diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py deleted file mode 100644 index 173df7d5c8..0000000000 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ /dev/null @@ -1,1222 +0,0 @@ -import sys -import re -import traceback -import copy - -import qtawesome -try: - import commonmark -except Exception: - commonmark = None -from Qt import QtWidgets, QtCore, QtGui - -from openpype.client import get_asset_by_name, get_subsets -from openpype.pipeline.create import ( - CreatorError, - SUBSET_NAME_ALLOWED_SYMBOLS, - TaskNotSetError, -) -from openpype.tools.utils import ( - ErrorMessageBox, - MessageOverlayObject, - ClickableFrame, -) - -from .widgets import IconValuePixmapLabel -from .assets_widget import CreateDialogAssetsWidget -from .tasks_widget import CreateDialogTasksWidget -from .precreate_widget import PreCreateWidget -from ..constants import ( - VARIANT_TOOLTIP, - CREATOR_IDENTIFIER_ROLE, - FAMILY_ROLE -) - -SEPARATORS = ("---separator---", "---") - - -class VariantInputsWidget(QtWidgets.QWidget): - resized = QtCore.Signal() - - def resizeEvent(self, event): - super(VariantInputsWidget, self).resizeEvent(event) - self.resized.emit() - - -class CreateErrorMessageBox(ErrorMessageBox): - def __init__( - self, - creator_label, - subset_name, - asset_name, - exc_msg, - formatted_traceback, - parent - ): - self._creator_label = creator_label - self._subset_name = subset_name - self._asset_name = asset_name - self._exc_msg = exc_msg - self._formatted_traceback = formatted_traceback - super(CreateErrorMessageBox, self).__init__("Creation failed", parent) - - def _create_top_widget(self, parent_widget): - label_widget = QtWidgets.QLabel(parent_widget) - label_widget.setText( - "Failed to create" - ) - return label_widget - - def _get_report_data(self): - report_message = ( - "{creator}: Failed to create Subset: \"{subset}\"" - " in Asset: \"{asset}\"" - "\n\nError: {message}" - ).format( - creator=self._creator_label, - subset=self._subset_name, - asset=self._asset_name, - message=self._exc_msg, - ) - if self._formatted_traceback: - report_message += "\n\n{}".format(self._formatted_traceback) - return [report_message] - - def _create_content(self, content_layout): - item_name_template = ( - "Creator: {}
" - "Subset: {}
" - "Asset: {}
" - ) - exc_msg_template = "{}" - - line = self._create_line() - content_layout.addWidget(line) - - item_name_widget = QtWidgets.QLabel(self) - item_name_widget.setText( - item_name_template.format( - self._creator_label, self._subset_name, self._asset_name - ) - ) - content_layout.addWidget(item_name_widget) - - message_label_widget = QtWidgets.QLabel(self) - message_label_widget.setText( - exc_msg_template.format(self.convert_text_for_html(self._exc_msg)) - ) - content_layout.addWidget(message_label_widget) - - if self._formatted_traceback: - line_widget = self._create_line() - tb_widget = self._create_traceback_widget( - self._formatted_traceback - ) - content_layout.addWidget(line_widget) - content_layout.addWidget(tb_widget) - - -# 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) - - # --- Short description widget --- - icon_widget = IconValuePixmapLabel(None, self) - icon_widget.setObjectName("FamilyIconLabel") - - # --- Short description inputs --- - short_desc_input_widget = QtWidgets.QWidget(self) - - family_label = QtWidgets.QLabel(short_desc_input_widget) - family_label.setAlignment( - QtCore.Qt.AlignBottom | QtCore.Qt.AlignLeft - ) - - description_label = QtWidgets.QLabel(short_desc_input_widget) - description_label.setAlignment( - QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft - ) - - short_desc_input_layout = QtWidgets.QVBoxLayout( - short_desc_input_widget - ) - short_desc_input_layout.setSpacing(0) - short_desc_input_layout.addWidget(family_label) - short_desc_input_layout.addWidget(description_label) - # -------------------------------- - - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(icon_widget, 0) - layout.addWidget(short_desc_input_widget, 1) - # -------------------------------- - - self._icon_widget = icon_widget - 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) - self._family_label.setText("") - self._description_label.setText("") - return - - plugin_icon = plugin.get_icon() - description = plugin.get_description() or "" - - self._icon_widget.set_icon_def(plugin_icon) - self._family_label.setText("{}".format(plugin.family)) - self._family_label.setTextInteractionFlags(QtCore.Qt.NoTextInteraction) - self._description_label.setText(description) - - -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() - - def set_expanded(self, expanded=None): - if self._expanded is expanded: - if expanded is not None: - return - expanded = False - self._expanded = expanded - 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: - question_mark_icon = qtawesome.icon( - self.question_mark_icon_name, color=QtCore.Qt.white - ) - question_mark_pix = question_mark_icon.pixmap(width, width) - - 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 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.resized.emit(self.height()) - - def resizeEvent(self, event): - super(HelpButton, self).resizeEvent(event) - self.resized.emit(self.height()) - - -class CreateDialog(QtWidgets.QDialog): - default_size = (1000, 560) - - def __init__( - self, controller, asset_name=None, task_name=None, parent=None - ): - super(CreateDialog, self).__init__(parent) - - self.setWindowTitle("Create new instance") - - self.controller = controller - - if asset_name is None: - asset_name = self.dbcon.Session.get("AVALON_ASSET") - - if task_name is None: - task_name = self.dbcon.Session.get("AVALON_TASK") - - self._asset_name = asset_name - self._task_name = task_name - - self._last_pos = None - self._asset_doc = None - self._subset_names = None - self._selected_creator = None - - self._prereq_available = False - - self._message_dialog = None - - name_pattern = "^[{}]*$".format(SUBSET_NAME_ALLOWED_SYMBOLS) - self._name_pattern = name_pattern - self._compiled_name_pattern = re.compile(name_pattern) - - overlay_object = MessageOverlayObject(self) - - context_widget = QtWidgets.QWidget(self) - - assets_widget = CreateDialogAssetsWidget(controller, context_widget) - tasks_widget = CreateDialogTasksWidget(controller, context_widget) - - context_layout = QtWidgets.QVBoxLayout(context_widget) - context_layout.setContentsMargins(0, 0, 0, 0) - context_layout.setSpacing(0) - context_layout.addWidget(assets_widget, 2) - 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_sort_model = QtCore.QSortFilterProxyModel() - creators_sort_model.setSourceModel(creators_model) - creators_view.setModel(creators_sort_model) - - variant_widget = VariantInputsWidget(self) - - variant_input = QtWidgets.QLineEdit(variant_widget) - variant_input.setObjectName("VariantInput") - variant_input.setToolTip(VARIANT_TOOLTIP) - - variant_hints_btn = QtWidgets.QToolButton(variant_widget) - variant_hints_btn.setArrowType(QtCore.Qt.DownArrow) - variant_hints_btn.setIconSize(QtCore.QSize(12, 12)) - - variant_hints_menu = QtWidgets.QMenu(variant_widget) - variant_hints_group = QtWidgets.QActionGroup(variant_hints_menu) - - variant_layout = QtWidgets.QHBoxLayout(variant_widget) - variant_layout.setContentsMargins(0, 0, 0, 0) - variant_layout.setSpacing(0) - variant_layout.addWidget(variant_input, 1) - variant_layout.addWidget(variant_hints_btn, 0, QtCore.Qt.AlignVCenter) - - subset_name_input = QtWidgets.QLineEdit(self) - subset_name_input.setEnabled(False) - - form_layout = QtWidgets.QFormLayout() - form_layout.addRow("Variant:", variant_widget) - form_layout.addRow("Subset:", subset_name_input) - - mid_widget = QtWidgets.QWidget(self) - mid_layout = QtWidgets.QVBoxLayout(mid_widget) - mid_layout.setContentsMargins(0, 0, 0, 0) - mid_layout.addWidget(creators_header_widget, 0) - mid_layout.addWidget(creators_view, 1) - mid_layout.addLayout(form_layout, 0) - # ------------ - - # --- Creator short info and attr defs --- - creator_attrs_widget = QtWidgets.QWidget(self) - - creator_short_desc_widget = CreatorShortDescWidget( - creator_attrs_widget - ) - - attr_separator_widget = QtWidgets.QWidget(self) - attr_separator_widget.setObjectName("Separator") - attr_separator_widget.setMinimumHeight(1) - attr_separator_widget.setMaximumHeight(1) - - # 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(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.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("CreatorDetailedDescription") - detail_description_input.setTextInteractionFlags( - QtCore.Qt.TextBrowserInteraction - ) - - 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) - splitter_widget.addWidget(creator_attrs_widget) - splitter_widget.addWidget(detail_description_widget) - splitter_widget.setStretchFactor(0, 1) - splitter_widget.setStretchFactor(1, 1) - splitter_widget.setStretchFactor(2, 1) - splitter_widget.setStretchFactor(3, 1) - - layout = QtWidgets.QHBoxLayout(self) - 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() - 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._invalidate_prereq) - - 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) - - 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) - variant_input.textChanged.connect(self._on_variant_change) - creators_view.selectionModel().currentChanged.connect( - self._on_creator_item_change - ) - variant_hints_btn.clicked.connect(self._on_variant_btn_click) - variant_hints_menu.triggered.connect(self._on_variant_action) - assets_widget.selection_changed.connect(self._on_asset_change) - assets_widget.current_context_required.connect( - 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 - ) - splitter_widget.splitterMoved.connect(self._on_splitter_move) - - controller.add_plugins_refresh_callback(self._on_plugins_refresh) - - self._overlay_object = overlay_object - - self._splitter_widget = splitter_widget - - self._context_widget = context_widget - self._assets_widget = assets_widget - self._tasks_widget = tasks_widget - - self.subset_name_input = subset_name_input - - self.variant_input = variant_input - self.variant_hints_btn = variant_hints_btn - 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_sort_model = creators_sort_model - self._creators_view = creators_view - self._create_btn = create_btn - - 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 - 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._last_desc_max_width = None - self._other_widgets_widths = [] - - def _emit_message(self, message): - self._overlay_object.add_message(message) - - def _context_change_is_enabled(self): - return self._context_widget.isEnabled() - - def _get_asset_name(self): - asset_name = None - if self._context_change_is_enabled(): - asset_name = self._assets_widget.get_selected_asset_name() - - if asset_name is None: - asset_name = self._asset_name - return asset_name - - def _get_task_name(self): - task_name = None - if self._context_change_is_enabled(): - # Don't use selection of task if asset is not set - asset_name = self._assets_widget.get_selected_asset_name() - if asset_name: - task_name = self._tasks_widget.get_selected_task_name() - - if not task_name: - task_name = self._task_name - return task_name - - @property - def dbcon(self): - return self.controller.dbcon - - def _set_context_enabled(self, enabled): - self._assets_widget.set_enabled(enabled) - self._tasks_widget.set_enabled(enabled) - check_prereq = self._context_widget.isEnabled() != enabled - self._context_widget.setEnabled(enabled) - if check_prereq: - self._invalidate_prereq() - - def refresh(self): - # Get context before refresh to keep selection of asset and - # task widgets - asset_name = self._get_asset_name() - task_name = self._get_task_name() - - self._prereq_available = False - - # Disable context widget so refresh of asset will use context asset - # name - self._set_context_enabled(False) - - self._assets_widget.refresh() - - # Refresh data before update of creators - self._refresh_asset() - # Then refresh creators which may trigger callbacks using refreshed - # data - self._refresh_creators() - - self._assets_widget.set_current_asset_name(self._asset_name) - self._assets_widget.select_asset_by_name(asset_name) - self._tasks_widget.set_asset_name(asset_name) - self._tasks_widget.select_task_name(task_name) - - self._invalidate_prereq_deffered() - - def _invalidate_prereq_deffered(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 _invalidate_prereq(self): - prereq_available = True - creator_btn_tooltips = [] - - available_creators = self._creators_model.rowCount() > 0 - if available_creators != self._creators_view.isEnabled(): - self._creators_view.setEnabled(available_creators) - - if not available_creators: - prereq_available = False - creator_btn_tooltips.append("Creator is not selected") - - if self._context_change_is_enabled() and self._asset_doc is None: - # QUESTION how to handle invalid asset? - prereq_available = False - creator_btn_tooltips.append("Context is not selected") - - if prereq_available != self._prereq_available: - self._prereq_available = prereq_available - - self._create_btn.setEnabled(prereq_available) - - self.variant_input.setEnabled(prereq_available) - self.variant_hints_btn.setEnabled(prereq_available) - - tooltip = "" - if creator_btn_tooltips: - tooltip = "\n".join(creator_btn_tooltips) - self._create_btn.setToolTip(tooltip) - - self._on_variant_change() - - def _refresh_asset(self): - asset_name = self._get_asset_name() - - # Skip if asset did not change - if self._asset_doc and self._asset_doc["name"] == asset_name: - return - - # Make sure `_asset_doc` and `_subset_names` variables are reset - self._asset_doc = None - self._subset_names = None - if asset_name is None: - return - - project_name = self.dbcon.active_project() - asset_doc = get_asset_by_name(project_name, asset_name) - self._asset_doc = asset_doc - - if asset_doc: - asset_id = asset_doc["_id"] - subset_docs = get_subsets( - project_name, asset_ids=[asset_id], fields=["name"] - ) - self._subset_names = { - subset_doc["name"] - for subset_doc in subset_docs - } - - if not asset_doc: - self.subset_name_input.setText("< Asset is not set >") - - def _refresh_creators(self): - # Refresh creators and add their families to list - existing_items = {} - old_creators = set() - for row in range(self._creators_model.rowCount()): - item = self._creators_model.item(row, 0) - identifier = item.data(CREATOR_IDENTIFIER_ROLE) - existing_items[identifier] = item - old_creators.add(identifier) - - # Add new families - new_creators = set() - for identifier, creator in self.controller.manual_creators.items(): - # TODO add details about creator - new_creators.add(identifier) - if identifier in existing_items: - item = existing_items[identifier] - else: - item = QtGui.QStandardItem() - item.setFlags( - QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable - ) - self._creators_model.appendRow(item) - - label = creator.label or identifier - item.setData(label, QtCore.Qt.DisplayRole) - item.setData(identifier, CREATOR_IDENTIFIER_ROLE) - item.setData(creator.family, FAMILY_ROLE) - - # Remove families that are no more available - for identifier in (old_creators - new_creators): - item = existing_items[identifier] - self._creators_model.takeRow(item.row()) - - if self._creators_model.rowCount() < 1: - return - - self._creators_sort_model.sort(0) - # Make sure there is a selection - indexes = self._creators_view.selectedIndexes() - if not indexes: - index = self._creators_sort_model.index(0, 0) - self._creators_view.setCurrentIndex(index) - else: - index = indexes[0] - - identifier = index.data(CREATOR_IDENTIFIER_ROLE) - - self._set_creator_by_identifier(identifier) - - def _on_plugins_refresh(self): - # Trigger refresh only if is visible - if self.isVisible(): - self.refresh() - - def _on_asset_change(self): - self._refresh_asset() - - asset_name = self._assets_widget.get_selected_asset_name() - self._tasks_widget.set_asset_name(asset_name) - if self._context_change_is_enabled(): - self._invalidate_prereq_deffered() - - def _on_task_change(self): - if self._context_change_is_enabled(): - self._invalidate_prereq_deffered() - - def _on_current_session_context_request(self): - self._assets_widget.set_current_session_asset() - 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(): - identifier = new_index.data(CREATOR_IDENTIFIER_ROLE) - self._set_creator_by_identifier(identifier) - - def _update_help_btn(self): - short_desc_rect = self._creator_short_desc_widget.rect() - - # 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() - 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 -= ( - 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_splitter_move(self, *args): - 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() - - 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) - - self._last_full_width = final_size.width() - self._other_widgets_widths = list(sizes) - - if now_visible: - 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: - 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 - - 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 - self._desc_widget_step = step_size - - self._desc_width_anim_timer.start() - - sizes.append(desc_width) - - final_size.setWidth(width) - - 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.setMinimumWidth(0) - self._detail_description_widget.setMaximumWidth( - self._last_desc_max_width - ) - 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("") - return - detailed_description = creator.get_detail_description() or "" - if commonmark: - html = commonmark.commonmark(detailed_description) - self._detail_description_input.setHtml(html) - else: - self._detail_description_input.setMarkdown(detailed_description) - - def _set_creator_by_identifier(self, identifier): - creator = self.controller.manual_creators.get(identifier) - self._set_creator(creator) - - def _set_creator(self, creator): - self._creator_short_desc_widget.set_plugin(creator) - self._set_creator_detailed_text(creator) - self._pre_create_widget.set_plugin(creator) - - self._selected_creator = creator - - if not creator: - self._set_context_enabled(False) - return - - if ( - creator.create_allow_context_change - != self._context_change_is_enabled() - ): - self._set_context_enabled(creator.create_allow_context_change) - self._refresh_asset() - - default_variants = creator.get_default_variants() - if not default_variants: - default_variants = ["Main"] - - default_variant = creator.get_default_variant() - if not default_variant: - default_variant = default_variants[0] - - for action in tuple(self.variant_hints_menu.actions()): - self.variant_hints_menu.removeAction(action) - action.deleteLater() - - for variant in default_variants: - if variant in SEPARATORS: - self.variant_hints_menu.addSeparator() - elif variant: - self.variant_hints_menu.addAction(variant) - - variant_text = default_variant or "Main" - # Make sure subset name is updated to new plugin - if variant_text == self.variant_input.text(): - self._on_variant_change() - else: - self.variant_input.setText(variant_text) - - def _on_variant_widget_resize(self): - self.variant_hints_btn.setFixedHeight(self.variant_input.height()) - - def _on_variant_btn_click(self): - pos = self.variant_hints_btn.rect().bottomLeft() - point = self.variant_hints_btn.mapToGlobal(pos) - self.variant_hints_menu.popup(point) - - def _on_variant_action(self, action): - value = action.text() - if self.variant_input.text() != value: - self.variant_input.setText(value) - - def _on_variant_change(self, variant_value=None): - if not self._prereq_available: - return - - # This should probably never happen? - if not self._selected_creator: - if self.subset_name_input.text(): - self.subset_name_input.setText("") - return - - if variant_value is None: - variant_value = self.variant_input.text() - - if not self._compiled_name_pattern.match(variant_value): - self._create_btn.setEnabled(False) - self._set_variant_state_property("invalid") - self.subset_name_input.setText("< Invalid variant >") - return - - if not self._context_change_is_enabled(): - self._create_btn.setEnabled(True) - self._set_variant_state_property("") - self.subset_name_input.setText("< Valid variant >") - return - - project_name = self.controller.project_name - task_name = self._get_task_name() - - asset_doc = copy.deepcopy(self._asset_doc) - # Calculate subset name with Creator plugin - try: - subset_name = self._selected_creator.get_subset_name( - variant_value, task_name, asset_doc, project_name - ) - except TaskNotSetError: - self._create_btn.setEnabled(False) - self._set_variant_state_property("invalid") - self.subset_name_input.setText("< Missing task >") - return - - self.subset_name_input.setText(subset_name) - - self._create_btn.setEnabled(True) - self._validate_subset_name(subset_name, variant_value) - - def _validate_subset_name(self, subset_name, variant_value): - # Get all subsets of the current asset - if self._subset_names: - existing_subset_names = set(self._subset_names) - else: - existing_subset_names = set() - existing_subset_names_low = set( - _name.lower() - for _name in existing_subset_names - ) - - # Replace - compare_regex = re.compile(re.sub( - variant_value, "(.+)", subset_name, flags=re.IGNORECASE - )) - variant_hints = set() - if variant_value: - for _name in existing_subset_names: - _result = compare_regex.search(_name) - if _result: - variant_hints |= set(_result.groups()) - - # Remove previous hints from menu - for action in tuple(self.variant_hints_group.actions()): - self.variant_hints_group.removeAction(action) - self.variant_hints_menu.removeAction(action) - action.deleteLater() - - # Add separator if there are hints and menu already has actions - if variant_hints and self.variant_hints_menu.actions(): - self.variant_hints_menu.addSeparator() - - # Add hints to actions - for variant_hint in variant_hints: - action = self.variant_hints_menu.addAction(variant_hint) - self.variant_hints_group.addAction(action) - - # Indicate subset existence - if not variant_value: - property_value = "empty" - - elif subset_name.lower() in existing_subset_names_low: - # validate existence of subset name with lowered text - # - "renderMain" vs. "rendermain" mean same path item for - # windows - property_value = "exists" - else: - property_value = "new" - - self._set_variant_state_property(property_value) - - variant_is_valid = variant_value.strip() != "" - if variant_is_valid != self._create_btn.isEnabled(): - self._create_btn.setEnabled(variant_is_valid) - - def _set_variant_state_property(self, state): - current_value = self.variant_input.property("state") - if current_value != state: - 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() - - def showEvent(self, event): - super(CreateDialog, self).showEvent(event) - if self._first_show: - self._first_show = False - self._on_first_show() - - if self._last_pos is not None: - self.move(self._last_pos) - - self._update_help_btn() - - self.refresh() - - def resizeEvent(self, event): - super(CreateDialog, self).resizeEvent(event) - self._update_help_btn() - - def _on_create(self): - indexes = self._creators_view.selectedIndexes() - if not indexes or len(indexes) > 1: - return - - if not self._create_btn.isEnabled(): - return - - index = indexes[0] - creator_label = index.data(QtCore.Qt.DisplayRole) - creator_identifier = index.data(CREATOR_IDENTIFIER_ROLE) - family = index.data(FAMILY_ROLE) - variant = self.variant_input.text() - # Care about subset name only if context change is enabled - subset_name = None - asset_name = None - task_name = None - if self._context_change_is_enabled(): - subset_name = self.subset_name_input.text() - asset_name = self._get_asset_name() - task_name = self._get_task_name() - - pre_create_data = self._pre_create_widget.current_value() - # Where to define these data? - # - what data show be stored? - instance_data = { - "asset": asset_name, - "task": task_name, - "variant": variant, - "family": family - } - - error_msg = None - formatted_traceback = None - try: - self.controller.create( - creator_identifier, - subset_name, - instance_data, - pre_create_data - ) - - except CreatorError as exc: - error_msg = str(exc) - - # Use bare except because some hosts raise their exceptions that - # do not inherit from python's `BaseException` - except: - exc_type, exc_value, exc_traceback = sys.exc_info() - formatted_traceback = "".join(traceback.format_exception( - exc_type, exc_value, exc_traceback - )) - error_msg = str(exc_value) - - if error_msg is None: - self._set_creator(self._selected_creator) - self._emit_message("Creation finished...") - else: - box = CreateErrorMessageBox( - creator_label, - subset_name, - asset_name, - error_msg, - formatted_traceback, - parent=self - ) - box.show() - # Store dialog so is not garbage collected before is shown - self._message_dialog = box diff --git a/openpype/tools/publisher/widgets/tabs_widget.py b/openpype/tools/publisher/widgets/tabs_widget.py index 0e92a6fd8d..bf3ef9eec0 100644 --- a/openpype/tools/publisher/widgets/tabs_widget.py +++ b/openpype/tools/publisher/widgets/tabs_widget.py @@ -53,6 +53,11 @@ class PublisherTabsWidget(QtWidgets.QFrame): self._current_button = None self._buttons_by_identifier = {} + def is_current_tab(self, identifier): + if isinstance(identifier, PublisherTabBtn): + identifier = identifier.identifier + return self._current_button == identifier + def add_tab(self, label, identifier): button = PublisherTabBtn(identifier, label, self) button.tab_clicked.connect(self._on_tab_click) @@ -61,8 +66,12 @@ class PublisherTabsWidget(QtWidgets.QFrame): if self._current_button is None: self.set_current_tab(identifier) + return button def set_current_tab(self, identifier): + if isinstance(identifier, PublisherTabBtn): + identifier = identifier.identifier + if identifier == self._current_button: return diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 8df9f9bbf5..5fd558a1b5 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -15,8 +15,6 @@ from .widgets import ( PublisherTabsWidget, - CreateDialog, - StopBtn, ResetBtn, ValidateBtn, @@ -76,7 +74,7 @@ class PublisherWindow(QtWidgets.QDialog): # Tabs widget under header tabs_widget = PublisherTabsWidget(self) - tabs_widget.add_tab("Create", "create") + create_tab = tabs_widget.add_tab("Create", "create") tabs_widget.add_tab("Publish", "publish") tabs_widget.add_tab("Report", "report") tabs_widget.add_tab("Details", "details") @@ -137,8 +135,6 @@ class PublisherWindow(QtWidgets.QDialog): main_layout.addWidget(content_stacked_widget, 1) main_layout.addWidget(footer_widget, 0) - creator_window = CreateDialog(controller, parent=self) - tabs_widget.tab_changed.connect(self._on_tab_change) create_overview_widget.active_changed.connect( self._on_context_or_active_change @@ -165,6 +161,7 @@ class PublisherWindow(QtWidgets.QDialog): self._header_layout = header_layout self._tabs_widget = tabs_widget + self._create_tab = create_tab self._content_stacked_widget = content_stacked_widget self.content_stacked_layout = content_stacked_layout @@ -182,8 +179,6 @@ class PublisherWindow(QtWidgets.QDialog): self._controller = controller - self.creator_window = creator_window - @property def controller(self): return self._controller @@ -208,7 +203,10 @@ class PublisherWindow(QtWidgets.QDialog): self.context_label.setText(label) def _on_tab_change(self, prev_tab, new_tab): - print(prev_tab, new_tab) + if new_tab in ("create", "publish"): + self._create_overview_widget.set_state(prev_tab, new_tab) + + # TODO handle rest of conditions def _on_context_or_active_change(self): self._validate_create_instances() @@ -222,23 +220,9 @@ class PublisherWindow(QtWidgets.QDialog): def _set_publish_visibility(self, visible): if visible: widget = self.publish_frame - publish_frame_visible = True else: widget = self._create_overview_widget - publish_frame_visible = False self.content_stacked_layout.setCurrentWidget(widget) - self._set_publish_frame_visible(publish_frame_visible) - - def _set_publish_frame_visible(self, publish_frame_visible): - """Publish frame visibility has changed. - - Also used in TrayPublisher to be able handle start/end of publish - widget overlay. - """ - - # Hide creator dialog if visible - if publish_frame_visible and self.creator_window.isVisible(): - self.creator_window.close() def _on_reset_clicked(self): self._controller.reset() @@ -264,7 +248,6 @@ class PublisherWindow(QtWidgets.QDialog): self._controller.publish() def _set_footer_enabled(self, enabled): - self.comment_input.setEnabled(enabled) self.reset_btn.setEnabled(True) if enabled: self.stop_btn.setEnabled(False) @@ -276,6 +259,8 @@ class PublisherWindow(QtWidgets.QDialog): self.publish_btn.setEnabled(enabled) def _on_publish_reset(self): + self._create_tab.setEnabled(True) + self.comment_input.setVisible(True) self._set_publish_visibility(False) self._set_footer_enabled(False) @@ -286,6 +271,11 @@ class PublisherWindow(QtWidgets.QDialog): self.validate_btn.setEnabled(False) self.publish_btn.setEnabled(False) + self.comment_input.setVisible(False) + self._create_tab.setEnabled(False) + if self._tabs_widget.is_current_tab(self._create_tab): + self._tabs_widget.set_current_tab("publish") + def _on_publish_validated(self): self.validate_btn.setEnabled(False) From 88ace97c941def6b9189cc4bc9e8bd6792d173bf Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Sep 2022 17:57:16 +0200 Subject: [PATCH 08/72] fixed project selection in tray publisher --- openpype/tools/traypublisher/window.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/openpype/tools/traypublisher/window.py b/openpype/tools/traypublisher/window.py index 128c0fef11..97edb9ab06 100644 --- a/openpype/tools/traypublisher/window.py +++ b/openpype/tools/traypublisher/window.py @@ -137,11 +137,14 @@ class StandaloneOverlayWidget(QtWidgets.QFrame): src_index = self._projects_model.find_project(project_name) if src_index is not None: index = self._projects_proxy.mapFromSource(src_index) - if index: - mode = ( - QtCore.QItemSelectionModel.Select - | QtCore.QItemSelectionModel.Rows) - self._projects_view.selectionModel().select(index, mode) + + if index is not None: + selection_model = self._projects_view.selectionModel() + selection_model.select( + index, + QtCore.QItemSelectionModel.SelectCurrent + ) + self._projects_view.setCurrentIndex(index) self._cancel_btn.setVisible(self._project_name is not None) super(StandaloneOverlayWidget, self).showEvent(event) @@ -239,15 +242,15 @@ class TrayPublishWindow(PublisherWindow): def _on_project_select(self, project_name): # TODO register project specific plugin paths - self.controller.save_changes() - self.controller.reset_project_data_cache() + self._controller.save_changes() + self._controller.reset_project_data_cache() self.reset() - if not self.controller.instances: + if not self._controller.instances: self._go_to_create_tab() def _on_tray_publish_save(self): - self.controller.save_changes() + self._controller.save_changes() print("NOT YET IMPLEMENTED") From 5857c01442dc81daa71e71f78725a682c450f27e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Sep 2022 18:20:08 +0200 Subject: [PATCH 09/72] changed PublishReportViewerWidget to frame --- openpype/tools/publisher/publish_report_viewer/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/publisher/publish_report_viewer/widgets.py b/openpype/tools/publisher/publish_report_viewer/widgets.py index 61eb814a56..dc82448495 100644 --- a/openpype/tools/publisher/publish_report_viewer/widgets.py +++ b/openpype/tools/publisher/publish_report_viewer/widgets.py @@ -331,7 +331,7 @@ class DetailsPopup(QtWidgets.QDialog): self.closed.emit() -class PublishReportViewerWidget(QtWidgets.QWidget): +class PublishReportViewerWidget(QtWidgets.QFrame): def __init__(self, parent=None): super(PublishReportViewerWidget, self).__init__(parent) From 55e4aa3c76d397e59c909021e4eb642f6e73821a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Sep 2022 18:20:33 +0200 Subject: [PATCH 10/72] added details to window --- openpype/tools/publisher/widgets/__init__.py | 1 + .../tools/publisher/widgets/create_widget.py | 1 + .../publisher/widgets/overview_widget.py | 9 +- openpype/tools/publisher/window.py | 128 +++++++++++------- 4 files changed, 86 insertions(+), 53 deletions(-) diff --git a/openpype/tools/publisher/widgets/__init__.py b/openpype/tools/publisher/widgets/__init__.py index 1d0ed0633b..f8e3c4b19b 100644 --- a/openpype/tools/publisher/widgets/__init__.py +++ b/openpype/tools/publisher/widgets/__init__.py @@ -13,6 +13,7 @@ from .publish_widget import PublishFrame from .tabs_widget import PublisherTabsWidget from .overview_widget import CreateOverviewWidget + __all__ = ( "get_icon_path", "get_pixmap", diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index a0b3db0409..733dbf18ca 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -443,6 +443,7 @@ class CreateWidget(QtWidgets.QWidget): splitter_widget.setStretchFactor(3, 1) layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(splitter_widget, 1) prereq_timer = QtCore.QTimer() diff --git a/openpype/tools/publisher/widgets/overview_widget.py b/openpype/tools/publisher/widgets/overview_widget.py index ddc976d458..7afe02116f 100644 --- a/openpype/tools/publisher/widgets/overview_widget.py +++ b/openpype/tools/publisher/widgets/overview_widget.py @@ -85,12 +85,7 @@ class CreateOverviewWidget(QtWidgets.QFrame): # Subset frame layout main_layout = QtWidgets.QVBoxLayout(self) - marings = main_layout.contentsMargins() - marings.setLeft(marings.left() * 2) - marings.setRight(marings.right() * 2) - marings.setTop(marings.top() * 2) - marings.setBottom(0) - main_layout.setContentsMargins(marings) + main_layout.setContentsMargins(0, 0, 0, 0) main_layout.addWidget(subset_content_widget, 1) # --- Calbacks for instances/subsets view --- @@ -137,7 +132,7 @@ class CreateOverviewWidget(QtWidgets.QFrame): self._current_state = "create" subset_attributes_wrap.setVisible(False) - def set_state(self, old_state, new_state): + def set_state(self, new_state, animate): if new_state == self._current_state: return diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 5fd558a1b5..4aa02ff2d5 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -8,6 +8,7 @@ from openpype.tools.utils import ( PlaceholderLineEdit, PixmapLabel ) +from .publish_report_viewer import PublishReportViewerWidget from .control import PublisherController from .widgets import ( CreateOverviewWidget, @@ -79,13 +80,6 @@ class PublisherWindow(QtWidgets.QDialog): tabs_widget.add_tab("Report", "report") tabs_widget.add_tab("Details", "details") - # Content - content_stacked_widget = QtWidgets.QWidget(self) - - create_overview_widget = CreateOverviewWidget( - controller, content_stacked_widget - ) - # Footer footer_widget = QtWidgets.QWidget(self) footer_bottom_widget = QtWidgets.QWidget(footer_widget) @@ -113,17 +107,45 @@ class PublisherWindow(QtWidgets.QDialog): footer_layout.addWidget(comment_input, 0) footer_layout.addWidget(footer_bottom_widget, 0) + # Content + # - wrap stacked widget under one more widget to be able propagate + # margins (QStackedLayout can't have margins) + content_widget = QtWidgets.QWidget(self) + + content_stacked_widget = QtWidgets.QWidget(content_widget) + + content_layout = QtWidgets.QVBoxLayout(content_widget) + marings = content_layout.contentsMargins() + marings.setLeft(marings.left() * 2) + marings.setRight(marings.right() * 2) + marings.setTop(marings.top() * 2) + marings.setBottom(0) + content_layout.setContentsMargins(marings) + content_layout.addWidget(content_stacked_widget, 1) + + # Overview - create and attributes part + overview_widget = CreateOverviewWidget( + controller, content_stacked_widget + ) + + # Details - Publish details + publish_details_widget = PublishReportViewerWidget( + content_stacked_widget + ) + # Create publish frame publish_frame = PublishFrame(controller, content_stacked_widget) content_stacked_layout = QtWidgets.QStackedLayout( content_stacked_widget ) + content_stacked_layout.setContentsMargins(0, 0, 0, 0) content_stacked_layout.setStackingMode( QtWidgets.QStackedLayout.StackAll ) - content_stacked_layout.addWidget(create_overview_widget) + content_stacked_layout.addWidget(overview_widget) + content_stacked_layout.addWidget(publish_details_widget) content_stacked_layout.addWidget(publish_frame) # Add main frame to this window @@ -132,17 +154,17 @@ class PublisherWindow(QtWidgets.QDialog): main_layout.setSpacing(0) main_layout.addWidget(header_widget, 0) main_layout.addWidget(tabs_widget, 0) - main_layout.addWidget(content_stacked_widget, 1) + main_layout.addWidget(content_widget, 1) main_layout.addWidget(footer_widget, 0) tabs_widget.tab_changed.connect(self._on_tab_change) - create_overview_widget.active_changed.connect( + overview_widget.active_changed.connect( self._on_context_or_active_change ) - create_overview_widget.instance_context_changed.connect( + overview_widget.instance_context_changed.connect( self._on_context_or_active_change ) - create_overview_widget.create_requested.connect( + overview_widget.create_requested.connect( self._on_create_request ) @@ -164,18 +186,20 @@ class PublisherWindow(QtWidgets.QDialog): self._create_tab = create_tab self._content_stacked_widget = content_stacked_widget - self.content_stacked_layout = content_stacked_layout - self._create_overview_widget = create_overview_widget - self.publish_frame = publish_frame + self._content_stacked_layout = content_stacked_layout - self.context_label = context_label + self._overview_widget = overview_widget + self._publish_details_widget = publish_details_widget + self._publish_frame = publish_frame - self.comment_input = comment_input + self._context_label = context_label - self.stop_btn = stop_btn - self.reset_btn = reset_btn - self.validate_btn = validate_btn - self.publish_btn = publish_btn + self._comment_input = comment_input + + self._stop_btn = stop_btn + self._reset_btn = reset_btn + self._validate_btn = validate_btn + self._publish_btn = publish_btn self._controller = controller @@ -200,11 +224,23 @@ class PublisherWindow(QtWidgets.QDialog): self._controller.reset() def set_context_label(self, label): - self.context_label.setText(label) + self._context_label.setText(label) - def _on_tab_change(self, prev_tab, new_tab): + def _on_tab_change(self, old_tab, new_tab): if new_tab in ("create", "publish"): - self._create_overview_widget.set_state(prev_tab, new_tab) + animate = True + if old_tab not in ("create", "publish"): + animate = False + self._content_stacked_layout.setCurrentWidget( + self._overview_widget + ) + self._overview_widget.set_state(new_tab, animate) + + elif new_tab == "details": + self._content_stacked_layout.setCurrentWidget( + self._publish_details_widget + ) + # TODO handle rest of conditions @@ -219,10 +255,10 @@ class PublisherWindow(QtWidgets.QDialog): def _set_publish_visibility(self, visible): if visible: - widget = self.publish_frame + widget = self._publish_frame else: - widget = self._create_overview_widget - self.content_stacked_layout.setCurrentWidget(widget) + widget = self._overview_widget + self._content_stacked_layout.setCurrentWidget(widget) def _on_reset_clicked(self): self._controller.reset() @@ -234,7 +270,7 @@ class PublisherWindow(QtWidgets.QDialog): if self._controller.publish_comment_is_set: return - comment = self.comment_input.text() + comment = self._comment_input.text() self._controller.set_comment(comment) def _on_validate_clicked(self): @@ -248,40 +284,40 @@ class PublisherWindow(QtWidgets.QDialog): self._controller.publish() def _set_footer_enabled(self, enabled): - self.reset_btn.setEnabled(True) + self._reset_btn.setEnabled(True) if enabled: - self.stop_btn.setEnabled(False) - self.validate_btn.setEnabled(True) - self.publish_btn.setEnabled(True) + self._stop_btn.setEnabled(False) + self._validate_btn.setEnabled(True) + self._publish_btn.setEnabled(True) else: - self.stop_btn.setEnabled(enabled) - self.validate_btn.setEnabled(enabled) - self.publish_btn.setEnabled(enabled) + self._stop_btn.setEnabled(enabled) + self._validate_btn.setEnabled(enabled) + self._publish_btn.setEnabled(enabled) def _on_publish_reset(self): self._create_tab.setEnabled(True) - self.comment_input.setVisible(True) + self._comment_input.setVisible(True) self._set_publish_visibility(False) self._set_footer_enabled(False) def _on_publish_start(self): - self.reset_btn.setEnabled(False) - self.stop_btn.setEnabled(True) - self.validate_btn.setEnabled(False) - self.publish_btn.setEnabled(False) + self._reset_btn.setEnabled(False) + self._stop_btn.setEnabled(True) + self._validate_btn.setEnabled(False) + self._publish_btn.setEnabled(False) - self.comment_input.setVisible(False) + self._comment_input.setVisible(False) self._create_tab.setEnabled(False) if self._tabs_widget.is_current_tab(self._create_tab): self._tabs_widget.set_current_tab("publish") def _on_publish_validated(self): - self.validate_btn.setEnabled(False) + self._validate_btn.setEnabled(False) def _on_publish_stop(self): - self.reset_btn.setEnabled(True) - self.stop_btn.setEnabled(False) + self._reset_btn.setEnabled(True) + self._stop_btn.setEnabled(False) validate_enabled = not self._controller.publish_has_crashed publish_enabled = not self._controller.publish_has_crashed if validate_enabled: @@ -296,8 +332,8 @@ class PublisherWindow(QtWidgets.QDialog): else: publish_enabled = not self._controller.publish_has_finished - self.validate_btn.setEnabled(validate_enabled) - self.publish_btn.setEnabled(publish_enabled) + self._validate_btn.setEnabled(validate_enabled) + self._publish_btn.setEnabled(publish_enabled) def _validate_create_instances(self): if not self._controller.host_is_valid: From bd9a987f7ba9de82a75f3d6b8e3d0cfbcd1b872c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Sep 2022 18:33:22 +0200 Subject: [PATCH 11/72] update details in certain situations --- openpype/tools/publisher/widgets/tabs_widget.py | 15 +++++++++------ openpype/tools/publisher/window.py | 16 ++++++++++++++-- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/openpype/tools/publisher/widgets/tabs_widget.py b/openpype/tools/publisher/widgets/tabs_widget.py index bf3ef9eec0..84638a002c 100644 --- a/openpype/tools/publisher/widgets/tabs_widget.py +++ b/openpype/tools/publisher/widgets/tabs_widget.py @@ -50,13 +50,13 @@ class PublisherTabsWidget(QtWidgets.QFrame): self._btns_layout = btns_layout - self._current_button = None + self._current_identifier = None self._buttons_by_identifier = {} def is_current_tab(self, identifier): if isinstance(identifier, PublisherTabBtn): identifier = identifier.identifier - return self._current_button == identifier + return self._current_identifier == identifier def add_tab(self, label, identifier): button = PublisherTabBtn(identifier, label, self) @@ -64,7 +64,7 @@ class PublisherTabsWidget(QtWidgets.QFrame): self._btns_layout.addWidget(button, 0) self._buttons_by_identifier[identifier] = button - if self._current_button is None: + if self._current_identifier is None: self.set_current_tab(identifier) return button @@ -72,21 +72,24 @@ class PublisherTabsWidget(QtWidgets.QFrame): if isinstance(identifier, PublisherTabBtn): identifier = identifier.identifier - if identifier == self._current_button: + if identifier == self._current_identifier: return new_btn = self._buttons_by_identifier.get(identifier) if new_btn is None: return - old_identifier = self._current_button + old_identifier = self._current_identifier old_btn = self._buttons_by_identifier.get(old_identifier) - self._current_button = identifier + self._current_identifier = identifier if old_btn is not None: old_btn.deactivate() new_btn.activate() self.tab_changed.emit(old_identifier, identifier) + def current_tab(self): + return self._current_identifier + def _on_tab_click(self, identifier): self.set_current_tab(identifier) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 4aa02ff2d5..95c639a56c 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -226,7 +226,17 @@ class PublisherWindow(QtWidgets.QDialog): def set_context_label(self, label): self._context_label.setText(label) + def _update_publish_details_widget(self, force=False): + if not force and self._tabs_widget.current_tab() != "details": + return + + report_data = self.controller.get_publish_report() + self._publish_details_widget.set_report_data(report_data) + def _on_tab_change(self, old_tab, new_tab): + if old_tab == "details": + self._publish_details_widget.close_details_popup() + if new_tab in ("create", "publish"): animate = True if old_tab not in ("create", "publish"): @@ -240,7 +250,7 @@ class PublisherWindow(QtWidgets.QDialog): self._content_stacked_layout.setCurrentWidget( self._publish_details_widget ) - + self._update_publish_details_widget() # TODO handle rest of conditions @@ -298,8 +308,8 @@ class PublisherWindow(QtWidgets.QDialog): self._create_tab.setEnabled(True) self._comment_input.setVisible(True) self._set_publish_visibility(False) - self._set_footer_enabled(False) + self._update_publish_details_widget() def _on_publish_start(self): self._reset_btn.setEnabled(False) @@ -334,6 +344,7 @@ class PublisherWindow(QtWidgets.QDialog): self._validate_btn.setEnabled(validate_enabled) self._publish_btn.setEnabled(publish_enabled) + self._update_publish_details_widget() def _validate_create_instances(self): if not self._controller.host_is_valid: @@ -359,3 +370,4 @@ class PublisherWindow(QtWidgets.QDialog): context_title = self.controller.get_context_title() self.set_context_label(context_title) + self._update_publish_details_widget() From acb167c0d4b48afce082f6d76dad2ecf264b0096 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Sep 2022 18:45:48 +0200 Subject: [PATCH 12/72] added report page to window --- openpype/tools/publisher/widgets/__init__.py | 6 ++++-- .../publisher/widgets/overview_widget.py | 4 ++-- .../publisher/widgets/validations_widget.py | 5 ++--- openpype/tools/publisher/window.py | 19 ++++++++++++++++--- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/openpype/tools/publisher/widgets/__init__.py b/openpype/tools/publisher/widgets/__init__.py index f8e3c4b19b..81bb77ce89 100644 --- a/openpype/tools/publisher/widgets/__init__.py +++ b/openpype/tools/publisher/widgets/__init__.py @@ -11,7 +11,8 @@ from .widgets import ( ) from .publish_widget import PublishFrame from .tabs_widget import PublisherTabsWidget -from .overview_widget import CreateOverviewWidget +from .overview_widget import OverviewWidget +from .validations_widget import ValidationsWidget __all__ = ( @@ -27,5 +28,6 @@ __all__ = ( "PublishFrame", "PublisherTabsWidget", - "CreateOverviewWidget", + "OverviewWidget", + "ValidationsWidget", ) diff --git a/openpype/tools/publisher/widgets/overview_widget.py b/openpype/tools/publisher/widgets/overview_widget.py index 7afe02116f..90527234a7 100644 --- a/openpype/tools/publisher/widgets/overview_widget.py +++ b/openpype/tools/publisher/widgets/overview_widget.py @@ -13,13 +13,13 @@ from .widgets import ( from .create_widget import CreateWidget -class CreateOverviewWidget(QtWidgets.QFrame): +class OverviewWidget(QtWidgets.QFrame): active_changed = QtCore.Signal() instance_context_changed = QtCore.Signal() create_requested = QtCore.Signal() def __init__(self, controller, parent): - super(CreateOverviewWidget, self).__init__(parent) + super(OverviewWidget, self).__init__(parent) self._refreshing_instances = False self._controller = controller diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index e7ab4ecf5a..b70cd81878 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -400,7 +400,7 @@ class VerticallScrollArea(QtWidgets.QScrollArea): return super(VerticallScrollArea, self).eventFilter(obj, event) -class ValidationsWidget(QtWidgets.QWidget): +class ValidationsWidget(QtWidgets.QFrame): """Widgets showing validation error. This widget is shown if validation error/s happened during validation part. @@ -418,11 +418,10 @@ class ValidationsWidget(QtWidgets.QWidget): │ Publish buttons │ └───────────────────────────────┘ """ + def __init__(self, controller, parent): super(ValidationsWidget, self).__init__(parent) - self.setAttribute(QtCore.Qt.WA_TranslucentBackground) - errors_scroll = VerticallScrollArea(self) errors_scroll.setWidgetResizable(True) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 95c639a56c..568735f4ae 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -11,7 +11,8 @@ from openpype.tools.utils import ( from .publish_report_viewer import PublishReportViewerWidget from .control import PublisherController from .widgets import ( - CreateOverviewWidget, + OverviewWidget, + ValidationsWidget, PublishFrame, PublisherTabsWidget, @@ -124,10 +125,12 @@ class PublisherWindow(QtWidgets.QDialog): content_layout.addWidget(content_stacked_widget, 1) # Overview - create and attributes part - overview_widget = CreateOverviewWidget( + overview_widget = OverviewWidget( controller, content_stacked_widget ) + report_widget = ValidationsWidget(controller, parent) + # Details - Publish details publish_details_widget = PublishReportViewerWidget( content_stacked_widget @@ -145,6 +148,7 @@ class PublisherWindow(QtWidgets.QDialog): QtWidgets.QStackedLayout.StackAll ) content_stacked_layout.addWidget(overview_widget) + content_stacked_layout.addWidget(report_widget) content_stacked_layout.addWidget(publish_details_widget) content_stacked_layout.addWidget(publish_frame) @@ -189,6 +193,7 @@ class PublisherWindow(QtWidgets.QDialog): self._content_stacked_layout = content_stacked_layout self._overview_widget = overview_widget + self._report_widget = report_widget self._publish_details_widget = publish_details_widget self._publish_frame = publish_frame @@ -252,7 +257,10 @@ class PublisherWindow(QtWidgets.QDialog): ) self._update_publish_details_widget() - # TODO handle rest of conditions + elif new_tab == "report": + self._content_stacked_layout.setCurrentWidget( + self._report_widget + ) def _on_context_or_active_change(self): self._validate_create_instances() @@ -319,6 +327,8 @@ class PublisherWindow(QtWidgets.QDialog): self._comment_input.setVisible(False) self._create_tab.setEnabled(False) + + self._report_widget.clear() if self._tabs_widget.is_current_tab(self._create_tab): self._tabs_widget.set_current_tab("publish") @@ -346,6 +356,9 @@ class PublisherWindow(QtWidgets.QDialog): self._publish_btn.setEnabled(publish_enabled) self._update_publish_details_widget() + validation_errors = self._controller.get_validation_errors() + self._report_widget.set_errors(validation_errors) + def _validate_create_instances(self): if not self._controller.host_is_valid: self._set_footer_enabled(True) From 3f4d59b54667b9c0fbda928173793ea31e599e9c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Sep 2022 18:45:58 +0200 Subject: [PATCH 13/72] removed report and validation from publish frame --- .../tools/publisher/widgets/publish_widget.py | 41 ++----------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/openpype/tools/publisher/widgets/publish_widget.py b/openpype/tools/publisher/widgets/publish_widget.py index b32b5381d1..ea23f9c42c 100644 --- a/openpype/tools/publisher/widgets/publish_widget.py +++ b/openpype/tools/publisher/widgets/publish_widget.py @@ -6,8 +6,6 @@ from Qt import QtWidgets, QtCore, QtGui from openpype.pipeline import KnownPublishError -from .validations_widget import ValidationsWidget -from ..publish_report_viewer import PublishReportViewerWidget from .widgets import ( StopBtn, ResetBtn, @@ -101,9 +99,6 @@ class PublishFrame(QtWidgets.QFrame): self.setObjectName("PublishFrame") - # Widget showing validation errors. Their details and action callbacks. - validation_errors_widget = ValidationsWidget(controller, self) - # Bottom part of widget where process and callback buttons are showed # - QFrame used to be able set background using stylesheets easily # and not override all children widgets style @@ -203,17 +198,15 @@ class PublishFrame(QtWidgets.QFrame): publish_widget = QtWidgets.QWidget(self) publish_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) publish_layout = QtWidgets.QVBoxLayout(publish_widget) - publish_layout.addWidget(validation_errors_widget, 1) + publish_layout.addStretch(1) publish_layout.addWidget(info_frame, 0) details_widget = QtWidgets.QWidget(self) - report_view = PublishReportViewerWidget(details_widget) close_report_btn = QtWidgets.QPushButton(details_widget) close_report_icon = self._get_report_close_icon() close_report_btn.setIcon(close_report_icon) details_layout = QtWidgets.QVBoxLayout(details_widget) - details_layout.addWidget(report_view) details_layout.addWidget(close_report_btn) main_layout = QtWidgets.QStackedLayout(self) @@ -224,8 +217,6 @@ class PublishFrame(QtWidgets.QFrame): main_layout.setCurrentWidget(publish_widget) - show_details_btn.clicked.connect(self._on_show_details) - copy_report_btn.clicked.connect(self._on_copy_report) export_report_btn.clicked.connect(self._on_export_report) @@ -234,8 +225,6 @@ class PublishFrame(QtWidgets.QFrame): validate_btn.clicked.connect(self._on_validate_clicked) publish_btn.clicked.connect(self._on_publish_clicked) - close_report_btn.clicked.connect(self._on_close_report_clicked) - controller.add_publish_reset_callback(self._on_publish_reset) controller.add_publish_started_callback(self._on_publish_start) controller.add_publish_validated_callback(self._on_publish_validated) @@ -249,8 +238,6 @@ class PublishFrame(QtWidgets.QFrame): self._info_frame = info_frame self._publish_widget = publish_widget - self._validation_errors_widget = validation_errors_widget - self._main_layout = main_layout self._main_label = main_label @@ -269,7 +256,6 @@ class PublishFrame(QtWidgets.QFrame): self._publish_btn = publish_btn self._details_widget = details_widget - self._report_view = report_view def _get_report_close_icon(self): size = 100 @@ -314,8 +300,6 @@ class PublishFrame(QtWidgets.QFrame): self._progress_widget.setMaximum(self.controller.publish_max_progress) def _on_publish_start(self): - self._validation_errors_widget.clear() - self._set_success_property(-1) self._change_bg_property() self._set_progress_visibility(True) @@ -388,7 +372,7 @@ class PublishFrame(QtWidgets.QFrame): elif validation_errors: self._set_progress_visibility(False) self._change_bg_property(1) - self._set_validation_errors(validation_errors) + self._set_validation_errors() elif self.controller.publish_has_finished: self._set_finished() @@ -421,14 +405,12 @@ class PublishFrame(QtWidgets.QFrame): self._message_label_bottom.setText("") self._set_success_property(0) - def _set_validation_errors(self, validation_errors): + def _set_validation_errors(self): self._main_label.setText("Your publish didn't pass studio validations") self._message_label_top.setText("") self._message_label_bottom.setText("Check results above please") self._set_success_property(2) - self._validation_errors_widget.set_errors(validation_errors) - def _set_finished(self): self._main_label.setText("Finished") self._message_label_top.setText("") @@ -489,23 +471,6 @@ class PublishFrame(QtWidgets.QFrame): with open(full_path, "w") as file_stream: json.dump(logs, file_stream) - def _on_show_details(self): - self._change_bg_property(2) - self._main_layout.setCurrentWidget(self._details_widget) - report_data = self.controller.get_publish_report() - self._report_view.set_report_data(report_data) - - def _on_close_report_clicked(self): - self._report_view.close_details_popup() - if self.controller.get_publish_crash_error(): - self._change_bg_property() - - elif self.controller.get_validation_errors(): - self._change_bg_property(1) - else: - self._change_bg_property(2) - self._main_layout.setCurrentWidget(self._publish_widget) - def _on_reset_clicked(self): self.controller.reset() From 0c86e157f98544a5f57b80b0b59450f51ed5a49a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Sep 2022 12:37:55 +0200 Subject: [PATCH 14/72] disable attributes after publish start --- openpype/tools/publisher/widgets/overview_widget.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/tools/publisher/widgets/overview_widget.py b/openpype/tools/publisher/widgets/overview_widget.py index 90527234a7..99b9d88007 100644 --- a/openpype/tools/publisher/widgets/overview_widget.py +++ b/openpype/tools/publisher/widgets/overview_widget.py @@ -38,6 +38,7 @@ class OverviewWidget(QtWidgets.QFrame): subset_views_layout = QtWidgets.QStackedLayout() subset_views_layout.addWidget(subset_view_cards) subset_views_layout.addWidget(subset_list_view) + subset_views_layout.setCurrentWidget(subset_view_cards) # Buttons at the bottom of subset view create_btn = CreateInstanceBtn(self) @@ -113,6 +114,7 @@ class OverviewWidget(QtWidgets.QFrame): ) # --- Controller callbacks --- + controller.add_publish_started_callback(self._on_publish_start) controller.add_publish_reset_callback(self._on_publish_reset) controller.add_instances_refresh_callback(self._on_instances_refresh) @@ -252,9 +254,15 @@ class OverviewWidget(QtWidgets.QFrame): # Force to change instance and refresh details self._on_subset_change() + def _on_publish_start(self): + """Publish started.""" + + self._subset_attributes_wrap.setEnabled(False) + def _on_publish_reset(self): """Context in controller has been refreshed.""" + self._subset_attributes_wrap.setEnabled(True) self._subset_content_widget.setEnabled(self._controller.host_is_valid) def _on_instances_refresh(self): From a2a686cfb86156b4bb12ed8256e572867a3e3a53 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Sep 2022 12:38:50 +0200 Subject: [PATCH 15/72] added base of validation labels showed in different stages of publishing --- .../publisher/widgets/validations_widget.py | 136 ++++++++++++++++-- 1 file changed, 124 insertions(+), 12 deletions(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index b70cd81878..b21cb3bb1b 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -414,15 +414,76 @@ class ValidationsWidget(QtWidgets.QFrame): │ │ Error detail │ │ │ │ │ │ │ │ │ │ - ├──────┴────────────────┴───────┤ - │ Publish buttons │ - └───────────────────────────────┘ + └──────┴────────────────┴───────┘ """ def __init__(self, controller, parent): super(ValidationsWidget, self).__init__(parent) - errors_scroll = VerticallScrollArea(self) + # Before publishing + before_publish_widget = QtWidgets.QWidget(self) + before_publish_label = QtWidgets.QLabel( + "Nothing to report until you run publish", + before_publish_widget + ) + before_publish_label.setAlignment(QtCore.Qt.AlignCenter) + before_publish_layout = QtWidgets.QHBoxLayout(before_publish_widget) + before_publish_layout.setContentsMargins(0, 0, 0, 0) + before_publish_layout.addWidget( + before_publish_label, 1, QtCore.Qt.AlignCenter + ) + + # After success publishing + publish_started_widget = QtWidgets.QWidget(self) + publish_started_label = QtWidgets.QLabel( + "Publishing run smoothly", + publish_started_widget + ) + publish_started_label.setAlignment(QtCore.Qt.AlignCenter) + publish_started_layout = QtWidgets.QHBoxLayout( + publish_started_widget + ) + publish_started_layout.setContentsMargins(0, 0, 0, 0) + publish_started_layout.addWidget( + publish_started_label, 1, QtCore.Qt.AlignCenter + ) + + # After success publishing + publish_stop_ok_widget = QtWidgets.QWidget(self) + publish_stop_ok_label = QtWidgets.QLabel( + "Publishing finished successfully", + publish_stop_ok_widget + ) + publish_stop_ok_label.setAlignment(QtCore.Qt.AlignCenter) + publish_stop_ok_layout = QtWidgets.QHBoxLayout( + publish_stop_ok_widget + ) + publish_stop_ok_layout.setContentsMargins(0, 0, 0, 0) + publish_stop_ok_layout.addWidget( + publish_stop_ok_label, 1, QtCore.Qt.AlignCenter + ) + + # After failed publishing (not with validation error) + publish_stop_fail_widget = QtWidgets.QWidget(self) + publish_stop_fail_label = QtWidgets.QLabel( + "This is not your fault", + publish_stop_fail_widget + ) + publish_stop_fail_label.setAlignment(QtCore.Qt.AlignCenter) + publish_stop_fail_layout = QtWidgets.QHBoxLayout( + publish_stop_fail_widget + ) + publish_stop_fail_layout.setContentsMargins(0, 0, 0, 0) + publish_stop_fail_layout.addWidget( + publish_stop_fail_label, 1, QtCore.Qt.AlignCenter + ) + + # Validation errors + validations_widget = QtWidgets.QWidget(self) + + content_widget = QtWidgets.QWidget(validations_widget) + + errors_scroll = VerticallScrollArea(content_widget) errors_scroll.setWidgetResizable(True) errors_widget = QtWidgets.QWidget(errors_scroll) @@ -432,35 +493,58 @@ class ValidationsWidget(QtWidgets.QFrame): errors_scroll.setWidget(errors_widget) - error_details_frame = QtWidgets.QFrame(self) + error_details_frame = QtWidgets.QFrame(content_widget) error_details_input = QtWidgets.QTextEdit(error_details_frame) error_details_input.setObjectName("InfoText") error_details_input.setTextInteractionFlags( QtCore.Qt.TextBrowserInteraction ) - actions_widget = ValidateActionsWidget(controller, self) + actions_widget = ValidateActionsWidget(controller, content_widget) actions_widget.setMinimumWidth(140) error_details_layout = QtWidgets.QHBoxLayout(error_details_frame) error_details_layout.addWidget(error_details_input, 1) error_details_layout.addWidget(actions_widget, 0) - content_layout = QtWidgets.QHBoxLayout() + content_layout = QtWidgets.QHBoxLayout(content_widget) content_layout.setSpacing(0) content_layout.setContentsMargins(0, 0, 0, 0) content_layout.addWidget(errors_scroll, 0) content_layout.addWidget(error_details_frame, 1) - top_label = QtWidgets.QLabel("Publish validation report", self) + top_label = QtWidgets.QLabel( + "Publish validation report", content_widget + ) top_label.setObjectName("PublishInfoMainLabel") top_label.setAlignment(QtCore.Qt.AlignCenter) - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(top_label) - layout.addLayout(content_layout) + validation_layout = QtWidgets.QVBoxLayout(validations_widget) + validation_layout.setContentsMargins(0, 0, 0, 0) + validation_layout.addWidget(top_label, 0) + validation_layout.addWidget(content_widget, 1) + + main_layout = QtWidgets.QStackedLayout(self) + main_layout.addWidget(before_publish_widget) + main_layout.addWidget(publish_started_widget) + main_layout.addWidget(publish_stop_ok_widget) + main_layout.addWidget(publish_stop_fail_widget) + main_layout.addWidget(validations_widget) + + main_layout.setCurrentWidget(before_publish_widget) + + controller.add_publish_started_callback(self._on_publish_start) + controller.add_publish_reset_callback(self._on_publish_reset) + controller.add_publish_stopped_callback(self._on_publish_stop) + + self._main_layout = main_layout + + self._before_publish_widget = before_publish_widget + self._publish_started_widget = publish_started_widget + self._publish_stop_ok_widget = publish_stop_ok_widget + self._publish_stop_fail_widget = publish_stop_fail_widget + self._validations_widget = validations_widget self._top_label = top_label self._errors_widget = errors_widget @@ -473,6 +557,8 @@ class ValidationsWidget(QtWidgets.QFrame): self._error_info = {} self._previous_select = None + self._controller = controller + def clear(self): """Delete all dynamic widgets and hide all wrappers.""" self._title_widgets = {} @@ -536,6 +622,32 @@ class ValidationsWidget(QtWidgets.QFrame): self.updateGeometry() + def _set_current_widget(self, widget): + self._main_layout.setCurrentWidget(widget) + + def _on_publish_start(self): + self._set_current_widget(self._publish_started_widget) + + def _on_publish_reset(self): + self._set_current_widget(self._before_publish_widget) + + def _on_publish_stop(self): + if self._controller.publish_has_crashed: + self._set_current_widget(self._publish_stop_fail_widget) + return + + if self._controller.publish_has_validation_errors: + validation_errors = self._controller.get_validation_errors() + self._set_current_widget(self._validations_widget) + self.set_errors(validation_errors) + return + + if self._contoller.publish_has_finished: + self._set_current_widget(self._publish_stop_ok_widget) + return + + self._set_current_widget(self._publish_started_widget) + def _on_select(self, index): if self._previous_select: if self._previous_select.index == index: From e725246c31acb948c0075aff4c33a4cac7ebe921 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 29 Sep 2022 13:49:39 +0200 Subject: [PATCH 16/72] publish frame is showed in a different way --- openpype/style/style.css | 10 - .../tools/publisher/widgets/publish_widget.py | 188 ++++++------------ openpype/tools/publisher/widgets/widgets.py | 43 ++-- openpype/tools/publisher/window.py | 141 ++++++++++--- 4 files changed, 194 insertions(+), 188 deletions(-) diff --git a/openpype/style/style.css b/openpype/style/style.css index 1d112fa575..9dbc6b2adc 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -971,16 +971,6 @@ VariantInputsWidget QToolButton { color: {color:publisher:error}; } -#PublishFrame { - background: rgba(0, 0, 0, 127); -} -#PublishFrame[state="1"] { - background: rgb(22, 25, 29); -} -#PublishFrame[state="2"] { - background: {color:bg}; -} - #PublishInfoFrame { background: {color:bg}; border: 2px solid black; diff --git a/openpype/tools/publisher/widgets/publish_widget.py b/openpype/tools/publisher/widgets/publish_widget.py index ea23f9c42c..8a8c25e1b6 100644 --- a/openpype/tools/publisher/widgets/publish_widget.py +++ b/openpype/tools/publisher/widgets/publish_widget.py @@ -2,7 +2,7 @@ import os import json import time -from Qt import QtWidgets, QtCore, QtGui +from Qt import QtWidgets, QtCore from openpype.pipeline import KnownPublishError @@ -11,9 +11,7 @@ from .widgets import ( ResetBtn, ValidateBtn, PublishBtn, - CopyPublishReportBtn, - SavePublishReportBtn, - ShowPublishReportBtn + PublishReportBtn, ) @@ -66,7 +64,7 @@ class ActionsButton(QtWidgets.QToolButton): self._set_action(action) -class PublishFrame(QtWidgets.QFrame): +class PublishFrame(QtWidgets.QWidget): """Frame showed during publishing. Shows all information related to publishing. Contains validation error @@ -77,65 +75,49 @@ class PublishFrame(QtWidgets.QFrame): only when publishing process is stopped and must be manually triggered to change into that layer. - +------------------------------------------------------------------------+ - | | - | | - | | - | < Validation error widget > | - | | - | | - | | - | | +------------------------------------------------------------------------+ | < Main label > | | < Label top > | | (#### 10% ) | | | - | Report: