Merge pull request #3936 from pypeclub/feature/OP-4158_Creation-UI-is-part-of-main-window
Publisher: Create dialog is part of main window
|
|
@ -312,6 +312,8 @@ class IPublishHost:
|
|||
required = [
|
||||
"get_context_data",
|
||||
"update_context_data",
|
||||
"get_context_title",
|
||||
"get_current_context",
|
||||
]
|
||||
missing = []
|
||||
for name in required:
|
||||
|
|
|
|||
|
|
@ -89,8 +89,10 @@
|
|||
},
|
||||
"publisher": {
|
||||
"error": "#AA5050",
|
||||
"crash": "#FF6432",
|
||||
"success": "#458056",
|
||||
"warning": "#ffc671",
|
||||
"tab-bg": "#16191d",
|
||||
"list-view-group": {
|
||||
"bg": "#434a56",
|
||||
"bg-hover": "rgba(168, 175, 189, 0.3)",
|
||||
|
|
|
|||
|
|
@ -856,6 +856,33 @@ 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:disabled {
|
||||
background: {color:bg-inputs};
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -865,18 +892,16 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
|||
}
|
||||
|
||||
#CreateDialogHelpButton {
|
||||
background: rgba(255, 255, 255, 31);
|
||||
background: {color:bg-buttons};
|
||||
border-top-left-radius: 0.2em;
|
||||
border-bottom-left-radius: 0.2em;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#CreateDialogHelpButton:hover {
|
||||
background: rgba(255, 255, 255, 63);
|
||||
background: {color:bg-button-hover};
|
||||
}
|
||||
#CreateDialogHelpButton QWidget {
|
||||
background: transparent;
|
||||
|
|
@ -944,19 +969,8 @@ 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;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
|
|
@ -965,7 +979,7 @@ VariantInputsWidget QToolButton {
|
|||
}
|
||||
|
||||
#PublishInfoFrame[state="0"] {
|
||||
background: {color:publisher:error};
|
||||
background: {color:publisher:crash};
|
||||
}
|
||||
|
||||
#PublishInfoFrame[state="1"] {
|
||||
|
|
@ -989,6 +1003,11 @@ VariantInputsWidget QToolButton {
|
|||
font-size: 13pt;
|
||||
}
|
||||
|
||||
ValidationArtistMessage QLabel {
|
||||
font-size: 20pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#ValidationActionButton {
|
||||
border-radius: 0.2em;
|
||||
padding: 4px 6px 4px 6px;
|
||||
|
|
@ -1005,17 +1024,16 @@ VariantInputsWidget QToolButton {
|
|||
}
|
||||
|
||||
#ValidationErrorTitleFrame {
|
||||
background: {color:bg-inputs};
|
||||
border-left: 4px solid transparent;
|
||||
border-radius: 0.2em;
|
||||
background: {color:bg-buttons};
|
||||
}
|
||||
|
||||
#ValidationErrorTitleFrame:hover {
|
||||
border-left-color: {color:border};
|
||||
background: {color:bg-buttons-hover};
|
||||
}
|
||||
|
||||
#ValidationErrorTitleFrame[selected="1"] {
|
||||
background: {color:bg};
|
||||
border-left-color: {palette:blue-light};
|
||||
background: {color:bg-view-selection};
|
||||
}
|
||||
|
||||
#ValidationErrorInstanceList {
|
||||
|
|
|
|||
|
|
@ -1,22 +1,17 @@
|
|||
import os
|
||||
import copy
|
||||
import inspect
|
||||
import logging
|
||||
import traceback
|
||||
import collections
|
||||
|
||||
import weakref
|
||||
try:
|
||||
from weakref import WeakMethod
|
||||
except Exception:
|
||||
from openpype.lib.python_2_comp import WeakMethod
|
||||
|
||||
import pyblish.api
|
||||
|
||||
from openpype.client import get_assets
|
||||
from openpype.lib.events import EventSystem
|
||||
from openpype.pipeline import (
|
||||
PublishValidationError,
|
||||
registered_host,
|
||||
legacy_io,
|
||||
)
|
||||
from openpype.pipeline.create import CreateContext
|
||||
|
||||
|
|
@ -107,17 +102,13 @@ class AssetDocsCache:
|
|||
self._asset_docs = None
|
||||
self._task_names_by_asset_name = {}
|
||||
|
||||
@property
|
||||
def dbcon(self):
|
||||
return self._controller.dbcon
|
||||
|
||||
def reset(self):
|
||||
self._asset_docs = None
|
||||
self._task_names_by_asset_name = {}
|
||||
|
||||
def _query(self):
|
||||
if self._asset_docs is None:
|
||||
project_name = self.dbcon.active_project()
|
||||
project_name = self._controller.project_name
|
||||
asset_docs = get_assets(
|
||||
project_name, fields=self.projection.keys()
|
||||
)
|
||||
|
|
@ -360,11 +351,15 @@ class PublisherController:
|
|||
dbcon (AvalonMongoDB): Connection to mongo with context.
|
||||
headless (bool): Headless publishing. ATM not implemented or used.
|
||||
"""
|
||||
|
||||
def __init__(self, dbcon=None, headless=False):
|
||||
self.log = logging.getLogger("PublisherController")
|
||||
self.host = registered_host()
|
||||
self.headless = headless
|
||||
|
||||
# Inner event system of controller
|
||||
self._event_system = EventSystem()
|
||||
|
||||
self.create_context = CreateContext(
|
||||
self.host, dbcon, headless=headless, reset=False
|
||||
)
|
||||
|
|
@ -405,18 +400,6 @@ class PublisherController:
|
|||
# Plugin iterator
|
||||
self._main_thread_iter = None
|
||||
|
||||
# Variables where callbacks are stored
|
||||
self._instances_refresh_callback_refs = set()
|
||||
self._plugins_refresh_callback_refs = set()
|
||||
|
||||
self._publish_reset_callback_refs = set()
|
||||
self._publish_started_callback_refs = set()
|
||||
self._publish_validated_callback_refs = set()
|
||||
self._publish_stopped_callback_refs = set()
|
||||
|
||||
self._publish_instance_changed_callback_refs = set()
|
||||
self._publish_plugin_changed_callback_refs = set()
|
||||
|
||||
# State flags to prevent executing method which is already in progress
|
||||
self._resetting_plugins = False
|
||||
self._resetting_instances = False
|
||||
|
|
@ -426,13 +409,42 @@ class PublisherController:
|
|||
|
||||
@property
|
||||
def project_name(self):
|
||||
"""Current project context."""
|
||||
return self.dbcon.Session["AVALON_PROJECT"]
|
||||
"""Current project context defined by host.
|
||||
|
||||
Returns:
|
||||
str: Project name.
|
||||
"""
|
||||
|
||||
if not hasattr(self.host, "get_current_context"):
|
||||
return legacy_io.active_project()
|
||||
|
||||
return self.host.get_current_context()["project_name"]
|
||||
|
||||
@property
|
||||
def dbcon(self):
|
||||
"""Pointer to AvalonMongoDB in creator context."""
|
||||
return self.create_context.dbcon
|
||||
def current_asset_name(self):
|
||||
"""Current context asset name defined by host.
|
||||
|
||||
Returns:
|
||||
Union[str, None]: Asset name or None if asset is not set.
|
||||
"""
|
||||
|
||||
if not hasattr(self.host, "get_current_context"):
|
||||
return legacy_io.Session["AVALON_ASSET"]
|
||||
|
||||
return self.host.get_current_context()["asset_name"]
|
||||
|
||||
@property
|
||||
def current_task_name(self):
|
||||
"""Current context task name defined by host.
|
||||
|
||||
Returns:
|
||||
Union[str, None]: Task name or None if task is not set.
|
||||
"""
|
||||
|
||||
if not hasattr(self.host, "get_current_context"):
|
||||
return legacy_io.Session["AVALON_TASK"]
|
||||
|
||||
return self.host.get_current_context()["task_name"]
|
||||
|
||||
@property
|
||||
def instances(self):
|
||||
|
|
@ -464,58 +476,35 @@ class PublisherController:
|
|||
"""Publish plugins with possible attribute definitions."""
|
||||
return self.create_context.plugins_with_defs
|
||||
|
||||
def _create_reference(self, callback):
|
||||
if inspect.ismethod(callback):
|
||||
ref = WeakMethod(callback)
|
||||
elif callable(callback):
|
||||
ref = weakref.ref(callback)
|
||||
else:
|
||||
raise TypeError("Expected function or method got {}".format(
|
||||
str(type(callback))
|
||||
))
|
||||
return ref
|
||||
@property
|
||||
def event_system(self):
|
||||
"""Inner event system for publisher controller.
|
||||
|
||||
def add_instances_refresh_callback(self, callback):
|
||||
"""Callbacks triggered on instances refresh."""
|
||||
ref = self._create_reference(callback)
|
||||
self._instances_refresh_callback_refs.add(ref)
|
||||
Known topics:
|
||||
"show.detailed.help" - Detailed help requested (UI related).
|
||||
"show.card.message" - Show card message request (UI related).
|
||||
"instances.refresh.finished" - Instances are refreshed.
|
||||
"plugins.refresh.finished" - Plugins refreshed.
|
||||
"publish.reset.finished" - Controller reset finished.
|
||||
"publish.process.started" - Publishing started. Can be started from
|
||||
paused state.
|
||||
"publish.process.validated" - Publishing passed validation.
|
||||
"publish.process.stopped" - Publishing stopped/paused process.
|
||||
"publish.process.plugin.changed" - Plugin state has changed.
|
||||
"publish.process.instance.changed" - Instance state has changed.
|
||||
|
||||
def add_plugins_refresh_callback(self, callback):
|
||||
"""Callbacks triggered on plugins refresh."""
|
||||
ref = self._create_reference(callback)
|
||||
self._plugins_refresh_callback_refs.add(ref)
|
||||
Returns:
|
||||
EventSystem: Event system which can trigger callbacks for topics.
|
||||
"""
|
||||
|
||||
return self._event_system
|
||||
|
||||
def _emit_event(self, topic, data=None):
|
||||
if data is None:
|
||||
data = {}
|
||||
self._event_system.emit(topic, data, "controller")
|
||||
|
||||
# --- Publish specific callbacks ---
|
||||
def add_publish_reset_callback(self, callback):
|
||||
"""Callbacks triggered on publishing reset."""
|
||||
ref = self._create_reference(callback)
|
||||
self._publish_reset_callback_refs.add(ref)
|
||||
|
||||
def add_publish_started_callback(self, callback):
|
||||
"""Callbacks triggered on publishing start."""
|
||||
ref = self._create_reference(callback)
|
||||
self._publish_started_callback_refs.add(ref)
|
||||
|
||||
def add_publish_validated_callback(self, callback):
|
||||
"""Callbacks triggered on passing last possible validation order."""
|
||||
ref = self._create_reference(callback)
|
||||
self._publish_validated_callback_refs.add(ref)
|
||||
|
||||
def add_instance_change_callback(self, callback):
|
||||
"""Callbacks triggered before next publish instance process."""
|
||||
ref = self._create_reference(callback)
|
||||
self._publish_instance_changed_callback_refs.add(ref)
|
||||
|
||||
def add_plugin_change_callback(self, callback):
|
||||
"""Callbacks triggered before next plugin processing."""
|
||||
ref = self._create_reference(callback)
|
||||
self._publish_plugin_changed_callback_refs.add(ref)
|
||||
|
||||
def add_publish_stopped_callback(self, callback):
|
||||
"""Callbacks triggered on publishing stop (any reason)."""
|
||||
ref = self._create_reference(callback)
|
||||
self._publish_stopped_callback_refs.add(ref)
|
||||
|
||||
def get_asset_docs(self):
|
||||
"""Get asset documents from cache for whole project."""
|
||||
return self._asset_docs_cache.get_asset_docs()
|
||||
|
|
@ -556,20 +545,6 @@ class PublisherController:
|
|||
)
|
||||
return result
|
||||
|
||||
def _trigger_callbacks(self, callbacks, *args, **kwargs):
|
||||
"""Helper method to trigger callbacks stored by their rerence."""
|
||||
# Trigger reset callbacks
|
||||
to_remove = set()
|
||||
for ref in callbacks:
|
||||
callback = ref()
|
||||
if callback:
|
||||
callback(*args, **kwargs)
|
||||
else:
|
||||
to_remove.add(ref)
|
||||
|
||||
for ref in to_remove:
|
||||
callbacks.remove(ref)
|
||||
|
||||
def reset(self):
|
||||
"""Reset everything related to creation and publishing."""
|
||||
# Stop publishing
|
||||
|
|
@ -585,6 +560,8 @@ class PublisherController:
|
|||
self._reset_publish()
|
||||
self._reset_instances()
|
||||
|
||||
self.emit_card_message("Refreshed..")
|
||||
|
||||
def _reset_plugins(self):
|
||||
"""Reset to initial state."""
|
||||
if self._resetting_plugins:
|
||||
|
|
@ -596,7 +573,7 @@ class PublisherController:
|
|||
|
||||
self._resetting_plugins = False
|
||||
|
||||
self._trigger_callbacks(self._plugins_refresh_callback_refs)
|
||||
self._emit_event("plugins.refresh.finished")
|
||||
|
||||
def _reset_instances(self):
|
||||
"""Reset create instances."""
|
||||
|
|
@ -612,7 +589,10 @@ class PublisherController:
|
|||
|
||||
self._resetting_instances = False
|
||||
|
||||
self._trigger_callbacks(self._instances_refresh_callback_refs)
|
||||
self._emit_event("instances.refresh.finished")
|
||||
|
||||
def emit_card_message(self, message):
|
||||
self._emit_event("show.card.message", {"message": message})
|
||||
|
||||
def get_creator_attribute_definitions(self, instances):
|
||||
"""Collect creator attribute definitions for multuple instances.
|
||||
|
|
@ -709,7 +689,7 @@ class PublisherController:
|
|||
creator = self.creators[creator_identifier]
|
||||
creator.create(subset_name, instance_data, options)
|
||||
|
||||
self._trigger_callbacks(self._instances_refresh_callback_refs)
|
||||
self._emit_event("instances.refresh.finished")
|
||||
|
||||
def save_changes(self):
|
||||
"""Save changes happened during creation."""
|
||||
|
|
@ -724,7 +704,7 @@ class PublisherController:
|
|||
|
||||
self.create_context.remove_instances(instances)
|
||||
|
||||
self._trigger_callbacks(self._instances_refresh_callback_refs)
|
||||
self._emit_event("instances.refresh.finished")
|
||||
|
||||
# --- Publish specific implementations ---
|
||||
@property
|
||||
|
|
@ -793,7 +773,7 @@ class PublisherController:
|
|||
self._publish_max_progress = len(self.publish_plugins)
|
||||
self._publish_progress = 0
|
||||
|
||||
self._trigger_callbacks(self._publish_reset_callback_refs)
|
||||
self._emit_event("publish.reset.finished")
|
||||
|
||||
def set_comment(self, comment):
|
||||
self._publish_context.data["comment"] = comment
|
||||
|
|
@ -820,7 +800,8 @@ class PublisherController:
|
|||
self.save_changes()
|
||||
|
||||
self._publish_is_running = True
|
||||
self._trigger_callbacks(self._publish_started_callback_refs)
|
||||
|
||||
self._emit_event("publish.process.started")
|
||||
self._main_thread_processor.start()
|
||||
self._publish_next_process()
|
||||
|
||||
|
|
@ -828,10 +809,12 @@ class PublisherController:
|
|||
"""Stop or pause publishing."""
|
||||
self._publish_is_running = False
|
||||
self._main_thread_processor.stop()
|
||||
self._trigger_callbacks(self._publish_stopped_callback_refs)
|
||||
|
||||
self._emit_event("publish.process.stopped")
|
||||
|
||||
def stop_publish(self):
|
||||
"""Stop publishing process (any reason)."""
|
||||
|
||||
if self._publish_is_running:
|
||||
self._stop_publish()
|
||||
|
||||
|
|
@ -892,9 +875,7 @@ class PublisherController:
|
|||
)
|
||||
# Trigger callbacks when validation stage is passed
|
||||
if self._publish_validated:
|
||||
self._trigger_callbacks(
|
||||
self._publish_validated_callback_refs
|
||||
)
|
||||
self._emit_event("publish.process.validated")
|
||||
|
||||
# Stop if plugin is over validation order and process
|
||||
# should process up to validation.
|
||||
|
|
@ -912,9 +893,14 @@ class PublisherController:
|
|||
self._publish_report.add_plugin_iter(plugin, self._publish_context)
|
||||
|
||||
# Trigger callback that new plugin is going to be processed
|
||||
self._trigger_callbacks(
|
||||
self._publish_plugin_changed_callback_refs, plugin
|
||||
plugin_label = plugin.__name__
|
||||
if hasattr(plugin, "label") and plugin.label:
|
||||
plugin_label = plugin.label
|
||||
self._emit_event(
|
||||
"publish.process.plugin.changed",
|
||||
{"plugin_label": plugin_label}
|
||||
)
|
||||
|
||||
# Plugin is instance plugin
|
||||
if plugin.__instanceEnabled__:
|
||||
instances = pyblish.logic.instances_by_plugin(
|
||||
|
|
@ -928,11 +914,15 @@ class PublisherController:
|
|||
if instance.data.get("publish") is False:
|
||||
continue
|
||||
|
||||
self._trigger_callbacks(
|
||||
self._publish_instance_changed_callback_refs,
|
||||
self._publish_context,
|
||||
instance
|
||||
instance_label = (
|
||||
instance.data.get("label")
|
||||
or instance.data["name"]
|
||||
)
|
||||
self._emit_event(
|
||||
"publish.process.instance.changed",
|
||||
{"instance_label": instance_label}
|
||||
)
|
||||
|
||||
yield MainThreadItem(
|
||||
self._process_and_continue, plugin, instance
|
||||
)
|
||||
|
|
@ -944,10 +934,14 @@ class PublisherController:
|
|||
[plugin], families
|
||||
)
|
||||
if plugins:
|
||||
self._trigger_callbacks(
|
||||
self._publish_instance_changed_callback_refs,
|
||||
self._publish_context,
|
||||
None
|
||||
instance_label = (
|
||||
self._publish_context.data.get("label")
|
||||
or self._publish_context.data.get("name")
|
||||
or "Context"
|
||||
)
|
||||
self._emit_event(
|
||||
"publish.process.instance.changed",
|
||||
{"instance_label": instance_label}
|
||||
)
|
||||
yield MainThreadItem(
|
||||
self._process_and_continue, plugin, None
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,35 +3,20 @@ 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 .help_widget import (
|
||||
HelpButton,
|
||||
HelpDialog,
|
||||
)
|
||||
from .publish_frame import PublishFrame
|
||||
from .tabs_widget import PublisherTabsWidget
|
||||
from .overview_widget import OverviewWidget
|
||||
from .validations_widget import ValidationsWidget
|
||||
|
||||
|
||||
__all__ = (
|
||||
|
|
@ -39,22 +24,17 @@ __all__ = (
|
|||
"get_pixmap",
|
||||
"get_icon",
|
||||
|
||||
"SubsetAttributesWidget",
|
||||
"BorderedLabelWidget",
|
||||
|
||||
"StopBtn",
|
||||
"ResetBtn",
|
||||
"ValidateBtn",
|
||||
"PublishBtn",
|
||||
|
||||
"CreateInstanceBtn",
|
||||
"RemoveInstanceBtn",
|
||||
"ChangeViewBtn",
|
||||
"HelpButton",
|
||||
"HelpDialog",
|
||||
|
||||
"PublishFrame",
|
||||
|
||||
"CreateDialog",
|
||||
|
||||
"InstanceCardView",
|
||||
"InstanceListView",
|
||||
"PublisherTabsWidget",
|
||||
"OverviewWidget",
|
||||
"ValidationsWidget",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,18 +13,17 @@ 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)
|
||||
|
||||
self._current_asset_name = None
|
||||
self._last_selection = None
|
||||
self._enabled = None
|
||||
|
||||
|
|
@ -42,11 +41,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,19 +62,19 @@ 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()
|
||||
self._clear_selection()
|
||||
|
||||
def set_current_asset_name(self, asset_name):
|
||||
self._current_asset_name = asset_name
|
||||
def update_current_asset(self):
|
||||
# Hide set current asset if there is no one
|
||||
self.set_current_asset_btn_visibility(asset_name is not None)
|
||||
asset_name = self._get_current_session_asset()
|
||||
self.set_current_asset_btn_visibility(bool(asset_name))
|
||||
|
||||
def _get_current_session_asset(self):
|
||||
return self._current_asset_name
|
||||
return self._controller.current_asset_name
|
||||
|
||||
def _create_source_model(self):
|
||||
return AssetsHierarchyModel(self._controller)
|
||||
|
|
|
|||
|
|
@ -3,11 +3,6 @@ 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
|
||||
|
|
@ -16,15 +11,14 @@ from openpype.pipeline.create import (
|
|||
SUBSET_NAME_ALLOWED_SYMBOLS,
|
||||
TaskNotSetError,
|
||||
)
|
||||
from openpype.tools.utils import (
|
||||
ErrorMessageBox,
|
||||
MessageOverlayObject,
|
||||
ClickableFrame,
|
||||
)
|
||||
from openpype.tools.utils import ErrorMessageBox
|
||||
|
||||
from .widgets import IconValuePixmapLabel
|
||||
from .assets_widget import CreateDialogAssetsWidget
|
||||
from .tasks_widget import CreateDialogTasksWidget
|
||||
from .widgets import (
|
||||
IconValuePixmapLabel,
|
||||
CreateBtn,
|
||||
)
|
||||
from .assets_widget import CreateWidgetAssetsWidget
|
||||
from .tasks_widget import CreateWidgetTasksWidget
|
||||
from .precreate_widget import PreCreateWidget
|
||||
from ..constants import (
|
||||
VARIANT_TOOLTIP,
|
||||
|
|
@ -118,8 +112,6 @@ class CreateErrorMessageBox(ErrorMessageBox):
|
|||
|
||||
# TODO add creator identifier/label to details
|
||||
class CreatorShortDescWidget(QtWidgets.QWidget):
|
||||
height_changed = QtCore.Signal(int)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(CreatorShortDescWidget, self).__init__(parent=parent)
|
||||
|
||||
|
|
@ -158,22 +150,6 @@ class CreatorShortDescWidget(QtWidgets.QWidget):
|
|||
self._family_label = family_label
|
||||
self._description_label = description_label
|
||||
|
||||
self._last_height = None
|
||||
|
||||
def _check_height_change(self):
|
||||
height = self.height()
|
||||
if height != self._last_height:
|
||||
self._last_height = height
|
||||
self.height_changed.emit(height)
|
||||
|
||||
def showEvent(self, event):
|
||||
super(CreatorShortDescWidget, self).showEvent(event)
|
||||
self._check_height_change()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(CreatorShortDescWidget, self).resizeEvent(event)
|
||||
self._check_height_change()
|
||||
|
||||
def set_plugin(self, plugin=None):
|
||||
if not plugin:
|
||||
self._icon_widget.set_icon_def(None)
|
||||
|
|
@ -190,122 +166,14 @@ class CreatorShortDescWidget(QtWidgets.QWidget):
|
|||
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)
|
||||
class CreateWidget(QtWidgets.QWidget):
|
||||
def __init__(self, controller, parent=None):
|
||||
super(CreateWidget, self).__init__(parent)
|
||||
|
||||
self.setWindowTitle("Create new instance")
|
||||
|
||||
self.controller = controller
|
||||
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
|
||||
|
|
@ -318,12 +186,12 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
self._name_pattern = name_pattern
|
||||
self._compiled_name_pattern = re.compile(name_pattern)
|
||||
|
||||
overlay_object = MessageOverlayObject(self)
|
||||
main_splitter_widget = QtWidgets.QSplitter(self)
|
||||
|
||||
context_widget = QtWidgets.QWidget(self)
|
||||
context_widget = QtWidgets.QWidget(main_splitter_widget)
|
||||
|
||||
assets_widget = CreateDialogAssetsWidget(controller, context_widget)
|
||||
tasks_widget = CreateDialogTasksWidget(controller, context_widget)
|
||||
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)
|
||||
|
|
@ -332,21 +200,44 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
context_layout.addWidget(tasks_widget, 1)
|
||||
|
||||
# --- Creators view ---
|
||||
creators_header_widget = QtWidgets.QWidget(self)
|
||||
header_label_widget = QtWidgets.QLabel(
|
||||
"Choose family:", creators_header_widget
|
||||
)
|
||||
creators_header_layout = QtWidgets.QHBoxLayout(creators_header_widget)
|
||||
creators_header_layout.setContentsMargins(0, 0, 0, 0)
|
||||
creators_header_layout.addWidget(header_label_widget, 1)
|
||||
creators_widget = QtWidgets.QWidget(main_splitter_widget)
|
||||
|
||||
creators_view = QtWidgets.QListView(self)
|
||||
creator_short_desc_widget = CreatorShortDescWidget(creators_widget)
|
||||
|
||||
attr_separator_widget = QtWidgets.QWidget(creators_widget)
|
||||
attr_separator_widget.setObjectName("Separator")
|
||||
attr_separator_widget.setMinimumHeight(1)
|
||||
attr_separator_widget.setMaximumHeight(1)
|
||||
|
||||
creators_splitter = QtWidgets.QSplitter(creators_widget)
|
||||
|
||||
creators_view_widget = QtWidgets.QWidget(creators_splitter)
|
||||
|
||||
creator_view_label = QtWidgets.QLabel(
|
||||
"Choose publish type", creators_view_widget
|
||||
)
|
||||
|
||||
creators_view = QtWidgets.QListView(creators_view_widget)
|
||||
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)
|
||||
creators_view_layout = QtWidgets.QVBoxLayout(creators_view_widget)
|
||||
creators_view_layout.setContentsMargins(0, 0, 0, 0)
|
||||
creators_view_layout.addWidget(creator_view_label, 0)
|
||||
creators_view_layout.addWidget(creators_view, 1)
|
||||
|
||||
# --- Creator attr defs ---
|
||||
creators_attrs_widget = QtWidgets.QWidget(creators_splitter)
|
||||
|
||||
variant_subset_label = QtWidgets.QLabel(
|
||||
"Create options", creators_attrs_widget
|
||||
)
|
||||
|
||||
variant_subset_widget = QtWidgets.QWidget(creators_attrs_widget)
|
||||
# Variant and subset input
|
||||
variant_widget = VariantInputsWidget(creators_attrs_widget)
|
||||
|
||||
variant_input = QtWidgets.QLineEdit(variant_widget)
|
||||
variant_input.setObjectName("VariantInput")
|
||||
|
|
@ -365,39 +256,20 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
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 = QtWidgets.QLineEdit(variant_subset_widget)
|
||||
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)
|
||||
variant_subset_layout = QtWidgets.QFormLayout(variant_subset_widget)
|
||||
variant_subset_layout.setContentsMargins(0, 0, 0, 0)
|
||||
variant_subset_layout.addRow("Variant", variant_widget)
|
||||
variant_subset_layout.addRow("Subset", subset_name_input)
|
||||
|
||||
# Precreate attributes widget
|
||||
pre_create_widget = PreCreateWidget(creator_attrs_widget)
|
||||
pre_create_widget = PreCreateWidget(creators_attrs_widget)
|
||||
|
||||
# Create button
|
||||
create_btn_wrapper = QtWidgets.QWidget(creator_attrs_widget)
|
||||
create_btn = QtWidgets.QPushButton("Create", create_btn_wrapper)
|
||||
create_btn_wrapper = QtWidgets.QWidget(creators_attrs_widget)
|
||||
create_btn = CreateBtn(create_btn_wrapper)
|
||||
create_btn.setEnabled(False)
|
||||
|
||||
create_btn_wrap_layout = QtWidgets.QHBoxLayout(create_btn_wrapper)
|
||||
|
|
@ -405,79 +277,45 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
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)
|
||||
# -------------------------------------
|
||||
creators_attrs_layout = QtWidgets.QVBoxLayout(creators_attrs_widget)
|
||||
creators_attrs_layout.setContentsMargins(0, 0, 0, 0)
|
||||
creators_attrs_layout.addWidget(variant_subset_label, 0)
|
||||
creators_attrs_layout.addWidget(variant_subset_widget, 0)
|
||||
creators_attrs_layout.addWidget(pre_create_widget, 1)
|
||||
creators_attrs_layout.addWidget(create_btn_wrapper, 0)
|
||||
|
||||
creators_splitter.addWidget(creators_view_widget)
|
||||
creators_splitter.addWidget(creators_attrs_widget)
|
||||
creators_splitter.setStretchFactor(0, 1)
|
||||
creators_splitter.setStretchFactor(1, 2)
|
||||
|
||||
creators_layout = QtWidgets.QVBoxLayout(creators_widget)
|
||||
creators_layout.setContentsMargins(0, 0, 0, 0)
|
||||
creators_layout.addWidget(creator_short_desc_widget, 0)
|
||||
creators_layout.addWidget(attr_separator_widget, 0)
|
||||
creators_layout.addWidget(creators_splitter, 1)
|
||||
# ------------
|
||||
|
||||
# --- 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)
|
||||
# TODO this has no way how can be showed now
|
||||
|
||||
# -------------------------------------------
|
||||
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)
|
||||
main_splitter_widget.addWidget(context_widget)
|
||||
main_splitter_widget.addWidget(creators_widget)
|
||||
main_splitter_widget.setStretchFactor(0, 1)
|
||||
main_splitter_widget.setStretchFactor(1, 3)
|
||||
|
||||
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)
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(main_splitter_widget, 1)
|
||||
|
||||
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)
|
||||
|
|
@ -492,16 +330,14 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
self._on_current_session_context_request
|
||||
)
|
||||
tasks_widget.task_changed.connect(self._on_task_change)
|
||||
creator_short_desc_widget.height_changed.connect(
|
||||
self._on_description_height_change
|
||||
|
||||
controller.event_system.add_callback(
|
||||
"plugins.refresh.finished", self._on_plugins_refresh
|
||||
)
|
||||
splitter_widget.splitterMoved.connect(self._on_splitter_move)
|
||||
|
||||
controller.add_plugins_refresh_callback(self._on_plugins_refresh)
|
||||
self._main_splitter_widget = main_splitter_widget
|
||||
|
||||
self._overlay_object = overlay_object
|
||||
|
||||
self._splitter_widget = splitter_widget
|
||||
self._creators_splitter = creators_splitter
|
||||
|
||||
self._context_widget = context_widget
|
||||
self._assets_widget = assets_widget
|
||||
|
|
@ -514,7 +350,6 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
self.variant_hints_menu = variant_hints_menu
|
||||
self.variant_hints_group = variant_hints_group
|
||||
|
||||
self._creators_header_widget = creators_header_widget
|
||||
self._creators_model = creators_model
|
||||
self._creators_sort_model = creators_sort_model
|
||||
self._creators_view = creators_view
|
||||
|
|
@ -524,26 +359,16 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
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 = []
|
||||
@property
|
||||
def current_asset_name(self):
|
||||
return self._controller.current_asset_name
|
||||
|
||||
def _emit_message(self, message):
|
||||
self._overlay_object.add_message(message)
|
||||
@property
|
||||
def current_task_name(self):
|
||||
return self._controller.current_task_name
|
||||
|
||||
def _context_change_is_enabled(self):
|
||||
return self._context_widget.isEnabled()
|
||||
|
|
@ -554,7 +379,7 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
asset_name = self._assets_widget.get_selected_asset_name()
|
||||
|
||||
if asset_name is None:
|
||||
asset_name = self._asset_name
|
||||
asset_name = self.current_asset_name
|
||||
return asset_name
|
||||
|
||||
def _get_task_name(self):
|
||||
|
|
@ -566,13 +391,9 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
task_name = self._tasks_widget.get_selected_task_name()
|
||||
|
||||
if not task_name:
|
||||
task_name = self._task_name
|
||||
task_name = self.current_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)
|
||||
|
|
@ -601,7 +422,7 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
# data
|
||||
self._refresh_creators()
|
||||
|
||||
self._assets_widget.set_current_asset_name(self._asset_name)
|
||||
self._assets_widget.update_current_asset()
|
||||
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)
|
||||
|
|
@ -611,10 +432,6 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
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 = []
|
||||
|
|
@ -660,7 +477,7 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
if asset_name is None:
|
||||
return
|
||||
|
||||
project_name = self.dbcon.active_project()
|
||||
project_name = self._controller.project_name
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
self._asset_doc = asset_doc
|
||||
|
||||
|
|
@ -689,7 +506,7 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
|
||||
# Add new families
|
||||
new_creators = set()
|
||||
for identifier, creator in self.controller.manual_creators.items():
|
||||
for identifier, creator in self._controller.manual_creators.items():
|
||||
# TODO add details about creator
|
||||
new_creators.add(identifier)
|
||||
if identifier in existing_items:
|
||||
|
|
@ -729,8 +546,7 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
|
||||
def _on_plugins_refresh(self):
|
||||
# Trigger refresh only if is visible
|
||||
if self.isVisible():
|
||||
self.refresh()
|
||||
self.refresh()
|
||||
|
||||
def _on_asset_change(self):
|
||||
self._refresh_asset()
|
||||
|
|
@ -746,14 +562,9 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
|
||||
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)
|
||||
task_name = self.current_task_name
|
||||
if task_name:
|
||||
self._tasks_widget.select_task_name(task_name)
|
||||
|
||||
def _on_creator_item_change(self, new_index, _old_index):
|
||||
identifier = None
|
||||
|
|
@ -761,196 +572,21 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
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)
|
||||
# TODO implement
|
||||
description = ""
|
||||
if creator is not None:
|
||||
description = creator.get_detail_description() or description
|
||||
self._controller.event_system.emit(
|
||||
"show.detailed.help",
|
||||
{
|
||||
"message": description
|
||||
},
|
||||
"create.widget"
|
||||
)
|
||||
|
||||
def _set_creator_by_identifier(self, identifier):
|
||||
creator = self.controller.manual_creators.get(identifier)
|
||||
creator = self._controller.manual_creators.get(identifier)
|
||||
self._set_creator(creator)
|
||||
|
||||
def _set_creator(self, creator):
|
||||
|
|
@ -1034,7 +670,7 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
self.subset_name_input.setText("< Valid variant >")
|
||||
return
|
||||
|
||||
project_name = self.controller.project_name
|
||||
project_name = self._controller.project_name
|
||||
task_name = self._get_task_name()
|
||||
|
||||
asset_doc = copy.deepcopy(self._asset_doc)
|
||||
|
|
@ -1116,41 +752,19 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
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()
|
||||
width = self.width()
|
||||
part = int(width / 4)
|
||||
rem_width = width - part
|
||||
self._main_splitter_widget.setSizes([part, rem_width])
|
||||
rem_width = rem_width - part
|
||||
self._creators_splitter.setSizes([part, rem_width])
|
||||
|
||||
def showEvent(self, event):
|
||||
super(CreateDialog, self).showEvent(event)
|
||||
super(CreateWidget, 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:
|
||||
|
|
@ -1186,7 +800,7 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
error_msg = None
|
||||
formatted_traceback = None
|
||||
try:
|
||||
self.controller.create(
|
||||
self._controller.create(
|
||||
creator_identifier,
|
||||
subset_name,
|
||||
instance_data,
|
||||
|
|
@ -1207,7 +821,7 @@ class CreateDialog(QtWidgets.QDialog):
|
|||
|
||||
if error_msg is None:
|
||||
self._set_creator(self._selected_creator)
|
||||
self._emit_message("Creation finished...")
|
||||
self._controller.emit_card_message("Creation finished...")
|
||||
else:
|
||||
box = CreateErrorMessageBox(
|
||||
creator_label,
|
||||
82
openpype/tools/publisher/widgets/help_widget.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
try:
|
||||
import commonmark
|
||||
except Exception:
|
||||
commonmark = None
|
||||
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
|
||||
class HelpButton(QtWidgets.QPushButton):
|
||||
"""Button used to trigger help dialog."""
|
||||
|
||||
def __init__(self, parent):
|
||||
super(HelpButton, self).__init__(parent)
|
||||
self.setObjectName("CreateDialogHelpButton")
|
||||
self.setText("?")
|
||||
|
||||
|
||||
class HelpWidget(QtWidgets.QWidget):
|
||||
"""Widget showing help for single functionality."""
|
||||
|
||||
def __init__(self, parent):
|
||||
super(HelpWidget, self).__init__(parent)
|
||||
|
||||
# TODO add hints what to help with?
|
||||
detail_description_input = QtWidgets.QTextEdit(self)
|
||||
detail_description_input.setObjectName("CreatorDetailedDescription")
|
||||
detail_description_input.setTextInteractionFlags(
|
||||
QtCore.Qt.TextBrowserInteraction
|
||||
)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(0)
|
||||
main_layout.addWidget(detail_description_input, 1)
|
||||
|
||||
self._detail_description_input = detail_description_input
|
||||
|
||||
self.set_detailed_text()
|
||||
|
||||
def set_detailed_text(self, text=None):
|
||||
if not text:
|
||||
text = "We didn't prepare help for this part..."
|
||||
|
||||
if commonmark:
|
||||
html = commonmark.commonmark(text)
|
||||
self._detail_description_input.setHtml(html)
|
||||
else:
|
||||
self._detail_description_input.setMarkdown(text)
|
||||
|
||||
|
||||
class HelpDialog(QtWidgets.QDialog):
|
||||
default_width = 530
|
||||
default_height = 340
|
||||
|
||||
def __init__(self, controller, parent):
|
||||
super(HelpDialog, self).__init__(parent)
|
||||
|
||||
self.setWindowTitle("Help dialog")
|
||||
|
||||
help_content = HelpWidget(self)
|
||||
|
||||
main_layout = QtWidgets.QHBoxLayout(self)
|
||||
main_layout.addWidget(help_content, 1)
|
||||
|
||||
controller.event_system.add_callback(
|
||||
"show.detailed.help", self._on_help_request
|
||||
)
|
||||
|
||||
self._controller = controller
|
||||
|
||||
self._help_content = help_content
|
||||
|
||||
def _on_help_request(self, event):
|
||||
message = event.get("message")
|
||||
self.set_detailed_text(message)
|
||||
|
||||
def set_detailed_text(self, text=None):
|
||||
self._help_content.set_detailed_text(text)
|
||||
|
||||
def showEvent(self, event):
|
||||
super(HelpDialog, self).showEvent(event)
|
||||
self.resize(self.default_width, self.default_height)
|
||||
|
Before Width: | Height: | Size: 5.3 KiB |
BIN
openpype/tools/publisher/widgets/images/create.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 6.1 KiB |
368
openpype/tools/publisher/widgets/overview_widget.py
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
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,
|
||||
)
|
||||
from .create_widget import CreateWidget
|
||||
|
||||
|
||||
class OverviewWidget(QtWidgets.QFrame):
|
||||
active_changed = QtCore.Signal()
|
||||
instance_context_changed = QtCore.Signal()
|
||||
create_requested = QtCore.Signal()
|
||||
|
||||
anim_end_value = 200
|
||||
anim_duration = 200
|
||||
|
||||
def __init__(self, controller, parent):
|
||||
super(OverviewWidget, self).__init__(parent)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
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)
|
||||
subset_views_layout.setCurrentWidget(subset_view_cards)
|
||||
|
||||
# Buttons at the bottom of subset view
|
||||
create_btn = CreateInstanceBtn(self)
|
||||
delete_btn = RemoveInstanceBtn(self)
|
||||
change_view_btn = ChangeViewBtn(self)
|
||||
|
||||
# --- Overview ---
|
||||
# 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(create_widget, 7)
|
||||
subset_content_layout.addWidget(subset_views_widget, 3)
|
||||
subset_content_layout.addWidget(subset_attributes_wrap, 7)
|
||||
|
||||
# Subset frame layout
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.addWidget(subset_content_widget, 1)
|
||||
|
||||
change_anim = QtCore.QVariantAnimation()
|
||||
change_anim.setStartValue(0)
|
||||
change_anim.setEndValue(self.anim_end_value)
|
||||
change_anim.setDuration(self.anim_duration)
|
||||
change_anim.setEasingCurve(QtCore.QEasingCurve.InOutQuad)
|
||||
|
||||
# --- 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)
|
||||
|
||||
change_anim.valueChanged.connect(self._on_change_anim)
|
||||
change_anim.finished.connect(self._on_change_anim_finished)
|
||||
|
||||
# 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 callbacks ---
|
||||
controller.event_system.add_callback(
|
||||
"publish.process.started", self._on_publish_start
|
||||
)
|
||||
controller.event_system.add_callback(
|
||||
"publish.reset.finished", self._on_publish_reset
|
||||
)
|
||||
controller.event_system.add_callback(
|
||||
"instances.refresh.finished", self._on_instances_refresh
|
||||
)
|
||||
|
||||
self._subset_content_widget = subset_content_widget
|
||||
self._subset_content_layout = subset_content_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._subset_attributes_widget = subset_attributes_widget
|
||||
self._create_widget = create_widget
|
||||
self._subset_views_widget = subset_views_widget
|
||||
self._subset_attributes_wrap = subset_attributes_wrap
|
||||
|
||||
self._change_anim = change_anim
|
||||
|
||||
# Start in create mode
|
||||
self._create_widget_policy = create_widget.sizePolicy()
|
||||
self._subset_views_widget_policy = subset_views_widget.sizePolicy()
|
||||
self._subset_attributes_wrap_policy = (
|
||||
subset_attributes_wrap.sizePolicy()
|
||||
)
|
||||
self._max_widget_width = None
|
||||
self._current_state = "create"
|
||||
subset_attributes_wrap.setVisible(False)
|
||||
|
||||
def set_state(self, new_state, animate):
|
||||
if new_state == self._current_state:
|
||||
return
|
||||
|
||||
self._current_state = new_state
|
||||
|
||||
anim_is_running = (
|
||||
self._change_anim.state() == self._change_anim.Running
|
||||
)
|
||||
if not animate:
|
||||
self._change_visibility_for_state()
|
||||
if anim_is_running:
|
||||
self._change_anim.stop()
|
||||
return
|
||||
|
||||
if self._max_widget_width is None:
|
||||
self._max_widget_width = self._subset_views_widget.maximumWidth()
|
||||
|
||||
if new_state == "create":
|
||||
direction = self._change_anim.Backward
|
||||
else:
|
||||
direction = self._change_anim.Forward
|
||||
self._change_anim.setDirection(direction)
|
||||
|
||||
if not anim_is_running:
|
||||
view_width = self._subset_views_widget.width()
|
||||
self._subset_views_widget.setMinimumWidth(view_width)
|
||||
self._subset_views_widget.setMaximumWidth(view_width)
|
||||
self._change_anim.start()
|
||||
|
||||
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_change_anim(self, value):
|
||||
self._create_widget.setVisible(True)
|
||||
self._subset_attributes_wrap.setVisible(True)
|
||||
width = (
|
||||
self._subset_content_widget.width()
|
||||
- (
|
||||
self._subset_views_widget.width()
|
||||
+ (self._subset_content_layout.spacing() * 2)
|
||||
)
|
||||
)
|
||||
subset_attrs_width = int(float(width) / self.anim_end_value) * value
|
||||
if subset_attrs_width > width:
|
||||
subset_attrs_width = width
|
||||
create_width = width - subset_attrs_width
|
||||
|
||||
self._create_widget.setMinimumWidth(create_width)
|
||||
self._create_widget.setMaximumWidth(create_width)
|
||||
self._subset_attributes_wrap.setMinimumWidth(subset_attrs_width)
|
||||
self._subset_attributes_wrap.setMaximumWidth(subset_attrs_width)
|
||||
|
||||
def _on_change_anim_finished(self):
|
||||
self._change_visibility_for_state()
|
||||
self._create_widget.setMinimumWidth(0)
|
||||
self._create_widget.setMaximumWidth(self._max_widget_width)
|
||||
self._subset_attributes_wrap.setMinimumWidth(0)
|
||||
self._subset_attributes_wrap.setMaximumWidth(self._max_widget_width)
|
||||
self._subset_views_widget.setMinimumWidth(0)
|
||||
self._subset_views_widget.setMaximumWidth(self._max_widget_width)
|
||||
self._create_widget.setSizePolicy(
|
||||
self._create_widget_policy
|
||||
)
|
||||
self._subset_attributes_wrap.setSizePolicy(
|
||||
self._subset_attributes_wrap_policy
|
||||
)
|
||||
self._subset_views_widget.setSizePolicy(
|
||||
self._subset_views_widget_policy
|
||||
)
|
||||
|
||||
def _change_visibility_for_state(self):
|
||||
self._create_widget.setVisible(
|
||||
self._current_state == "create"
|
||||
)
|
||||
self._subset_attributes_wrap.setVisible(
|
||||
self._current_state == "publish"
|
||||
)
|
||||
|
||||
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_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):
|
||||
"""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()
|
||||
520
openpype/tools/publisher/widgets/publish_frame.py
Normal file
|
|
@ -0,0 +1,520 @@
|
|||
import os
|
||||
import json
|
||||
import time
|
||||
|
||||
from Qt import QtWidgets, QtCore
|
||||
|
||||
from openpype.pipeline import KnownPublishError
|
||||
|
||||
from .widgets import (
|
||||
StopBtn,
|
||||
ResetBtn,
|
||||
ValidateBtn,
|
||||
PublishBtn,
|
||||
PublishReportBtn,
|
||||
)
|
||||
|
||||
|
||||
class PublishFrame(QtWidgets.QWidget):
|
||||
"""Frame showed during publishing.
|
||||
|
||||
Shows all information related to publishing. Contains validation error
|
||||
widget which is showed if only validation error happens during validation.
|
||||
|
||||
Processing layer is default layer. Validation error layer is shown if only
|
||||
validation exception is raised during publishing. Report layer is available
|
||||
only when publishing process is stopped and must be manually triggered to
|
||||
change into that layer.
|
||||
|
||||
+------------------------------------------------------------------------+
|
||||
| < Main label > |
|
||||
| < Label top > |
|
||||
| (#### 10% <Progress bar> ) |
|
||||
| <Instance label> <Plugin label> |
|
||||
| <Report> <Reset><Stop><Validate><Publish> |
|
||||
+------------------------------------------------------------------------+
|
||||
"""
|
||||
|
||||
details_page_requested = QtCore.Signal()
|
||||
|
||||
def __init__(self, controller, borders, parent):
|
||||
super(PublishFrame, self).__init__(parent)
|
||||
|
||||
# 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
|
||||
content_frame = QtWidgets.QFrame(self)
|
||||
content_frame.setObjectName("PublishInfoFrame")
|
||||
|
||||
top_content_widget = QtWidgets.QWidget(content_frame)
|
||||
|
||||
# Center widget displaying current state (without any specific info)
|
||||
main_label = QtWidgets.QLabel(top_content_widget)
|
||||
main_label.setObjectName("PublishInfoMainLabel")
|
||||
main_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
# Supporting labels for main label
|
||||
# Top label is displayed just under main label
|
||||
message_label_top = QtWidgets.QLabel(top_content_widget)
|
||||
message_label_top.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
# Label showing currently processed instance
|
||||
progress_widget = QtWidgets.QWidget(top_content_widget)
|
||||
instance_plugin_widget = QtWidgets.QWidget(progress_widget)
|
||||
instance_label = QtWidgets.QLabel(
|
||||
"<Instance name>", instance_plugin_widget
|
||||
)
|
||||
instance_label.setAlignment(
|
||||
QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter
|
||||
)
|
||||
# Label showing currently processed plugin
|
||||
plugin_label = QtWidgets.QLabel(
|
||||
"<Plugin name>", instance_plugin_widget
|
||||
)
|
||||
plugin_label.setAlignment(
|
||||
QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter
|
||||
)
|
||||
instance_plugin_layout = QtWidgets.QHBoxLayout(instance_plugin_widget)
|
||||
instance_plugin_layout.setContentsMargins(0, 0, 0, 0)
|
||||
instance_plugin_layout.addWidget(instance_label, 1)
|
||||
instance_plugin_layout.addWidget(plugin_label, 1)
|
||||
|
||||
# Progress bar showing progress of publishing
|
||||
progress_bar = QtWidgets.QProgressBar(progress_widget)
|
||||
progress_bar.setObjectName("PublishProgressBar")
|
||||
|
||||
progress_layout = QtWidgets.QVBoxLayout(progress_widget)
|
||||
progress_layout.setSpacing(5)
|
||||
progress_layout.setContentsMargins(0, 0, 0, 0)
|
||||
progress_layout.addWidget(instance_plugin_widget, 0)
|
||||
progress_layout.addWidget(progress_bar, 0)
|
||||
|
||||
top_content_layout = QtWidgets.QVBoxLayout(top_content_widget)
|
||||
top_content_layout.setContentsMargins(0, 0, 0, 0)
|
||||
top_content_layout.setSpacing(5)
|
||||
top_content_layout.setAlignment(QtCore.Qt.AlignCenter)
|
||||
top_content_layout.addWidget(main_label)
|
||||
# TODO stretches should be probably replaced by spacing...
|
||||
# - stretch in floating frame doesn't make sense
|
||||
top_content_layout.addWidget(message_label_top)
|
||||
top_content_layout.addWidget(progress_widget)
|
||||
|
||||
# Publishing buttons to stop, reset or trigger publishing
|
||||
footer_widget = QtWidgets.QWidget(content_frame)
|
||||
|
||||
report_btn = PublishReportBtn(footer_widget)
|
||||
|
||||
shrunk_main_label = QtWidgets.QLabel(footer_widget)
|
||||
shrunk_main_label.setObjectName("PublishInfoMainLabel")
|
||||
shrunk_main_label.setAlignment(
|
||||
QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft
|
||||
)
|
||||
|
||||
reset_btn = ResetBtn(footer_widget)
|
||||
stop_btn = StopBtn(footer_widget)
|
||||
validate_btn = ValidateBtn(footer_widget)
|
||||
publish_btn = PublishBtn(footer_widget)
|
||||
|
||||
report_btn.add_action("Go to details", "go_to_report")
|
||||
report_btn.add_action("Copy report", "copy_report")
|
||||
report_btn.add_action("Export report", "export_report")
|
||||
|
||||
# Footer on info frame layout
|
||||
footer_layout = QtWidgets.QHBoxLayout(footer_widget)
|
||||
footer_layout.setContentsMargins(0, 0, 0, 0)
|
||||
footer_layout.addWidget(report_btn, 0)
|
||||
footer_layout.addWidget(shrunk_main_label, 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)
|
||||
|
||||
# Info frame content
|
||||
content_layout = QtWidgets.QVBoxLayout(content_frame)
|
||||
content_layout.setSpacing(5)
|
||||
|
||||
content_layout.addWidget(top_content_widget)
|
||||
content_layout.addWidget(footer_widget)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(borders, 0, borders, borders)
|
||||
main_layout.addWidget(content_frame)
|
||||
|
||||
shrunk_anim = QtCore.QVariantAnimation()
|
||||
shrunk_anim.setDuration(140)
|
||||
shrunk_anim.setEasingCurve(QtCore.QEasingCurve.InOutQuad)
|
||||
|
||||
# Force translucent background for widgets
|
||||
for widget in (
|
||||
self,
|
||||
top_content_widget,
|
||||
footer_widget,
|
||||
progress_widget,
|
||||
instance_plugin_widget,
|
||||
):
|
||||
widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
|
||||
report_btn.triggered.connect(self._on_report_triggered)
|
||||
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)
|
||||
|
||||
shrunk_anim.valueChanged.connect(self._on_shrunk_anim)
|
||||
shrunk_anim.finished.connect(self._on_shrunk_anim_finish)
|
||||
|
||||
controller.event_system.add_callback(
|
||||
"publish.reset.finished", self._on_publish_reset
|
||||
)
|
||||
controller.event_system.add_callback(
|
||||
"publish.process.started", self._on_publish_start
|
||||
)
|
||||
controller.event_system.add_callback(
|
||||
"publish.process.validated", self._on_publish_validated
|
||||
)
|
||||
controller.event_system.add_callback(
|
||||
"publish.process.stopped", self._on_publish_stop
|
||||
)
|
||||
|
||||
controller.event_system.add_callback(
|
||||
"publish.process.instance.changed", self._on_instance_change
|
||||
)
|
||||
controller.event_system.add_callback(
|
||||
"publish.process.plugin.changed", self._on_plugin_change
|
||||
)
|
||||
|
||||
self._shrunk_anim = shrunk_anim
|
||||
|
||||
self.controller = controller
|
||||
|
||||
self._content_frame = content_frame
|
||||
self._content_layout = content_layout
|
||||
self._top_content_layout = top_content_layout
|
||||
self._top_content_widget = top_content_widget
|
||||
|
||||
self._main_label = main_label
|
||||
self._message_label_top = message_label_top
|
||||
|
||||
self._instance_label = instance_label
|
||||
self._plugin_label = plugin_label
|
||||
|
||||
self._progress_bar = progress_bar
|
||||
self._progress_widget = progress_widget
|
||||
|
||||
self._shrunk_main_label = shrunk_main_label
|
||||
self._reset_btn = reset_btn
|
||||
self._stop_btn = stop_btn
|
||||
self._validate_btn = validate_btn
|
||||
self._publish_btn = publish_btn
|
||||
|
||||
self._shrunken = False
|
||||
self._top_widget_max_height = None
|
||||
self._top_widget_size_policy = top_content_widget.sizePolicy()
|
||||
self._last_instance_label = None
|
||||
self._last_plugin_label = None
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
super(PublishFrame, self).mouseReleaseEvent(event)
|
||||
self._change_shrunk_state()
|
||||
|
||||
def _change_shrunk_state(self):
|
||||
self.set_shrunk_state(not self._shrunken)
|
||||
|
||||
def set_shrunk_state(self, shrunk):
|
||||
if shrunk is self._shrunken:
|
||||
return
|
||||
|
||||
if self._top_widget_max_height is None:
|
||||
self._top_widget_max_height = (
|
||||
self._top_content_widget.maximumHeight()
|
||||
)
|
||||
|
||||
self._shrunken = shrunk
|
||||
|
||||
anim_is_running = (
|
||||
self._shrunk_anim.state() == self._shrunk_anim.Running
|
||||
)
|
||||
if not self.isVisible():
|
||||
if anim_is_running:
|
||||
self._shrunk_anim.stop()
|
||||
self._on_shrunk_anim_finish()
|
||||
return
|
||||
|
||||
start = 0
|
||||
end = 0
|
||||
if shrunk:
|
||||
start = self._top_content_widget.height()
|
||||
else:
|
||||
if anim_is_running:
|
||||
start = self._shrunk_anim.currentValue()
|
||||
hint = self._top_content_widget.minimumSizeHint()
|
||||
end = hint.height()
|
||||
|
||||
self._shrunk_anim.setStartValue(start)
|
||||
self._shrunk_anim.setEndValue(end)
|
||||
if not anim_is_running:
|
||||
self._shrunk_anim.start()
|
||||
|
||||
def _on_shrunk_anim(self, value):
|
||||
diff = self._top_content_widget.height() - value
|
||||
if not self._top_content_widget.isVisible():
|
||||
diff -= self._content_layout.spacing()
|
||||
|
||||
window_pos = self.pos()
|
||||
window_pos_y = window_pos.y() + diff
|
||||
window_height = self.height() - diff
|
||||
|
||||
self._top_content_widget.setMinimumHeight(value)
|
||||
self._top_content_widget.setMaximumHeight(value)
|
||||
self._top_content_widget.setVisible(True)
|
||||
|
||||
self.resize(self.width(), window_height)
|
||||
self.move(window_pos.x(), window_pos_y)
|
||||
|
||||
def _on_shrunk_anim_finish(self):
|
||||
self._top_content_widget.setVisible(not self._shrunken)
|
||||
self._top_content_widget.setMinimumHeight(0)
|
||||
self._top_content_widget.setMaximumHeight(
|
||||
self._top_widget_max_height
|
||||
)
|
||||
self._top_content_widget.setSizePolicy(self._top_widget_size_policy)
|
||||
|
||||
if self._shrunken:
|
||||
self._shrunk_main_label.setText(self._main_label.text())
|
||||
else:
|
||||
self._shrunk_main_label.setText("")
|
||||
|
||||
if self._shrunken:
|
||||
content_frame_hint = self._content_frame.sizeHint()
|
||||
|
||||
layout = self.layout()
|
||||
margins = layout.contentsMargins()
|
||||
window_height = (
|
||||
content_frame_hint.height()
|
||||
+ margins.bottom()
|
||||
+ margins.top()
|
||||
)
|
||||
diff = self.height() - window_height
|
||||
window_pos = self.pos()
|
||||
window_pos_y = window_pos.y() + diff
|
||||
self.resize(self.width(), window_height)
|
||||
self.move(window_pos.x(), window_pos_y)
|
||||
|
||||
def _set_main_label(self, message):
|
||||
self._main_label.setText(message)
|
||||
if self._shrunken:
|
||||
self._shrunk_main_label.setText(message)
|
||||
|
||||
def _on_publish_reset(self):
|
||||
self._last_instance_label = None
|
||||
self._last_plugin_label = None
|
||||
|
||||
self._set_success_property()
|
||||
self._set_progress_visibility(True)
|
||||
|
||||
self._main_label.setText("Hit publish (play button)! If you want")
|
||||
self._message_label_top.setText("")
|
||||
|
||||
self._reset_btn.setEnabled(True)
|
||||
self._stop_btn.setEnabled(False)
|
||||
self._validate_btn.setEnabled(True)
|
||||
self._publish_btn.setEnabled(True)
|
||||
|
||||
self._progress_bar.setValue(self.controller.publish_progress)
|
||||
self._progress_bar.setMaximum(self.controller.publish_max_progress)
|
||||
|
||||
def _on_publish_start(self):
|
||||
if self._last_plugin_label:
|
||||
self._plugin_label.setText(self._last_plugin_label)
|
||||
|
||||
if self._last_instance_label:
|
||||
self._instance_label.setText(self._last_instance_label)
|
||||
|
||||
self._set_success_property(-1)
|
||||
self._set_progress_visibility(True)
|
||||
self._set_main_label("Publishing...")
|
||||
|
||||
self._reset_btn.setEnabled(False)
|
||||
self._stop_btn.setEnabled(True)
|
||||
self._validate_btn.setEnabled(False)
|
||||
self._publish_btn.setEnabled(False)
|
||||
|
||||
self.set_shrunk_state(False)
|
||||
|
||||
def _on_publish_validated(self):
|
||||
self._validate_btn.setEnabled(False)
|
||||
|
||||
def _on_instance_change(self, event):
|
||||
"""Change instance label when instance is going to be processed."""
|
||||
|
||||
self._last_instance_label = event["instance_label"]
|
||||
self._instance_label.setText(event["instance_label"])
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
def _on_plugin_change(self, event):
|
||||
"""Change plugin label when instance is going to be processed."""
|
||||
|
||||
self._last_plugin_label = event["plugin_label"]
|
||||
self._progress_bar.setValue(self.controller.publish_progress)
|
||||
self._plugin_label.setText(event["plugin_label"])
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
def _on_publish_stop(self):
|
||||
self._progress_bar.setValue(self.controller.publish_progress)
|
||||
|
||||
self._reset_btn.setEnabled(True)
|
||||
self._stop_btn.setEnabled(False)
|
||||
|
||||
self._instance_label.setText("")
|
||||
self._plugin_label.setText("")
|
||||
|
||||
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
|
||||
if publish_enabled:
|
||||
if (
|
||||
self.controller.publish_has_validated
|
||||
and self.controller.publish_has_validation_errors
|
||||
):
|
||||
publish_enabled = False
|
||||
|
||||
else:
|
||||
publish_enabled = not self.controller.publish_has_finished
|
||||
|
||||
self._validate_btn.setEnabled(validate_enabled)
|
||||
self._publish_btn.setEnabled(publish_enabled)
|
||||
|
||||
error = self.controller.get_publish_crash_error()
|
||||
validation_errors = self.controller.get_validation_errors()
|
||||
if error:
|
||||
self._set_error(error)
|
||||
|
||||
elif validation_errors:
|
||||
self._set_progress_visibility(False)
|
||||
self._set_validation_errors()
|
||||
|
||||
elif self.controller.publish_has_finished:
|
||||
self._set_finished()
|
||||
|
||||
else:
|
||||
self._set_stopped()
|
||||
|
||||
def _set_stopped(self):
|
||||
main_label = "Publish paused"
|
||||
if self.controller.publish_has_validated:
|
||||
main_label += " - Validation passed"
|
||||
|
||||
self._set_main_label(main_label)
|
||||
self._message_label_top.setText(
|
||||
"Hit publish (play button) to continue."
|
||||
)
|
||||
|
||||
self._set_success_property(-1)
|
||||
|
||||
def _set_error(self, error):
|
||||
self._set_main_label("Error happened")
|
||||
if isinstance(error, KnownPublishError):
|
||||
msg = str(error)
|
||||
else:
|
||||
msg = (
|
||||
"Something went wrong. Send report"
|
||||
" to your supervisor or OpenPype."
|
||||
)
|
||||
self._message_label_top.setText(msg)
|
||||
|
||||
self._set_success_property(0)
|
||||
|
||||
def _set_validation_errors(self):
|
||||
self._set_main_label("Your publish didn't pass studio validations")
|
||||
self._message_label_top.setText("Check results above please")
|
||||
self._set_success_property(2)
|
||||
|
||||
def _set_finished(self):
|
||||
self._set_main_label("Finished")
|
||||
self._message_label_top.setText("")
|
||||
self._set_success_property(1)
|
||||
|
||||
def _set_progress_visibility(self, visible):
|
||||
window_height = self.height()
|
||||
self._progress_widget.setVisible(visible)
|
||||
# Ignore rescaling and move of widget if is shrunken of progress bar
|
||||
# should be visible
|
||||
if self._shrunken or visible:
|
||||
return
|
||||
|
||||
height = self._progress_widget.height()
|
||||
diff = height + self._top_content_layout.spacing()
|
||||
|
||||
window_pos = self.pos()
|
||||
window_pos_y = self.pos().y() + diff
|
||||
window_height -= diff
|
||||
|
||||
self.resize(self.width(), window_height)
|
||||
self.move(window_pos.x(), window_pos_y)
|
||||
|
||||
def _set_success_property(self, state=None):
|
||||
if state is None:
|
||||
state = ""
|
||||
else:
|
||||
state = str(state)
|
||||
|
||||
for widget in (self._progress_bar, self._content_frame):
|
||||
if widget.property("state") != state:
|
||||
widget.setProperty("state", state)
|
||||
widget.style().polish(widget)
|
||||
|
||||
def _copy_report(self):
|
||||
logs = self.controller.get_publish_report()
|
||||
logs_string = json.dumps(logs, indent=4)
|
||||
|
||||
mime_data = QtCore.QMimeData()
|
||||
mime_data.setText(logs_string)
|
||||
QtWidgets.QApplication.instance().clipboard().setMimeData(
|
||||
mime_data
|
||||
)
|
||||
|
||||
def _export_report(self):
|
||||
default_filename = "publish-report-{}".format(
|
||||
time.strftime("%y%m%d-%H-%M")
|
||||
)
|
||||
default_filepath = os.path.join(
|
||||
os.path.expanduser("~"),
|
||||
default_filename
|
||||
)
|
||||
new_filepath, ext = QtWidgets.QFileDialog.getSaveFileName(
|
||||
self, "Save report", default_filepath, ".json"
|
||||
)
|
||||
if not ext or not new_filepath:
|
||||
return
|
||||
|
||||
logs = self.controller.get_publish_report()
|
||||
full_path = new_filepath + ext
|
||||
dir_path = os.path.dirname(full_path)
|
||||
if not os.path.exists(dir_path):
|
||||
os.makedirs(dir_path)
|
||||
|
||||
with open(full_path, "w") as file_stream:
|
||||
json.dump(logs, file_stream)
|
||||
|
||||
def _on_report_triggered(self, identifier):
|
||||
if identifier == "export_report":
|
||||
self._export_report()
|
||||
|
||||
elif identifier == "copy_report":
|
||||
self._copy_report()
|
||||
|
||||
elif identifier == "go_to_report":
|
||||
self.details_page_requested.emit()
|
||||
|
||||
def _on_reset_clicked(self):
|
||||
self.controller.reset()
|
||||
|
||||
def _on_stop_clicked(self):
|
||||
self.controller.stop_publish()
|
||||
|
||||
def _on_validate_clicked(self):
|
||||
self.controller.validate()
|
||||
|
||||
def _on_publish_clicked(self):
|
||||
self.controller.publish()
|
||||
|
|
@ -1,519 +0,0 @@
|
|||
import os
|
||||
import json
|
||||
import time
|
||||
|
||||
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,
|
||||
ValidateBtn,
|
||||
PublishBtn,
|
||||
CopyPublishReportBtn,
|
||||
SavePublishReportBtn,
|
||||
ShowPublishReportBtn
|
||||
)
|
||||
|
||||
|
||||
class ActionsButton(QtWidgets.QToolButton):
|
||||
def __init__(self, parent=None):
|
||||
super(ActionsButton, self).__init__(parent)
|
||||
|
||||
self.setText("< No action >")
|
||||
self.setPopupMode(self.MenuButtonPopup)
|
||||
menu = QtWidgets.QMenu(self)
|
||||
|
||||
self.setMenu(menu)
|
||||
|
||||
self._menu = menu
|
||||
self._actions = []
|
||||
self._current_action = None
|
||||
|
||||
self.clicked.connect(self._on_click)
|
||||
|
||||
def current_action(self):
|
||||
return self._current_action
|
||||
|
||||
def add_action(self, action):
|
||||
self._actions.append(action)
|
||||
action.triggered.connect(self._on_action_trigger)
|
||||
self._menu.addAction(action)
|
||||
if self._current_action is None:
|
||||
self._set_action(action)
|
||||
|
||||
def set_action(self, action):
|
||||
if action not in self._actions:
|
||||
self.add_action(action)
|
||||
self._set_action(action)
|
||||
|
||||
def _set_action(self, action):
|
||||
if action is self._current_action:
|
||||
return
|
||||
self._current_action = action
|
||||
self.setText(action.text())
|
||||
self.setIcon(action.icon())
|
||||
|
||||
def _on_click(self):
|
||||
self._current_action.trigger()
|
||||
|
||||
def _on_action_trigger(self):
|
||||
action = self.sender()
|
||||
if action not in self._actions:
|
||||
return
|
||||
|
||||
self._set_action(action)
|
||||
|
||||
|
||||
class PublishFrame(QtWidgets.QFrame):
|
||||
"""Frame showed during publishing.
|
||||
|
||||
Shows all information related to publishing. Contains validation error
|
||||
widget which is showed if only validation error happens during validation.
|
||||
|
||||
Processing layer is default layer. Validation error layer is shown if only
|
||||
validation exception is raised during publishing. Report layer is available
|
||||
only when publishing process is stopped and must be manually triggered to
|
||||
change into that layer.
|
||||
|
||||
+------------------------------------------------------------------------+
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| < Validation error widget > |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
+------------------------------------------------------------------------+
|
||||
| < Main label > |
|
||||
| < Label top > |
|
||||
| (#### 10% <Progress bar> ) |
|
||||
| <Instance label> <Plugin label> |
|
||||
| Report: <Copy><Save> <Label bottom> <Reset><Stop><Validate><Publish> |
|
||||
+------------------------------------------------------------------------+
|
||||
"""
|
||||
def __init__(self, controller, parent):
|
||||
super(PublishFrame, self).__init__(parent)
|
||||
|
||||
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
|
||||
info_frame = QtWidgets.QFrame(self)
|
||||
info_frame.setObjectName("PublishInfoFrame")
|
||||
|
||||
# Content of info frame
|
||||
# - separated into QFrame and QWidget (widget has transparent bg)
|
||||
content_widget = QtWidgets.QWidget(info_frame)
|
||||
content_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
|
||||
info_layout = QtWidgets.QVBoxLayout(info_frame)
|
||||
info_layout.setContentsMargins(0, 0, 0, 0)
|
||||
info_layout.addWidget(content_widget)
|
||||
|
||||
# Center widget displaying current state (without any specific info)
|
||||
main_label = QtWidgets.QLabel(content_widget)
|
||||
main_label.setObjectName("PublishInfoMainLabel")
|
||||
main_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
# Supporting labels for main label
|
||||
# Top label is displayed just under main label
|
||||
message_label_top = QtWidgets.QLabel(content_widget)
|
||||
message_label_top.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
# Bottom label is displayed between report and publish buttons
|
||||
# at bottom part of info frame
|
||||
message_label_bottom = QtWidgets.QLabel(content_widget)
|
||||
message_label_bottom.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
# Label showing currently processed instance
|
||||
instance_label = QtWidgets.QLabel("<Instance name>", content_widget)
|
||||
instance_label.setAlignment(
|
||||
QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter
|
||||
)
|
||||
# Label showing currently processed plugin
|
||||
plugin_label = QtWidgets.QLabel("<Plugin name>", content_widget)
|
||||
plugin_label.setAlignment(
|
||||
QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter
|
||||
)
|
||||
instance_plugin_layout = QtWidgets.QHBoxLayout()
|
||||
instance_plugin_layout.addWidget(instance_label, 1)
|
||||
instance_plugin_layout.addWidget(plugin_label, 1)
|
||||
|
||||
# Progress bar showing progress of publishing
|
||||
progress_widget = QtWidgets.QProgressBar(content_widget)
|
||||
progress_widget.setObjectName("PublishProgressBar")
|
||||
|
||||
# Report buttons to be able copy, save or see report
|
||||
report_btns_widget = QtWidgets.QWidget(content_widget)
|
||||
report_btns_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
# Hidden by default
|
||||
report_btns_widget.setVisible(False)
|
||||
|
||||
report_label = QtWidgets.QLabel("Report:", report_btns_widget)
|
||||
copy_report_btn = CopyPublishReportBtn(report_btns_widget)
|
||||
export_report_btn = SavePublishReportBtn(report_btns_widget)
|
||||
show_details_btn = ShowPublishReportBtn(report_btns_widget)
|
||||
|
||||
report_btns_layout = QtWidgets.QHBoxLayout(report_btns_widget)
|
||||
report_btns_layout.setContentsMargins(0, 0, 0, 0)
|
||||
report_btns_layout.addWidget(report_label, 0)
|
||||
report_btns_layout.addWidget(copy_report_btn, 0)
|
||||
report_btns_layout.addWidget(export_report_btn, 0)
|
||||
report_btns_layout.addWidget(show_details_btn, 0)
|
||||
|
||||
# Publishing buttons to stop, reset or trigger publishing
|
||||
reset_btn = ResetBtn(content_widget)
|
||||
stop_btn = StopBtn(content_widget)
|
||||
validate_btn = ValidateBtn(content_widget)
|
||||
publish_btn = PublishBtn(content_widget)
|
||||
|
||||
# Footer on info frame layout
|
||||
info_footer_layout = QtWidgets.QHBoxLayout()
|
||||
info_footer_layout.addWidget(report_btns_widget, 0)
|
||||
info_footer_layout.addWidget(message_label_bottom, 1)
|
||||
info_footer_layout.addWidget(reset_btn, 0)
|
||||
info_footer_layout.addWidget(stop_btn, 0)
|
||||
info_footer_layout.addWidget(validate_btn, 0)
|
||||
info_footer_layout.addWidget(publish_btn, 0)
|
||||
|
||||
# Info frame content
|
||||
content_layout = QtWidgets.QVBoxLayout(content_widget)
|
||||
content_layout.setSpacing(5)
|
||||
content_layout.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
content_layout.addWidget(main_label)
|
||||
content_layout.addStretch(1)
|
||||
content_layout.addWidget(message_label_top)
|
||||
content_layout.addStretch(1)
|
||||
content_layout.addLayout(instance_plugin_layout)
|
||||
content_layout.addWidget(progress_widget)
|
||||
content_layout.addStretch(1)
|
||||
content_layout.addLayout(info_footer_layout)
|
||||
|
||||
# Whole widget layout
|
||||
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.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)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setStackingMode(main_layout.StackOne)
|
||||
main_layout.addWidget(publish_widget)
|
||||
main_layout.addWidget(details_widget)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
controller.add_publish_stopped_callback(self._on_publish_stop)
|
||||
|
||||
controller.add_instance_change_callback(self._on_instance_change)
|
||||
controller.add_plugin_change_callback(self._on_plugin_change)
|
||||
|
||||
self.controller = controller
|
||||
|
||||
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
|
||||
self._message_label_top = message_label_top
|
||||
|
||||
self._instance_label = instance_label
|
||||
self._plugin_label = plugin_label
|
||||
|
||||
self._progress_widget = progress_widget
|
||||
|
||||
self._report_btns_widget = report_btns_widget
|
||||
self._message_label_bottom = message_label_bottom
|
||||
self._reset_btn = reset_btn
|
||||
self._stop_btn = stop_btn
|
||||
self._validate_btn = validate_btn
|
||||
self._publish_btn = publish_btn
|
||||
|
||||
self._details_widget = details_widget
|
||||
self._report_view = report_view
|
||||
|
||||
def _get_report_close_icon(self):
|
||||
size = 100
|
||||
pix = QtGui.QPixmap(size, size)
|
||||
pix.fill(QtCore.Qt.transparent)
|
||||
|
||||
half_stroke_size = size / 12
|
||||
stroke_size = 2 * half_stroke_size
|
||||
size_part = size / 5
|
||||
|
||||
p1 = QtCore.QPoint(half_stroke_size, size_part)
|
||||
p2 = QtCore.QPoint(size / 2, size_part * 4)
|
||||
p3 = QtCore.QPoint(size - half_stroke_size, size_part)
|
||||
painter = QtGui.QPainter(pix)
|
||||
pen = QtGui.QPen(QtCore.Qt.white)
|
||||
pen.setWidth(stroke_size)
|
||||
pen.setCapStyle(QtCore.Qt.RoundCap)
|
||||
painter.setPen(pen)
|
||||
painter.setBrush(QtCore.Qt.transparent)
|
||||
painter.drawLine(p1, p2)
|
||||
painter.drawLine(p2, p3)
|
||||
painter.end()
|
||||
|
||||
return QtGui.QIcon(pix)
|
||||
|
||||
def _on_publish_reset(self):
|
||||
self._set_success_property()
|
||||
self._change_bg_property()
|
||||
self._set_progress_visibility(True)
|
||||
|
||||
self._main_label.setText("Hit publish (play button)! If you want")
|
||||
self._message_label_top.setText("")
|
||||
self._message_label_bottom.setText("")
|
||||
self._report_btns_widget.setVisible(False)
|
||||
|
||||
self._reset_btn.setEnabled(True)
|
||||
self._stop_btn.setEnabled(False)
|
||||
self._validate_btn.setEnabled(True)
|
||||
self._publish_btn.setEnabled(True)
|
||||
|
||||
self._progress_widget.setValue(self.controller.publish_progress)
|
||||
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)
|
||||
self._main_label.setText("Publishing...")
|
||||
self._report_btns_widget.setVisible(False)
|
||||
|
||||
self._reset_btn.setEnabled(False)
|
||||
self._stop_btn.setEnabled(True)
|
||||
self._validate_btn.setEnabled(False)
|
||||
self._publish_btn.setEnabled(False)
|
||||
|
||||
def _on_publish_validated(self):
|
||||
self._validate_btn.setEnabled(False)
|
||||
|
||||
def _on_instance_change(self, context, instance):
|
||||
"""Change instance label when instance is going to be processed."""
|
||||
if instance is None:
|
||||
new_name = (
|
||||
context.data.get("label")
|
||||
or context.data.get("name")
|
||||
or "Context"
|
||||
)
|
||||
else:
|
||||
new_name = (
|
||||
instance.data.get("label")
|
||||
or instance.data["name"]
|
||||
)
|
||||
|
||||
self._instance_label.setText(new_name)
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
def _on_plugin_change(self, plugin):
|
||||
"""Change plugin label when instance is going to be processed."""
|
||||
plugin_name = plugin.__name__
|
||||
if hasattr(plugin, "label") and plugin.label:
|
||||
plugin_name = plugin.label
|
||||
|
||||
self._progress_widget.setValue(self.controller.publish_progress)
|
||||
self._plugin_label.setText(plugin_name)
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
def _on_publish_stop(self):
|
||||
self._progress_widget.setValue(self.controller.publish_progress)
|
||||
self._report_btns_widget.setVisible(True)
|
||||
|
||||
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:
|
||||
validate_enabled = not self.controller.publish_has_validated
|
||||
if publish_enabled:
|
||||
if (
|
||||
self.controller.publish_has_validated
|
||||
and self.controller.publish_has_validation_errors
|
||||
):
|
||||
publish_enabled = False
|
||||
|
||||
else:
|
||||
publish_enabled = not self.controller.publish_has_finished
|
||||
|
||||
self._validate_btn.setEnabled(validate_enabled)
|
||||
self._publish_btn.setEnabled(publish_enabled)
|
||||
|
||||
error = self.controller.get_publish_crash_error()
|
||||
validation_errors = self.controller.get_validation_errors()
|
||||
if error:
|
||||
self._set_error(error)
|
||||
|
||||
elif validation_errors:
|
||||
self._set_progress_visibility(False)
|
||||
self._change_bg_property(1)
|
||||
self._set_validation_errors(validation_errors)
|
||||
|
||||
elif self.controller.publish_has_finished:
|
||||
self._set_finished()
|
||||
|
||||
else:
|
||||
self._set_stopped()
|
||||
|
||||
def _set_stopped(self):
|
||||
main_label = "Publish paused"
|
||||
if self.controller.publish_has_validated:
|
||||
main_label += " - Validation passed"
|
||||
|
||||
self._main_label.setText(main_label)
|
||||
self._message_label_top.setText(
|
||||
"Hit publish (play button) to continue."
|
||||
)
|
||||
|
||||
self._set_success_property(-1)
|
||||
|
||||
def _set_error(self, error):
|
||||
self._main_label.setText("Error happened")
|
||||
if isinstance(error, KnownPublishError):
|
||||
msg = str(error)
|
||||
else:
|
||||
msg = (
|
||||
"Something went wrong. Send report"
|
||||
" to your supervisor or OpenPype."
|
||||
)
|
||||
self._message_label_top.setText(msg)
|
||||
self._message_label_bottom.setText("")
|
||||
self._set_success_property(0)
|
||||
|
||||
def _set_validation_errors(self, validation_errors):
|
||||
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("")
|
||||
self._message_label_bottom.setText("")
|
||||
self._set_success_property(1)
|
||||
|
||||
def _change_bg_property(self, state=None):
|
||||
self.setProperty("state", str(state or ""))
|
||||
self.style().polish(self)
|
||||
|
||||
def _set_progress_visibility(self, visible):
|
||||
self._instance_label.setVisible(visible)
|
||||
self._plugin_label.setVisible(visible)
|
||||
self._progress_widget.setVisible(visible)
|
||||
self._message_label_top.setVisible(visible)
|
||||
|
||||
def _set_success_property(self, state=None):
|
||||
if state is None:
|
||||
state = ""
|
||||
else:
|
||||
state = str(state)
|
||||
|
||||
for widget in (self._progress_widget, self._info_frame):
|
||||
if widget.property("state") != state:
|
||||
widget.setProperty("state", state)
|
||||
widget.style().polish(widget)
|
||||
|
||||
def _on_copy_report(self):
|
||||
logs = self.controller.get_publish_report()
|
||||
logs_string = json.dumps(logs, indent=4)
|
||||
|
||||
mime_data = QtCore.QMimeData()
|
||||
mime_data.setText(logs_string)
|
||||
QtWidgets.QApplication.instance().clipboard().setMimeData(
|
||||
mime_data
|
||||
)
|
||||
|
||||
def _on_export_report(self):
|
||||
default_filename = "publish-report-{}".format(
|
||||
time.strftime("%y%m%d-%H-%M")
|
||||
)
|
||||
default_filepath = os.path.join(
|
||||
os.path.expanduser("~"),
|
||||
default_filename
|
||||
)
|
||||
new_filepath, ext = QtWidgets.QFileDialog.getSaveFileName(
|
||||
self, "Save report", default_filepath, ".json"
|
||||
)
|
||||
if not ext or not new_filepath:
|
||||
return
|
||||
|
||||
logs = self.controller.get_publish_report()
|
||||
full_path = new_filepath + ext
|
||||
dir_path = os.path.dirname(full_path)
|
||||
if not os.path.exists(dir_path):
|
||||
os.makedirs(dir_path)
|
||||
|
||||
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()
|
||||
|
||||
def _on_stop_clicked(self):
|
||||
self.controller.stop_publish()
|
||||
|
||||
def _on_validate_clicked(self):
|
||||
self.controller.validate()
|
||||
|
||||
def _on_publish_clicked(self):
|
||||
self.controller.publish()
|
||||
95
openpype/tools/publisher/widgets/tabs_widget.py
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
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_identifier = None
|
||||
self._buttons_by_identifier = {}
|
||||
|
||||
def is_current_tab(self, identifier):
|
||||
if isinstance(identifier, PublisherTabBtn):
|
||||
identifier = identifier.identifier
|
||||
return self._current_identifier == 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_identifier 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_identifier:
|
||||
return
|
||||
|
||||
new_btn = self._buttons_by_identifier.get(identifier)
|
||||
if new_btn is None:
|
||||
return
|
||||
|
||||
old_identifier = self._current_identifier
|
||||
old_btn = self._buttons_by_identifier.get(old_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)
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ except Exception:
|
|||
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
|
||||
from openpype.tools.utils import BaseClickableFrame
|
||||
from openpype.tools.utils import BaseClickableFrame, ClickableFrame
|
||||
from .widgets import (
|
||||
IconValuePixmapLabel
|
||||
)
|
||||
|
|
@ -60,9 +60,8 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget):
|
|||
self._error_info = error_info
|
||||
self._selected = False
|
||||
|
||||
title_frame = BaseClickableFrame(self)
|
||||
title_frame = ClickableFrame(self)
|
||||
title_frame.setObjectName("ValidationErrorTitleFrame")
|
||||
title_frame._mouse_release_callback = self._mouse_release_callback
|
||||
|
||||
toggle_instance_btn = QtWidgets.QToolButton(title_frame)
|
||||
toggle_instance_btn.setObjectName("ArrowBtn")
|
||||
|
|
@ -72,14 +71,15 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget):
|
|||
label_widget = QtWidgets.QLabel(error_info["title"], title_frame)
|
||||
|
||||
title_frame_layout = QtWidgets.QHBoxLayout(title_frame)
|
||||
title_frame_layout.addWidget(toggle_instance_btn)
|
||||
title_frame_layout.addWidget(label_widget)
|
||||
title_frame_layout.addWidget(label_widget, 1)
|
||||
title_frame_layout.addWidget(toggle_instance_btn, 0)
|
||||
|
||||
instances_model = QtGui.QStandardItemModel()
|
||||
error_info = error_info["error_info"]
|
||||
|
||||
help_text_by_instance_id = {}
|
||||
context_validation = False
|
||||
items = []
|
||||
if (
|
||||
not error_info
|
||||
or (len(error_info) == 1 and error_info[0][0] is None)
|
||||
|
|
@ -88,8 +88,10 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget):
|
|||
toggle_instance_btn.setArrowType(QtCore.Qt.NoArrow)
|
||||
description = self._prepare_description(error_info[0][1])
|
||||
help_text_by_instance_id[None] = description
|
||||
# Add fake item to have minimum size hint of view widget
|
||||
items.append(QtGui.QStandardItem("Context"))
|
||||
|
||||
else:
|
||||
items = []
|
||||
for instance, exception in error_info:
|
||||
label = instance.data.get("label") or instance.data.get("name")
|
||||
item = QtGui.QStandardItem(label)
|
||||
|
|
@ -102,29 +104,33 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget):
|
|||
description = self._prepare_description(exception)
|
||||
help_text_by_instance_id[instance.id] = description
|
||||
|
||||
instances_model.invisibleRootItem().appendRows(items)
|
||||
if items:
|
||||
root_item = instances_model.invisibleRootItem()
|
||||
root_item.appendRows(items)
|
||||
|
||||
instances_view = ValidationErrorInstanceList(self)
|
||||
instances_view.setModel(instances_model)
|
||||
instances_view.setVisible(False)
|
||||
|
||||
self.setLayoutDirection(QtCore.Qt.LeftToRight)
|
||||
|
||||
view_layout = QtWidgets.QHBoxLayout()
|
||||
view_widget = QtWidgets.QWidget(self)
|
||||
view_layout = QtWidgets.QHBoxLayout(view_widget)
|
||||
view_layout.setContentsMargins(0, 0, 0, 0)
|
||||
view_layout.setSpacing(0)
|
||||
view_layout.addSpacing(14)
|
||||
view_layout.addWidget(instances_view)
|
||||
view_layout.addWidget(instances_view, 0)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.setSpacing(0)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(title_frame)
|
||||
layout.addLayout(view_layout)
|
||||
layout.addWidget(title_frame, 0)
|
||||
layout.addWidget(view_widget, 0)
|
||||
view_widget.setVisible(False)
|
||||
|
||||
if not context_validation:
|
||||
toggle_instance_btn.clicked.connect(self._on_toggle_btn_click)
|
||||
|
||||
title_frame.clicked.connect(self._mouse_release_callback)
|
||||
instances_view.selectionModel().selectionChanged.connect(
|
||||
self._on_seleciton_change
|
||||
)
|
||||
|
|
@ -133,7 +139,7 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget):
|
|||
|
||||
self._toggle_instance_btn = toggle_instance_btn
|
||||
|
||||
self._view_layout = view_layout
|
||||
self._view_widget = view_widget
|
||||
|
||||
self._instances_model = instances_model
|
||||
self._instances_view = instances_view
|
||||
|
|
@ -141,17 +147,21 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget):
|
|||
self._context_validation = context_validation
|
||||
self._help_text_by_instance_id = help_text_by_instance_id
|
||||
|
||||
self._expanded = False
|
||||
|
||||
def sizeHint(self):
|
||||
result = super(ValidationErrorTitleWidget, self).sizeHint()
|
||||
expected_width = 0
|
||||
for idx in range(self._view_layout.count()):
|
||||
expected_width += self._view_layout.itemAt(idx).sizeHint().width()
|
||||
expected_width = max(
|
||||
self._view_widget.minimumSizeHint().width(),
|
||||
self._view_widget.sizeHint().width()
|
||||
)
|
||||
|
||||
if expected_width < 200:
|
||||
expected_width = 200
|
||||
|
||||
if result.width() < expected_width:
|
||||
result.setWidth(expected_width)
|
||||
|
||||
return result
|
||||
|
||||
def minimumSizeHint(self):
|
||||
|
|
@ -170,6 +180,7 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget):
|
|||
|
||||
def _mouse_release_callback(self):
|
||||
"""Mark this widget as selected on click."""
|
||||
|
||||
self.set_selected(True)
|
||||
|
||||
def current_desctiption_text(self):
|
||||
|
|
@ -208,25 +219,44 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget):
|
|||
if selected is None:
|
||||
selected = not self._selected
|
||||
|
||||
elif selected == self._selected:
|
||||
if not selected:
|
||||
self._instances_view.clearSelection()
|
||||
|
||||
if selected == self._selected:
|
||||
return
|
||||
|
||||
self._selected = selected
|
||||
self._change_style_property(selected)
|
||||
if selected:
|
||||
self.selected.emit(self._index)
|
||||
self._set_expanded(True)
|
||||
|
||||
def _on_toggle_btn_click(self):
|
||||
"""Show/hide instances list."""
|
||||
new_visible = not self._instances_view.isVisible()
|
||||
self._instances_view.setVisible(new_visible)
|
||||
if new_visible:
|
||||
|
||||
self._set_expanded()
|
||||
|
||||
def _set_expanded(self, expanded=None):
|
||||
if expanded is None:
|
||||
expanded = not self._expanded
|
||||
|
||||
elif expanded is self._expanded:
|
||||
return
|
||||
|
||||
if expanded and self._context_validation:
|
||||
return
|
||||
|
||||
self._expanded = expanded
|
||||
self._view_widget.setVisible(expanded)
|
||||
if expanded:
|
||||
self._toggle_instance_btn.setArrowType(QtCore.Qt.DownArrow)
|
||||
else:
|
||||
self._toggle_instance_btn.setArrowType(QtCore.Qt.RightArrow)
|
||||
|
||||
def _on_seleciton_change(self):
|
||||
self.instance_changed.emit(self._index)
|
||||
sel_model = self._instances_view.selectionModel()
|
||||
if sel_model.selectedIndexes():
|
||||
self.instance_changed.emit(self._index)
|
||||
|
||||
|
||||
class ActionButton(BaseClickableFrame):
|
||||
|
|
@ -400,7 +430,21 @@ class VerticallScrollArea(QtWidgets.QScrollArea):
|
|||
return super(VerticallScrollArea, self).eventFilter(obj, event)
|
||||
|
||||
|
||||
class ValidationsWidget(QtWidgets.QWidget):
|
||||
class ValidationArtistMessage(QtWidgets.QWidget):
|
||||
def __init__(self, message, parent):
|
||||
super(ValidationArtistMessage, self).__init__(parent)
|
||||
|
||||
artist_msg_label = QtWidgets.QLabel(message, self)
|
||||
artist_msg_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
main_layout = QtWidgets.QHBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.addWidget(
|
||||
artist_msg_label, 1, QtCore.Qt.AlignCenter
|
||||
)
|
||||
|
||||
|
||||
class ValidationsWidget(QtWidgets.QFrame):
|
||||
"""Widgets showing validation error.
|
||||
|
||||
This widget is shown if validation error/s happened during validation part.
|
||||
|
|
@ -414,16 +458,35 @@ class ValidationsWidget(QtWidgets.QWidget):
|
|||
│ │ Error detail │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
├──────┴────────────────┴───────┤
|
||||
│ Publish buttons │
|
||||
└───────────────────────────────┘
|
||||
└──────┴────────────────┴───────┘
|
||||
"""
|
||||
|
||||
def __init__(self, controller, parent):
|
||||
super(ValidationsWidget, self).__init__(parent)
|
||||
|
||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
# Before publishing
|
||||
before_publish_widget = ValidationArtistMessage(
|
||||
"Nothing to report until you run publish", self
|
||||
)
|
||||
# After success publishing
|
||||
publish_started_widget = ValidationArtistMessage(
|
||||
"Publishing went smoothly", self
|
||||
)
|
||||
# After success publishing
|
||||
publish_stop_ok_widget = ValidationArtistMessage(
|
||||
"Publishing finished successfully", self
|
||||
)
|
||||
# After failed publishing (not with validation error)
|
||||
publish_stop_fail_widget = ValidationArtistMessage(
|
||||
"This is not your fault...", self
|
||||
)
|
||||
|
||||
errors_scroll = VerticallScrollArea(self)
|
||||
# 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)
|
||||
|
|
@ -433,35 +496,64 @@ class ValidationsWidget(QtWidgets.QWidget):
|
|||
|
||||
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.event_system.add_callback(
|
||||
"publish.process.started", self._on_publish_start
|
||||
)
|
||||
controller.event_system.add_callback(
|
||||
"publish.reset.finished", self._on_publish_reset
|
||||
)
|
||||
controller.event_system.add_callback(
|
||||
"publish.process.stopped", 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
|
||||
|
|
@ -474,6 +566,8 @@ class ValidationsWidget(QtWidgets.QWidget):
|
|||
self._error_info = {}
|
||||
self._previous_select = None
|
||||
|
||||
self._controller = controller
|
||||
|
||||
def clear(self):
|
||||
"""Delete all dynamic widgets and hide all wrappers."""
|
||||
self._title_widgets = {}
|
||||
|
|
@ -537,6 +631,32 @@ class ValidationsWidget(QtWidgets.QWidget):
|
|||
|
||||
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:
|
||||
|
|
@ -553,8 +673,9 @@ class ValidationsWidget(QtWidgets.QWidget):
|
|||
|
||||
def _on_instance_change(self, index):
|
||||
if self._previous_select and self._previous_select.index != index:
|
||||
return
|
||||
self._update_description()
|
||||
self._title_widgets[index].set_selected(True)
|
||||
else:
|
||||
self._update_description()
|
||||
|
||||
def _update_description(self):
|
||||
description = self._previous_select.current_desctiption_text()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import os
|
||||
import re
|
||||
import copy
|
||||
import functools
|
||||
import collections
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
import qtawesome
|
||||
|
|
@ -182,6 +183,16 @@ class PublishIconBtn(IconButton):
|
|||
return pixmap
|
||||
|
||||
|
||||
class CreateBtn(PublishIconBtn):
|
||||
"""Create instance button."""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
icon_path = get_icon_path("create")
|
||||
super(CreateBtn, self).__init__(icon_path, "Create", parent)
|
||||
self.setToolTip("Create new subset/s")
|
||||
self.setLayoutDirection(QtCore.Qt.RightToLeft)
|
||||
|
||||
|
||||
class ResetBtn(PublishIconBtn):
|
||||
"""Publish reset button."""
|
||||
def __init__(self, parent=None):
|
||||
|
|
@ -222,28 +233,34 @@ class CreateInstanceBtn(PublishIconBtn):
|
|||
self.setToolTip("Create new instance")
|
||||
|
||||
|
||||
class CopyPublishReportBtn(PublishIconBtn):
|
||||
"""Copy report button."""
|
||||
def __init__(self, parent=None):
|
||||
icon_path = get_icon_path("copy")
|
||||
super(CopyPublishReportBtn, self).__init__(icon_path, parent)
|
||||
self.setToolTip("Copy report")
|
||||
class PublishReportBtn(PublishIconBtn):
|
||||
"""Publish report button."""
|
||||
|
||||
triggered = QtCore.Signal(str)
|
||||
|
||||
class SavePublishReportBtn(PublishIconBtn):
|
||||
"""Save report button."""
|
||||
def __init__(self, parent=None):
|
||||
icon_path = get_icon_path("download_arrow")
|
||||
super(SavePublishReportBtn, self).__init__(icon_path, parent)
|
||||
self.setToolTip("Export and save report")
|
||||
|
||||
|
||||
class ShowPublishReportBtn(PublishIconBtn):
|
||||
"""Show report button."""
|
||||
def __init__(self, parent=None):
|
||||
icon_path = get_icon_path("view_report")
|
||||
super(ShowPublishReportBtn, self).__init__(icon_path, parent)
|
||||
self.setToolTip("Show details")
|
||||
super(PublishReportBtn, self).__init__(icon_path, parent)
|
||||
self.setToolTip("Copy report")
|
||||
self._actions = []
|
||||
|
||||
def add_action(self, label, identifier):
|
||||
action = QtWidgets.QAction(label)
|
||||
action.setData(identifier)
|
||||
action.triggered.connect(
|
||||
functools.partial(self._on_action_trigger, action)
|
||||
)
|
||||
self._actions.append(action)
|
||||
|
||||
def _on_action_trigger(self, action):
|
||||
identifier = action.data()
|
||||
self.triggered.emit(identifier)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
super(PublishReportBtn, self).mouseReleaseEvent(event)
|
||||
menu = QtWidgets.QMenu(self)
|
||||
menu.addActions(self._actions)
|
||||
menu.exec_(event.globalPos())
|
||||
|
||||
|
||||
class RemoveInstanceBtn(PublishIconBtn):
|
||||
|
|
|
|||
|
|
@ -6,32 +6,35 @@ from openpype import (
|
|||
)
|
||||
from openpype.tools.utils import (
|
||||
PlaceholderLineEdit,
|
||||
PixmapLabel
|
||||
MessageOverlayObject,
|
||||
PixmapLabel,
|
||||
)
|
||||
|
||||
from .publish_report_viewer import PublishReportViewerWidget
|
||||
from .control import PublisherController
|
||||
from .widgets import (
|
||||
BorderedLabelWidget,
|
||||
OverviewWidget,
|
||||
ValidationsWidget,
|
||||
PublishFrame,
|
||||
SubsetAttributesWidget,
|
||||
InstanceCardView,
|
||||
InstanceListView,
|
||||
CreateDialog,
|
||||
|
||||
PublisherTabsWidget,
|
||||
|
||||
StopBtn,
|
||||
ResetBtn,
|
||||
ValidateBtn,
|
||||
PublishBtn,
|
||||
|
||||
CreateInstanceBtn,
|
||||
RemoveInstanceBtn,
|
||||
ChangeViewBtn
|
||||
HelpButton,
|
||||
HelpDialog,
|
||||
)
|
||||
|
||||
|
||||
class PublisherWindow(QtWidgets.QDialog):
|
||||
"""Main window of publisher."""
|
||||
default_width = 1200
|
||||
default_height = 700
|
||||
default_width = 1300
|
||||
default_height = 800
|
||||
footer_border = 8
|
||||
publish_footer_spacer = 2
|
||||
|
||||
def __init__(self, parent=None, reset_on_show=None):
|
||||
super(PublisherWindow, self).__init__(parent)
|
||||
|
|
@ -58,122 +61,121 @@ class PublisherWindow(QtWidgets.QDialog):
|
|||
| on_top_flag
|
||||
)
|
||||
|
||||
self._reset_on_show = reset_on_show
|
||||
self._first_show = True
|
||||
self._refreshing_instances = False
|
||||
|
||||
controller = PublisherController()
|
||||
|
||||
help_dialog = HelpDialog(controller, self)
|
||||
|
||||
overlay_object = MessageOverlayObject(self)
|
||||
|
||||
# Header
|
||||
header_widget = QtWidgets.QWidget(self)
|
||||
|
||||
icon_pixmap = QtGui.QPixmap(resources.get_openpype_icon_filepath())
|
||||
icon_label = PixmapLabel(icon_pixmap, header_widget)
|
||||
icon_label.setObjectName("PublishContextLabel")
|
||||
|
||||
context_label = QtWidgets.QLabel(header_widget)
|
||||
context_label.setObjectName("PublishContextLabel")
|
||||
|
||||
header_extra_widget = QtWidgets.QWidget(header_widget)
|
||||
|
||||
help_btn = HelpButton(header_widget)
|
||||
|
||||
header_layout = QtWidgets.QHBoxLayout(header_widget)
|
||||
header_layout.setContentsMargins(15, 15, 15, 15)
|
||||
header_layout.setContentsMargins(15, 15, 0, 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)
|
||||
header_layout.addWidget(header_extra_widget, 0)
|
||||
header_layout.addWidget(help_btn, 0)
|
||||
|
||||
line_widget = QtWidgets.QWidget(self)
|
||||
line_widget.setObjectName("Separator")
|
||||
line_widget.setMinimumHeight(2)
|
||||
# Tabs widget under header
|
||||
tabs_widget = PublisherTabsWidget(self)
|
||||
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")
|
||||
|
||||
# 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
|
||||
)
|
||||
|
||||
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)
|
||||
# Widget where is stacked publish overlay and widgets that should be
|
||||
# covered by it
|
||||
under_publish_stack = QtWidgets.QWidget(self)
|
||||
# Added wrap widget where all widgets under overlay are added
|
||||
# - this is because footer is also under overlay and the top part
|
||||
# is faked with floating frame
|
||||
under_publish_widget = QtWidgets.QWidget(under_publish_stack)
|
||||
|
||||
# Footer
|
||||
comment_input = PlaceholderLineEdit(subset_frame)
|
||||
footer_widget = QtWidgets.QWidget(under_publish_widget)
|
||||
footer_bottom_widget = QtWidgets.QWidget(footer_widget)
|
||||
|
||||
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_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)
|
||||
|
||||
# Subset frame layout
|
||||
subset_layout = QtWidgets.QVBoxLayout(subset_frame)
|
||||
marings = subset_layout.contentsMargins()
|
||||
# Spacer helps keep distance of Publish Frame when comment input
|
||||
# is hidden - so when is shrunken it is not overlaying pages
|
||||
footer_spacer = QtWidgets.QWidget(footer_widget)
|
||||
footer_spacer.setMinimumHeight(self.publish_footer_spacer)
|
||||
footer_spacer.setMaximumHeight(self.publish_footer_spacer)
|
||||
footer_spacer.setVisible(False)
|
||||
|
||||
footer_layout = QtWidgets.QVBoxLayout(footer_widget)
|
||||
footer_margins = footer_layout.contentsMargins()
|
||||
|
||||
footer_layout.setContentsMargins(
|
||||
footer_margins.left() + self.footer_border,
|
||||
footer_margins.top(),
|
||||
footer_margins.right() + self.footer_border,
|
||||
footer_margins.bottom() + self.footer_border
|
||||
)
|
||||
|
||||
footer_layout.addWidget(comment_input, 0)
|
||||
footer_layout.addWidget(footer_spacer, 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(under_publish_widget)
|
||||
|
||||
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(marings.bottom() * 2)
|
||||
subset_layout.setContentsMargins(marings)
|
||||
subset_layout.addWidget(subset_content_widget, 1)
|
||||
subset_layout.addLayout(footer_layout, 0)
|
||||
marings.setBottom(0)
|
||||
content_layout.setContentsMargins(marings)
|
||||
content_layout.addWidget(content_stacked_widget, 1)
|
||||
|
||||
# Create publish frame
|
||||
publish_frame = PublishFrame(controller, content_stacked_widget)
|
||||
# Overview - create and attributes part
|
||||
overview_widget = OverviewWidget(
|
||||
controller, content_stacked_widget
|
||||
)
|
||||
|
||||
report_widget = ValidationsWidget(controller, parent)
|
||||
|
||||
# Details - Publish details
|
||||
publish_details_widget = PublishReportViewerWidget(
|
||||
content_stacked_widget
|
||||
)
|
||||
|
||||
content_stacked_layout = QtWidgets.QStackedLayout(
|
||||
content_stacked_widget
|
||||
|
|
@ -182,282 +184,348 @@ class PublisherWindow(QtWidgets.QDialog):
|
|||
content_stacked_layout.setStackingMode(
|
||||
QtWidgets.QStackedLayout.StackAll
|
||||
)
|
||||
content_stacked_layout.addWidget(subset_frame)
|
||||
content_stacked_layout.addWidget(publish_frame)
|
||||
content_stacked_layout.addWidget(overview_widget)
|
||||
content_stacked_layout.addWidget(report_widget)
|
||||
content_stacked_layout.addWidget(publish_details_widget)
|
||||
content_stacked_layout.setCurrentWidget(overview_widget)
|
||||
|
||||
under_publish_layout = QtWidgets.QVBoxLayout(under_publish_widget)
|
||||
under_publish_layout.setContentsMargins(0, 0, 0, 0)
|
||||
under_publish_layout.setSpacing(0)
|
||||
under_publish_layout.addWidget(content_widget, 1)
|
||||
under_publish_layout.addWidget(footer_widget, 0)
|
||||
|
||||
# Overlay which covers inputs during publishing
|
||||
publish_overlay = QtWidgets.QFrame(under_publish_stack)
|
||||
publish_overlay.setObjectName("OverlayFrame")
|
||||
|
||||
under_publish_stack_layout = QtWidgets.QStackedLayout(
|
||||
under_publish_stack
|
||||
)
|
||||
under_publish_stack_layout.setContentsMargins(0, 0, 0, 0)
|
||||
under_publish_stack_layout.setStackingMode(
|
||||
QtWidgets.QStackedLayout.StackAll
|
||||
)
|
||||
under_publish_stack_layout.addWidget(under_publish_widget)
|
||||
under_publish_stack_layout.addWidget(publish_overlay)
|
||||
under_publish_stack_layout.setCurrentWidget(under_publish_widget)
|
||||
|
||||
# Add main frame to this window
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
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(content_stacked_widget, 1)
|
||||
main_layout.addWidget(tabs_widget, 0)
|
||||
main_layout.addWidget(under_publish_stack, 1)
|
||||
|
||||
creator_window = CreateDialog(controller, parent=self)
|
||||
# Floating publish frame
|
||||
publish_frame = PublishFrame(controller, self.footer_border, self)
|
||||
|
||||
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)
|
||||
help_btn.clicked.connect(self._on_help_click)
|
||||
tabs_widget.tab_changed.connect(self._on_tab_change)
|
||||
overview_widget.active_changed.connect(
|
||||
self._on_context_or_active_change
|
||||
)
|
||||
overview_widget.instance_context_changed.connect(
|
||||
self._on_context_or_active_change
|
||||
)
|
||||
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
|
||||
publish_frame.details_page_requested.connect(self._go_to_details_tab)
|
||||
|
||||
controller.event_system.add_callback(
|
||||
"instances.refresh.finished", self._on_instances_refresh
|
||||
)
|
||||
subset_view_cards.selection_changed.connect(
|
||||
self._on_subset_change
|
||||
controller.event_system.add_callback(
|
||||
"publish.reset.finished", self._on_publish_reset
|
||||
)
|
||||
# Active instances changed
|
||||
subset_list_view.active_changed.connect(
|
||||
self._on_active_changed
|
||||
controller.event_system.add_callback(
|
||||
"publish.process.started", self._on_publish_start
|
||||
)
|
||||
subset_view_cards.active_changed.connect(
|
||||
self._on_active_changed
|
||||
controller.event_system.add_callback(
|
||||
"publish.process.validated", self._on_publish_validated
|
||||
)
|
||||
# Instance context has changed
|
||||
subset_attributes_widget.instance_context_changed.connect(
|
||||
self._on_instance_context_change
|
||||
controller.event_system.add_callback(
|
||||
"publish.process.stopped", self._on_publish_stop
|
||||
)
|
||||
controller.event_system.add_callback(
|
||||
"show.card.message", self._on_overlay_message
|
||||
)
|
||||
|
||||
controller.add_instances_refresh_callback(self._on_instances_refresh)
|
||||
# Store extra header widget for TrayPublisher
|
||||
# - can be used to add additional widgets to header between context
|
||||
# label and help button
|
||||
self._help_dialog = help_dialog
|
||||
self._help_btn = help_btn
|
||||
|
||||
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)
|
||||
controller.add_publish_stopped_callback(self._on_publish_stop)
|
||||
self._header_extra_widget = header_extra_widget
|
||||
|
||||
# Store header for TrayPublisher
|
||||
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
|
||||
self.publish_frame = publish_frame
|
||||
self.subset_frame = subset_frame
|
||||
self.subset_content_widget = subset_content_widget
|
||||
self._under_publish_stack_layout = under_publish_stack_layout
|
||||
|
||||
self.context_label = context_label
|
||||
self._under_publish_widget = under_publish_widget
|
||||
self._publish_overlay = publish_overlay
|
||||
self._publish_frame = publish_frame
|
||||
|
||||
self.subset_view_cards = subset_view_cards
|
||||
self.subset_list_view = subset_list_view
|
||||
self.subset_views_layout = subset_views_layout
|
||||
self._content_stacked_layout = content_stacked_layout
|
||||
|
||||
self.delete_btn = delete_btn
|
||||
self._overview_widget = overview_widget
|
||||
self._report_widget = report_widget
|
||||
self._publish_details_widget = publish_details_widget
|
||||
|
||||
self.subset_attributes_widget = subset_attributes_widget
|
||||
self._context_label = context_label
|
||||
|
||||
self.comment_input = comment_input
|
||||
self._comment_input = comment_input
|
||||
self._footer_spacer = footer_spacer
|
||||
|
||||
self.stop_btn = stop_btn
|
||||
self.reset_btn = reset_btn
|
||||
self.validate_btn = validate_btn
|
||||
self.publish_btn = publish_btn
|
||||
self._stop_btn = stop_btn
|
||||
self._reset_btn = reset_btn
|
||||
self._validate_btn = validate_btn
|
||||
self._publish_btn = publish_btn
|
||||
|
||||
self.controller = controller
|
||||
self._overlay_object = overlay_object
|
||||
|
||||
self.creator_window = creator_window
|
||||
self._controller = controller
|
||||
|
||||
self._first_show = True
|
||||
self._reset_on_show = reset_on_show
|
||||
self._restart_timer = None
|
||||
self._publish_frame_visible = None
|
||||
|
||||
self._set_publish_visibility(False)
|
||||
|
||||
@property
|
||||
def controller(self):
|
||||
return self._controller
|
||||
|
||||
def showEvent(self, event):
|
||||
super(PublisherWindow, self).showEvent(event)
|
||||
if self._first_show:
|
||||
self._first_show = False
|
||||
self.resize(self.default_width, self.default_height)
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
if self._reset_on_show:
|
||||
self.reset()
|
||||
self._on_first_show()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(PublisherWindow, self).resizeEvent(event)
|
||||
self._update_publish_frame_rect()
|
||||
|
||||
def _on_overlay_message(self, event):
|
||||
self._overlay_object.add_message(event["message"])
|
||||
|
||||
def _on_first_show(self):
|
||||
self.resize(self.default_width, self.default_height)
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
if not self._reset_on_show:
|
||||
return
|
||||
|
||||
# Detach showing - give OS chance to draw the window
|
||||
timer = QtCore.QTimer()
|
||||
timer.setSingleShot(True)
|
||||
timer.setInterval(1)
|
||||
timer.timeout.connect(self._on_show_restart_timer)
|
||||
self._restart_timer = timer
|
||||
timer.start()
|
||||
|
||||
def _on_show_restart_timer(self):
|
||||
"""Callback for '_restart_timer' timer."""
|
||||
|
||||
self._restart_timer = None
|
||||
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)
|
||||
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_create_clicked(self):
|
||||
self.creator_window.show()
|
||||
|
||||
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 _set_publish_visibility(self, visible):
|
||||
if visible:
|
||||
widget = self.publish_frame
|
||||
publish_frame_visible = True
|
||||
else:
|
||||
widget = self.subset_frame
|
||||
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()
|
||||
|
||||
def _on_stop_clicked(self):
|
||||
self.controller.stop_publish()
|
||||
|
||||
def _set_publish_comment(self):
|
||||
if self.controller.publish_comment_is_set:
|
||||
def _update_publish_details_widget(self, force=False):
|
||||
if not force and self._tabs_widget.current_tab() != "details":
|
||||
return
|
||||
|
||||
comment = self.comment_input.text()
|
||||
self.controller.set_comment(comment)
|
||||
report_data = self.controller.get_publish_report()
|
||||
self._publish_details_widget.set_report_data(report_data)
|
||||
|
||||
def _on_help_click(self):
|
||||
if self._help_dialog.isVisible():
|
||||
return
|
||||
|
||||
self._help_dialog.show()
|
||||
|
||||
window = self.window()
|
||||
desktop = QtWidgets.QApplication.desktop()
|
||||
screen_idx = desktop.screenNumber(window)
|
||||
screen = desktop.screen(screen_idx)
|
||||
screen_rect = screen.geometry()
|
||||
|
||||
window_geo = window.geometry()
|
||||
dialog_x = window_geo.x() + window_geo.width()
|
||||
dialog_right = (dialog_x + self._help_dialog.width()) - 1
|
||||
diff = dialog_right - screen_rect.right()
|
||||
if diff > 0:
|
||||
dialog_x -= diff
|
||||
|
||||
self._help_dialog.setGeometry(
|
||||
dialog_x, window_geo.y(),
|
||||
self._help_dialog.width(), self._help_dialog.height()
|
||||
)
|
||||
|
||||
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"):
|
||||
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
|
||||
)
|
||||
self._update_publish_details_widget()
|
||||
|
||||
elif new_tab == "report":
|
||||
self._content_stacked_layout.setCurrentWidget(
|
||||
self._report_widget
|
||||
)
|
||||
|
||||
def _on_context_or_active_change(self):
|
||||
self._validate_create_instances()
|
||||
|
||||
def _on_create_request(self):
|
||||
self._go_to_create_tab()
|
||||
|
||||
def _go_to_create_tab(self):
|
||||
self._tabs_widget.set_current_tab("create")
|
||||
|
||||
def _go_to_details_tab(self):
|
||||
self._tabs_widget.set_current_tab("details")
|
||||
|
||||
def _go_to_report_tab(self):
|
||||
self._tabs_widget.set_current_tab("report")
|
||||
|
||||
def _set_publish_overlay_visibility(self, visible):
|
||||
if visible:
|
||||
widget = self._publish_overlay
|
||||
else:
|
||||
widget = self._under_publish_widget
|
||||
self._under_publish_stack_layout.setCurrentWidget(widget)
|
||||
|
||||
def _set_publish_visibility(self, visible):
|
||||
if visible is self._publish_frame_visible:
|
||||
return
|
||||
self._publish_frame_visible = visible
|
||||
self._publish_frame.setVisible(visible)
|
||||
self._update_publish_frame_rect()
|
||||
|
||||
def _on_reset_clicked(self):
|
||||
self._controller.reset()
|
||||
|
||||
def _on_stop_clicked(self):
|
||||
self._controller.stop_publish()
|
||||
|
||||
def _set_publish_comment(self):
|
||||
if self._controller.publish_comment_is_set:
|
||||
return
|
||||
|
||||
comment = self._comment_input.text()
|
||||
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)
|
||||
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._set_comment_input_visiblity(True)
|
||||
self._set_publish_overlay_visibility(False)
|
||||
self._set_publish_visibility(False)
|
||||
self._set_footer_enabled(False)
|
||||
self._update_publish_details_widget()
|
||||
|
||||
def _on_publish_start(self):
|
||||
self._create_tab.setEnabled(False)
|
||||
|
||||
self._reset_btn.setEnabled(False)
|
||||
self._stop_btn.setEnabled(True)
|
||||
self._validate_btn.setEnabled(False)
|
||||
self._publish_btn.setEnabled(False)
|
||||
|
||||
self._set_comment_input_visiblity(False)
|
||||
self._set_publish_visibility(True)
|
||||
self._set_publish_overlay_visibility(True)
|
||||
|
||||
self._publish_details_widget.close_details_popup()
|
||||
|
||||
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)
|
||||
|
||||
def _on_publish_stop(self):
|
||||
self._set_publish_overlay_visibility(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:
|
||||
validate_enabled = not self._controller.publish_has_validated
|
||||
if publish_enabled:
|
||||
if (
|
||||
self._controller.publish_has_validated
|
||||
and self._controller.publish_has_validation_errors
|
||||
):
|
||||
publish_enabled = False
|
||||
if self._tabs_widget.is_current_tab("publish"):
|
||||
self._go_to_report_tab()
|
||||
|
||||
else:
|
||||
publish_enabled = not self._controller.publish_has_finished
|
||||
|
||||
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:
|
||||
if not self._controller.host_is_valid:
|
||||
self._set_footer_enabled(True)
|
||||
return
|
||||
|
||||
all_valid = None
|
||||
for instance in self.controller.instances:
|
||||
for instance in self._controller.instances:
|
||||
if not instance["active"]:
|
||||
continue
|
||||
|
||||
|
|
@ -470,38 +538,29 @@ class PublisherWindow(QtWidgets.QDialog):
|
|||
|
||||
self._set_footer_enabled(bool(all_valid))
|
||||
|
||||
def _on_publish_reset(self):
|
||||
self._set_publish_visibility(False)
|
||||
def _on_instances_refresh(self):
|
||||
self._validate_create_instances()
|
||||
|
||||
self.subset_content_widget.setEnabled(self.controller.host_is_valid)
|
||||
context_title = self.controller.get_context_title()
|
||||
self.set_context_label(context_title)
|
||||
self._update_publish_details_widget()
|
||||
|
||||
self._set_footer_enabled(False)
|
||||
def _set_comment_input_visiblity(self, visible):
|
||||
self._comment_input.setVisible(visible)
|
||||
self._footer_spacer.setVisible(not visible)
|
||||
|
||||
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)
|
||||
def _update_publish_frame_rect(self):
|
||||
if not self._publish_frame_visible:
|
||||
return
|
||||
|
||||
def _on_publish_validated(self):
|
||||
self.validate_btn.setEnabled(False)
|
||||
window_size = self.size()
|
||||
size_hint = self._publish_frame.minimumSizeHint()
|
||||
|
||||
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
|
||||
if validate_enabled:
|
||||
validate_enabled = not self.controller.publish_has_validated
|
||||
if publish_enabled:
|
||||
if (
|
||||
self.controller.publish_has_validated
|
||||
and self.controller.publish_has_validation_errors
|
||||
):
|
||||
publish_enabled = False
|
||||
width = window_size.width()
|
||||
height = size_hint.height()
|
||||
|
||||
else:
|
||||
publish_enabled = not self.controller.publish_has_finished
|
||||
self._publish_frame.resize(width, height)
|
||||
|
||||
self.validate_btn.setEnabled(validate_enabled)
|
||||
self.publish_btn.setEnabled(publish_enabled)
|
||||
self._publish_frame.move(
|
||||
0, window_size.height() - height
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,25 +6,23 @@ Tray publisher can be considered as host implementeation with creators and
|
|||
publishing plugins.
|
||||
"""
|
||||
|
||||
import platform
|
||||
|
||||
from Qt import QtWidgets, QtCore
|
||||
import qtawesome
|
||||
import appdirs
|
||||
|
||||
from openpype.pipeline import (
|
||||
install_host,
|
||||
AvalonMongoDB,
|
||||
)
|
||||
from openpype.lib import JSONSettingRegistry
|
||||
from openpype.pipeline import install_host
|
||||
from openpype.hosts.traypublisher.api import TrayPublisherHost
|
||||
from openpype.tools.publisher import PublisherWindow
|
||||
from openpype.tools.publisher.window import PublisherWindow
|
||||
from openpype.tools.utils import PlaceholderLineEdit
|
||||
from openpype.tools.utils.constants import PROJECT_NAME_ROLE
|
||||
from openpype.tools.utils.models import (
|
||||
ProjectModel,
|
||||
ProjectSortFilterProxy
|
||||
)
|
||||
|
||||
from openpype.tools.utils import PlaceholderLineEdit
|
||||
import appdirs
|
||||
from openpype.lib import JSONSettingRegistry
|
||||
|
||||
|
||||
class TrayPublisherRegistry(JSONSettingRegistry):
|
||||
"""Class handling OpenPype general settings registry.
|
||||
|
|
@ -55,14 +53,10 @@ class StandaloneOverlayWidget(QtWidgets.QFrame):
|
|||
|
||||
content_widget = QtWidgets.QWidget(middle_frame)
|
||||
|
||||
# Create db connection for projects model
|
||||
dbcon = AvalonMongoDB()
|
||||
dbcon.install()
|
||||
|
||||
header_label = QtWidgets.QLabel("Choose project", content_widget)
|
||||
header_label.setObjectName("ChooseProjectLabel")
|
||||
# Create project models and view
|
||||
projects_model = ProjectModel(dbcon)
|
||||
projects_model = ProjectModel()
|
||||
projects_proxy = ProjectSortFilterProxy()
|
||||
projects_proxy.setSourceModel(projects_model)
|
||||
projects_proxy.setFilterKeyColumn(0)
|
||||
|
|
@ -137,11 +131,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)
|
||||
|
|
@ -193,7 +190,7 @@ class TrayPublishWindow(PublisherWindow):
|
|||
|
||||
overlay_widget = StandaloneOverlayWidget(self)
|
||||
|
||||
btns_widget = QtWidgets.QWidget(self)
|
||||
btns_widget = self._header_extra_widget
|
||||
|
||||
back_to_overlay_btn = QtWidgets.QPushButton(
|
||||
"Change project", btns_widget
|
||||
|
|
@ -208,8 +205,6 @@ class TrayPublishWindow(PublisherWindow):
|
|||
btns_layout.addWidget(save_btn, 0)
|
||||
btns_layout.addWidget(back_to_overlay_btn, 0)
|
||||
|
||||
self._header_layout.addWidget(btns_widget, 0)
|
||||
|
||||
overlay_widget.project_selected.connect(self._on_project_select)
|
||||
back_to_overlay_btn.clicked.connect(self._on_back_to_overlay)
|
||||
save_btn.clicked.connect(self._on_tray_publish_save)
|
||||
|
|
@ -239,22 +234,32 @@ 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:
|
||||
self._on_create_clicked()
|
||||
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")
|
||||
|
||||
|
||||
def main():
|
||||
host = TrayPublisherHost()
|
||||
install_host(host)
|
||||
app = QtWidgets.QApplication([])
|
||||
|
||||
app_instance = QtWidgets.QApplication.instance()
|
||||
if app_instance is None:
|
||||
app_instance = QtWidgets.QApplication([])
|
||||
|
||||
if platform.system().lower() == "windows":
|
||||
import ctypes
|
||||
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
|
||||
u"traypublisher"
|
||||
)
|
||||
|
||||
window = TrayPublishWindow()
|
||||
window.show()
|
||||
app.exec_()
|
||||
app_instance.exec_()
|
||||
|
|
|
|||
|
|
@ -122,6 +122,16 @@ class OverlayMessageWidget(QtWidgets.QFrame):
|
|||
self._timeout_timer = timeout_timer
|
||||
self._hover_timer = hover_timer
|
||||
|
||||
def update_message(self, message, message_type=None, timeout=None):
|
||||
self._label_widget.setText(message)
|
||||
if timeout:
|
||||
self._timeout_timer.setInterval(timeout)
|
||||
|
||||
if message_type:
|
||||
set_style_property(self, "type", message_type)
|
||||
|
||||
self._timeout_timer.start()
|
||||
|
||||
def size_hint_without_word_wrap(self):
|
||||
"""Size hint in cases that word wrap of label is disabled."""
|
||||
self._label_widget.setWordWrap(False)
|
||||
|
|
@ -195,7 +205,9 @@ class MessageOverlayObject(QtCore.QObject):
|
|||
self._move_size_remove = 8
|
||||
self._default_timeout = default_timeout
|
||||
|
||||
def add_message(self, message, message_type=None, timeout=None):
|
||||
def add_message(
|
||||
self, message, message_type=None, timeout=None, message_id=None
|
||||
):
|
||||
"""Add single message into overlay.
|
||||
|
||||
Args:
|
||||
|
|
@ -203,6 +215,12 @@ class MessageOverlayObject(QtCore.QObject):
|
|||
timeout (int): Message timeout.
|
||||
message_type (str): Message type can be used as property in
|
||||
stylesheets.
|
||||
message_id (str): UUID of already existing message to update
|
||||
it's message and timeout. Is created with different id if is
|
||||
not available anymore.
|
||||
|
||||
Returns:
|
||||
str: UUID of message which can be used to update message.
|
||||
"""
|
||||
# Skip empty messages
|
||||
if not message:
|
||||
|
|
@ -212,31 +230,48 @@ class MessageOverlayObject(QtCore.QObject):
|
|||
timeout = self._default_timeout
|
||||
|
||||
# Create unique id of message
|
||||
label_id = str(uuid.uuid4())
|
||||
# Create message widget
|
||||
widget = OverlayMessageWidget(
|
||||
label_id, message, self._widget, message_type, timeout
|
||||
)
|
||||
widget.close_requested.connect(self._on_message_close_request)
|
||||
widget.show()
|
||||
widget = None
|
||||
if message_id is not None:
|
||||
widget = self._messages.get(message_id)
|
||||
|
||||
if message_id is None:
|
||||
message_id = str(uuid.uuid4())
|
||||
|
||||
elif message_id in self._messages_order:
|
||||
self._messages_order.remove(message_id)
|
||||
|
||||
if widget is not None:
|
||||
# NOTE: Update of message won't change paint order which should be
|
||||
# ok in most of cases as it matters only when messages are
|
||||
# animated
|
||||
widget.update_message(message, message_type, timeout)
|
||||
else:
|
||||
# Create message widget
|
||||
widget = OverlayMessageWidget(
|
||||
message_id, message, self._widget, message_type, timeout
|
||||
)
|
||||
widget.close_requested.connect(self._on_message_close_request)
|
||||
widget.show()
|
||||
|
||||
# Move widget outside of window
|
||||
pos = widget.pos()
|
||||
pos.setY(pos.y() - widget.height())
|
||||
widget.move(pos)
|
||||
# Store message
|
||||
self._messages[label_id] = widget
|
||||
self._messages_order.append(label_id)
|
||||
self._messages[message_id] = widget
|
||||
self._messages_order.append(message_id)
|
||||
# Trigger recalculation timer
|
||||
self._recalculate_timer.start()
|
||||
|
||||
def _on_message_close_request(self, label_id):
|
||||
return message_id
|
||||
|
||||
def _on_message_close_request(self, message_id):
|
||||
"""Message widget requested removement."""
|
||||
|
||||
widget = self._messages.get(label_id)
|
||||
widget = self._messages.get(message_id)
|
||||
if widget is not None:
|
||||
# Add message to closing messages and start recalculation
|
||||
self._closing_messages.add(label_id)
|
||||
self._closing_messages.add(message_id)
|
||||
self._recalculate_timer.start()
|
||||
|
||||
def _recalculate_positions(self):
|
||||
|
|
|
|||