added typehints for controller

This commit is contained in:
Jakub Trllo 2024-06-24 14:02:49 +02:00
parent bafb3e7ced
commit 3de2d65c5b
20 changed files with 484 additions and 308 deletions

View file

@ -5,6 +5,8 @@ from .projects import (
ProjectItem,
ProjectsModel,
PROJECTS_MODEL_SENDER,
FolderTypeItem,
TaskTypeItem,
)
from .hierarchy import (
FolderItem,
@ -24,6 +26,8 @@ __all__ = (
"ProjectItem",
"ProjectsModel",
"PROJECTS_MODEL_SENDER",
"FolderTypeItem",
"TaskTypeItem",
"FolderItem",
"TaskItem",

View file

@ -3,9 +3,16 @@ from typing import Optional, Dict, List, Tuple, Any, Callable, Union, Iterable
from ayon_core.lib import AbstractAttrDef
from ayon_core.host import HostBase
from ayon_core.tools.common_models import FolderItem, TaskItem
from ayon_core.pipeline.create import CreateContext, CreatedInstance
from ayon_core.pipeline.create.context import ConvertorItem
from ayon_core.tools.common_models import (
FolderItem,
TaskItem,
FolderTypeItem,
TaskTypeItem,
)
from .models import CreatorItem
class CardMessageTypes:
@ -14,22 +21,19 @@ class CardMessageTypes:
error = "error"
class AbstractPublisherController(ABC):
"""Publisher tool controller.
Define what must be implemented to be able use Publisher functionality.
Goal is to have "data driven" controller that can be used to control UI
running in different process. That lead to some disadvantages like UI can't
access objects directly but by using wrappers that can be serialized.
"""
class AbstractPublisherCommon(ABC):
@abstractmethod
def is_headless(self) -> bool:
pass
def register_event_callback(self, topic, callback):
"""Register event callback.
Listen for events with given topic.
Args:
topic (str): Name of topic.
callback (Callable): Callback that will be called when event
is triggered.
"""
@abstractmethod
def get_host(self) -> HostBase:
pass
@abstractmethod
@ -38,11 +42,32 @@ class AbstractPublisherController(ABC):
data: Optional[Dict[str, Any]] = None,
source: Optional[str] = None
):
"""Use implemented event system to trigger event."""
"""Emit event.
Args:
topic (str): Event topic used for callbacks filtering.
data (Optional[dict[str, Any]]): Event data.
source (Optional[str]): Event source.
"""
pass
@abstractmethod
def register_event_callback(self, topic: str, callback: Callable):
def emit_card_message(
self,
message: str,
message_type: Optional[str] = CardMessageTypes.standard
):
"""Emit a card message which can have a lifetime.
This is for UI purposes. Method can be extended to more arguments
in future e.g. different message timeout or type (color).
Args:
message (str): Message that will be showed.
message_type (Optional[str]): Message type.
"""
pass
@abstractmethod
@ -88,34 +113,36 @@ class AbstractPublisherController(ABC):
pass
@abstractmethod
def is_host_valid(self) -> bool:
"""Host is valid for creation part.
def reset(self):
"""Reset whole controller.
Host must have implemented certain functionality to be able create
in Publisher tool.
Returns:
bool: Host can handle creation of instances.
This should reset create context, publish context and all variables
that are related to it.
"""
pass
class AbstractPublisherBackend(AbstractPublisherCommon):
@abstractmethod
def get_folder_entity(
self, project_name: str, folder_id: str
) -> Union[Dict[str, Any], None]:
def is_headless(self) -> bool:
"""Controller is in headless mode.
Notes:
Not sure if this method is relevant in UI tool?
Returns:
bool: Headless mode.
"""
pass
@abstractmethod
def get_task_entity(
self, project_name: str, task_id: str
) -> Union[Dict[str, Any], None]:
def get_host(self) -> HostBase:
pass
@abstractmethod
def get_folder_item_by_path(
self, project_name: str, folder_path: str
) -> Union[FolderItem, None]:
def get_create_context(self) -> CreateContext:
pass
@abstractmethod
@ -129,31 +156,42 @@ class AbstractPublisherController(ABC):
pass
@abstractmethod
def get_create_context(self) -> CreateContext:
def get_folder_entity(
self, project_name: str, folder_id: str
) -> Union[Dict[str, Any], None]:
pass
@abstractmethod
def get_instances(self) -> List[CreatedInstance]:
"""Collected/created instances.
def get_folder_item_by_path(
self, project_name: str, folder_path: str
) -> Union[FolderItem, None]:
pass
@abstractmethod
def get_task_entity(
self, project_name: str, task_id: str
) -> Union[Dict[str, Any], None]:
pass
class AbstractPublisherFrontend(AbstractPublisherCommon):
@abstractmethod
def register_event_callback(self, topic: str, callback: Callable):
pass
@abstractmethod
def is_host_valid(self) -> bool:
"""Host is valid for creation part.
Host must have implemented certain functionality to be able create
in Publisher tool.
Returns:
List[CreatedInstance]: List of created instances.
bool: Host can handle creation of instances.
"""
pass
@abstractmethod
def get_instance_by_id(
self, instance_id: str
) -> Union[CreatedInstance, None]:
pass
@abstractmethod
def get_instances_by_id(
self, instance_ids: Optional[Iterable[str]] = None
) -> Dict[str, Union[CreatedInstance, None]]:
pass
@abstractmethod
def get_context_title(self) -> Union[str, None]:
"""Get context title for artist shown at the top of main window.
@ -166,19 +204,108 @@ class AbstractPublisherController(ABC):
pass
@abstractmethod
def get_existing_product_names(self, folder_path: str) -> List[str]:
def get_task_items_by_folder_paths(
self, folder_paths: Iterable[str]
) -> Dict[str, List[TaskItem]]:
pass
@abstractmethod
def reset(self):
"""Reset whole controller.
def get_folder_items(
self, project_name: str, sender: Optional[str] = None
) -> List[FolderItem]:
pass
This should reset create context, publish context and all variables
that are related to it.
@abstractmethod
def get_task_items(
self, project_name: str, folder_id: str, sender: Optional[str] = None
) -> List[TaskItem]:
pass
@abstractmethod
def get_folder_type_items(
self, project_name: str, sender: Optional[str] = None
) -> List[FolderTypeItem]:
pass
@abstractmethod
def get_task_type_items(
self, project_name: str, sender: Optional[str] = None
) -> List[TaskTypeItem]:
pass
@abstractmethod
def are_folder_paths_valid(self, folder_paths: Iterable[str]) -> bool:
"""Folder paths do exist in project.
Args:
folder_paths (Iterable[str]): List of folder paths.
Returns:
bool: All folder paths exist in project.
"""
pass
# --- Create ---
@abstractmethod
def get_creator_items(self) -> Dict[str, CreatorItem]:
"""Creator items by identifier.
Returns:
Dict[str, CreatorItem]: Creator items that will be shown to user.
"""
pass
@abstractmethod
def get_creator_icon(
self, identifier: str
) -> Union[str, Dict[str, Any], None]:
"""Receive creator's icon by identifier.
Todos:
Icon should be part of 'CreatorItem'.
Args:
identifier (str): Creator's identifier.
Returns:
Union[str, None]: Creator's icon string.
"""
pass
@abstractmethod
def get_convertor_items(self) -> Dict[str, ConvertorItem]:
"""Convertor items by identifier.
Returns:
Dict[str, ConvertorItem]: Convertor items that can be triggered
by user.
"""
pass
@abstractmethod
def get_instances(self) -> List[CreatedInstance]:
"""Collected/created instances.
Returns:
List[CreatedInstance]: List of created instances.
"""
pass
@abstractmethod
def get_instances_by_id(
self, instance_ids: Optional[Iterable[str]] = None
) -> Dict[str, Union[CreatedInstance, None]]:
pass
@abstractmethod
def get_existing_product_names(self, folder_path: str) -> List[str]:
pass
@abstractmethod
def get_creator_attribute_definitions(
self, instances: List[CreatedInstance]
@ -197,21 +324,6 @@ class AbstractPublisherController(ABC):
]]:
pass
@abstractmethod
def get_creator_icon(
self, identifier: str
) -> Union[str, Dict[str, Any], None]:
"""Receive creator's icon by identifier.
Args:
identifier (str): Creator's identifier.
Returns:
Union[str, None]: Creator's icon string.
"""
pass
@abstractmethod
def get_product_name(
self,
@ -257,6 +369,17 @@ class AbstractPublisherController(ABC):
pass
@abstractmethod
def trigger_convertor_items(self, convertor_identifiers: List[str]):
pass
@abstractmethod
def remove_instances(self, instance_ids: Iterable[str]):
"""Remove list of instances from create context."""
# TODO expect instance ids
pass
@abstractmethod
def save_changes(self) -> bool:
"""Save changes in create context.
@ -269,10 +392,37 @@ class AbstractPublisherController(ABC):
pass
# --- Publish ---
@abstractmethod
def remove_instances(self, instance_ids: List[str]):
"""Remove list of instances from create context."""
# TODO expect instance ids
def publish(self):
"""Trigger publishing without any order limitations."""
pass
@abstractmethod
def validate(self):
"""Trigger publishing which will stop after validation order."""
pass
@abstractmethod
def stop_publish(self):
"""Stop publishing can be also used to pause publishing.
Pause of publishing is possible only if all plugins successfully
finished.
"""
pass
@abstractmethod
def run_action(self, plugin_id: str, action_id: str):
"""Trigger pyblish action on a plugin.
Args:
plugin_id (str): Id of publish plugin.
action_id (str): Id of publish action.
"""
pass
@ -312,8 +462,18 @@ class AbstractPublisherController(ABC):
Returns:
bool: If publishing passed last possible validation order.
"""
"""
pass
@abstractmethod
def publish_can_continue(self):
"""Publish has still plugins to process and did not crash yet.
Returns:
bool: Publishing can continue in processing.
"""
pass
@abstractmethod
@ -336,16 +496,6 @@ class AbstractPublisherController(ABC):
pass
@abstractmethod
def get_publish_max_progress(self) -> int:
"""Get maximum possible progress number.
Returns:
int: Number that can be used as 100% of publish progress bar.
"""
pass
@abstractmethod
def get_publish_progress(self) -> int:
"""Current progress number.
@ -356,6 +506,16 @@ class AbstractPublisherController(ABC):
pass
@abstractmethod
def get_publish_max_progress(self) -> int:
"""Get maximum possible progress number.
Returns:
int: Number that can be used as 100% of publish progress bar.
"""
pass
@abstractmethod
def get_publish_error_msg(self) -> Union[str, None]:
"""Current error message which cause fail of publishing.
@ -375,59 +535,6 @@ class AbstractPublisherController(ABC):
def get_validation_errors(self):
pass
@abstractmethod
def publish(self):
"""Trigger publishing without any order limitations."""
pass
@abstractmethod
def validate(self):
"""Trigger publishing which will stop after validation order."""
pass
@abstractmethod
def stop_publish(self):
"""Stop publishing can be also used to pause publishing.
Pause of publishing is possible only if all plugins successfully
finished.
"""
pass
@abstractmethod
def run_action(self, plugin_id: str, action_id: str):
"""Trigger pyblish action on a plugin.
Args:
plugin_id (str): Id of publish plugin.
action_id (str): Id of publish action.
"""
pass
@abstractmethod
def get_convertor_items(self) -> Dict[str, ConvertorItem]:
pass
@abstractmethod
def trigger_convertor_items(self, convertor_identifiers: List[str]):
pass
@abstractmethod
def get_thumbnail_paths_for_instances(
self, instance_ids: List[str]
) -> Dict[str, Union[str, None]]:
pass
@abstractmethod
def set_thumbnail_paths_for_instances(
self, thumbnail_path_mapping: Dict[str, str]
):
pass
@abstractmethod
def set_comment(self, comment: str):
"""Set comment on pyblish context.
@ -441,21 +548,15 @@ class AbstractPublisherController(ABC):
pass
@abstractmethod
def emit_card_message(
self,
message: str,
message_type: Optional[str] = CardMessageTypes.standard
def get_thumbnail_paths_for_instances(
self, instance_ids: List[str]
) -> Dict[str, Union[str, None]]:
pass
@abstractmethod
def set_thumbnail_paths_for_instances(
self, thumbnail_path_mapping: Dict[str, Optional[str]]
):
"""Emit a card message which can have a lifetime.
This is for UI purposes. Method can be extended to more arguments
in future e.g. different message timeout or type (color).
Args:
message (str): Message that will be showed.
message_type (Optional[str]): Message type.
"""
pass
@abstractmethod

View file

@ -37,6 +37,9 @@ __all__ = (
"CONTEXT_ID",
"CONTEXT_LABEL",
"CONTEXT_GROUP",
"CONVERTOR_ITEM_GROUP",
"VARIANT_TOOLTIP",
"INPUTS_LAYOUT_HSPACING",

View file

@ -17,43 +17,17 @@ from .models import (
PublishModel,
CreateModel,
)
from .abstract import AbstractPublisherController, CardMessageTypes
from .abstract import (
AbstractPublisherBackend,
AbstractPublisherFrontend,
CardMessageTypes
)
class BasePublisherController(AbstractPublisherController):
"""Implement common logic for controllers.
Implement event system, logger and common attributes. Attributes are
triggering value changes so anyone can listen to their topics.
Prepare implementation for creator items. Controller must implement just
their filling by '_collect_creator_items'.
All prepared implementation is based on calling super '__init__'.
"""
def get_thumbnail_temp_dir_path(self):
"""Return path to directory where thumbnails can be temporary stored.
Returns:
str: Path to a directory.
"""
return os.path.join(
tempfile.gettempdir(),
"publisher_thumbnails",
get_process_id()
)
def clear_thumbnail_temp_dir_path(self):
"""Remove content of thumbnail temp directory."""
dirpath = self.get_thumbnail_temp_dir_path()
if os.path.exists(dirpath):
shutil.rmtree(dirpath)
class PublisherController(BasePublisherController):
class PublisherController(
AbstractPublisherBackend,
AbstractPublisherFrontend,
):
"""Middleware between UI, CreateContext and publish Context.
Handle both creation and publishing parts.
@ -137,6 +111,17 @@ class PublisherController(BasePublisherController):
data = {}
self._event_system.emit(topic, data, source)
def emit_card_message(
self, message, message_type=CardMessageTypes.standard
):
self._emit_event(
"show.card.message",
{
"message": message,
"message_type": message_type
}
)
def register_event_callback(self, topic, callback):
self._event_system.add_callback(topic, callback)
@ -183,6 +168,7 @@ class PublisherController(BasePublisherController):
Args:
identifier (str): Creator's identifier for which should
be icon returned.
"""
return self._create_model.get_creator_icon(identifier)
@ -201,9 +187,6 @@ class PublisherController(BasePublisherController):
"""Current instances in create context."""
return self._create_model.get_instances()
def get_instance_by_id(self, instance_id):
return self._create_model.get_instance_by_id(instance_id)
def get_instances_by_id(self, instance_ids=None):
return self._create_model.get_instances_by_id(instance_ids)
@ -356,17 +339,26 @@ class PublisherController(BasePublisherController):
thumbnail_path_mapping
)
def emit_card_message(
self, message, message_type=CardMessageTypes.standard
):
self._emit_event(
"show.card.message",
{
"message": message,
"message_type": message_type
}
def get_thumbnail_temp_dir_path(self):
"""Return path to directory where thumbnails can be temporary stored.
Returns:
str: Path to a directory.
"""
return os.path.join(
tempfile.gettempdir(),
"publisher_thumbnails",
get_process_id()
)
def clear_thumbnail_temp_dir_path(self):
"""Remove content of thumbnail temp directory."""
dirpath = self.get_thumbnail_temp_dir_path()
if os.path.exists(dirpath):
shutil.rmtree(dirpath)
def get_creator_attribute_definitions(self, instances):
"""Collect creator attribute definitions for multuple instances.

View file

@ -1,9 +1,10 @@
from .create import CreateModel
from .create import CreateModel, CreatorItem
from .publish import PublishModel
__all__ = (
"CreateModel",
"CreatorItem",
"PublishModel",
)

View file

@ -23,7 +23,7 @@ from ayon_core.pipeline.create.context import (
ConvertorItem,
)
from ayon_core.tools.publisher.abstract import (
AbstractPublisherController,
AbstractPublisherBackend,
CardMessageTypes,
)
CREATE_EVENT_SOURCE = "publisher.create.model"
@ -191,9 +191,9 @@ class CreatorItem:
class CreateModel:
def __init__(self, controller: AbstractPublisherController):
def __init__(self, controller: AbstractPublisherBackend):
self._log = None
self._controller = controller
self._controller: AbstractPublisherBackend = controller
self._create_context = CreateContext(
controller.get_host(),

View file

@ -16,7 +16,7 @@ from ayon_core.pipeline import (
)
from ayon_core.pipeline.plugin_discover import DiscoverResult
from ayon_core.pipeline.publish import get_publish_instance_label
from ayon_core.tools.publisher.abstract import AbstractPublisherController
from ayon_core.tools.publisher.abstract import AbstractPublisherBackend
PUBLISH_EVENT_SOURCE = "publisher.publish.model"
# Define constant for plugin orders offset
@ -788,7 +788,7 @@ def collect_families_from_instances(
class PublishModel:
def __init__(self, controller: AbstractPublisherController):
def __init__(self, controller: AbstractPublisherBackend):
self._controller = controller
# Publishing should stop at validation stage

View file

@ -29,18 +29,21 @@ from ayon_core.tools.utils import NiceCheckbox
from ayon_core.tools.utils import BaseClickableFrame
from ayon_core.tools.utils.lib import html_escape
from ayon_core.tools.publisher.abstract import AbstractPublisherFrontend
from ayon_core.tools.publisher.constants import (
CONTEXT_ID,
CONTEXT_LABEL,
CONTEXT_GROUP,
CONVERTOR_ITEM_GROUP,
)
from .widgets import (
AbstractInstanceView,
ContextWarningLabel,
IconValuePixmapLabel,
PublishPixmapLabel
)
from ..constants import (
CONTEXT_ID,
CONTEXT_LABEL,
CONTEXT_GROUP,
CONVERTOR_ITEM_GROUP,
)
class SelectionTypes:
@ -560,7 +563,7 @@ class InstanceCardView(AbstractInstanceView):
def __init__(self, controller, parent):
super(InstanceCardView, self).__init__(parent)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
scroll_area = QtWidgets.QScrollArea(self)
scroll_area.setWidgetResizable(True)

View file

@ -5,6 +5,7 @@ from ayon_core.tools.utils import PlaceholderLineEdit, GoToCurrentButton
from ayon_core.tools.common_models import HierarchyExpectedSelection
from ayon_core.tools.utils import FoldersWidget, TasksWidget
from ayon_core.tools.publisher.abstract import AbstractPublisherFrontend
class CreateSelectionModel(object):
@ -18,8 +19,8 @@ class CreateSelectionModel(object):
event_source = "publisher.create.selection.model"
def __init__(self, controller):
self._controller = controller
def __init__(self, controller: "CreateHierarchyController"):
self._controller: CreateHierarchyController = controller
self._project_name = None
self._folder_id = None
@ -94,9 +95,9 @@ class CreateHierarchyController:
controller (PublisherController): Publisher controller.
"""
def __init__(self, controller):
def __init__(self, controller: AbstractPublisherFrontend):
self._event_system = QueuedEventSystem()
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._selection_model = CreateSelectionModel(self)
self._expected_selection = HierarchyExpectedSelection(
self, handle_project=False
@ -168,10 +169,10 @@ class CreateContextWidget(QtWidgets.QWidget):
folder_changed = QtCore.Signal()
task_changed = QtCore.Signal()
def __init__(self, controller, parent):
super(CreateContextWidget, self).__init__(parent)
def __init__(self, controller: AbstractPublisherFrontend, parent):
super().__init__(parent)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._enabled = True
self._last_project_name = None
self._last_folder_id = None

View file

@ -9,14 +9,8 @@ from ayon_core.pipeline.create import (
TaskNotSetError,
)
from .thumbnail_widget import ThumbnailWidget
from .widgets import (
IconValuePixmapLabel,
CreateBtn,
)
from .create_context_widgets import CreateContextWidget
from .precreate_widget import PreCreateWidget
from ..constants import (
from ayon_core.tools.publisher.abstract import AbstractPublisherFrontend
from ayon_core.tools.publisher.constants import (
VARIANT_TOOLTIP,
PRODUCT_TYPE_ROLE,
CREATOR_IDENTIFIER_ROLE,
@ -26,6 +20,14 @@ from ..constants import (
INPUTS_LAYOUT_VSPACING,
)
from .thumbnail_widget import ThumbnailWidget
from .widgets import (
IconValuePixmapLabel,
CreateBtn,
)
from .create_context_widgets import CreateContextWidget
from .precreate_widget import PreCreateWidget
SEPARATORS = ("---separator---", "---")
@ -106,7 +108,7 @@ class CreateWidget(QtWidgets.QWidget):
def __init__(self, controller, parent=None):
super(CreateWidget, self).__init__(parent)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._folder_path = None
self._product_names = None

View file

@ -2,12 +2,13 @@ from qtpy import QtWidgets
from ayon_core.lib.events import QueuedEventSystem
from ayon_core.tools.utils import PlaceholderLineEdit, FoldersWidget
from ayon_core.tools.publisher.abstract import AbstractPublisherFrontend
class FoldersDialogController:
def __init__(self, controller):
def __init__(self, controller: AbstractPublisherFrontend):
self._event_system = QueuedEventSystem()
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
@property
def event_system(self):

View file

@ -5,6 +5,8 @@ except Exception:
from qtpy import QtWidgets, QtCore
from ayon_core.tools.publisher.abstract import AbstractPublisherFrontend
class HelpButton(QtWidgets.QPushButton):
"""Button used to trigger help dialog."""
@ -54,7 +56,9 @@ class HelpDialog(QtWidgets.QDialog):
default_width = 530
default_height = 340
def __init__(self, controller, parent):
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super(HelpDialog, self).__init__(parent)
self.setWindowTitle("Help dialog")
@ -68,7 +72,7 @@ class HelpDialog(QtWidgets.QDialog):
"show.detailed.help", self._on_help_request
)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._help_content = help_content

View file

@ -29,8 +29,9 @@ from qtpy import QtWidgets, QtCore, QtGui
from ayon_core.style import get_objected_colors
from ayon_core.tools.utils import NiceCheckbox
from ayon_core.tools.utils.lib import html_escape, checkstate_int_to_enum
from .widgets import AbstractInstanceView
from ..constants import (
from ayon_core.tools.publisher.abstract import AbstractPublisherFrontend
from ayon_core.tools.publisher.constants import (
INSTANCE_ID_ROLE,
SORT_VALUE_ROLE,
IS_GROUP_ROLE,
@ -41,6 +42,8 @@ from ..constants import (
CONVERTOR_ITEM_GROUP,
)
from .widgets import AbstractInstanceView
class ListItemDelegate(QtWidgets.QStyledItemDelegate):
"""Generic delegate for instance group.
@ -442,10 +445,12 @@ class InstanceListView(AbstractInstanceView):
double_clicked = QtCore.Signal()
def __init__(self, controller, parent):
super(InstanceListView, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
instance_view = InstanceTreeView(self)
instance_delegate = ListItemDelegate(instance_view)

View file

@ -1,7 +1,8 @@
from qtpy import QtWidgets, QtCore
from .border_label_widget import BorderedLabelWidget
from ayon_core.tools.publisher.abstract import AbstractPublisherFrontend
from .border_label_widget import BorderedLabelWidget
from .card_view_widgets import InstanceCardView
from .list_view_widgets import InstanceListView
from .widgets import (
@ -23,11 +24,13 @@ class OverviewWidget(QtWidgets.QFrame):
anim_end_value = 200
anim_duration = 200
def __init__(self, controller, parent):
super(OverviewWidget, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
self._refreshing_instances = False
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
product_content_widget = QtWidgets.QWidget(self)

View file

@ -1,5 +1,7 @@
from qtpy import QtWidgets, QtCore
from ayon_core.tools.publisher.abstract import AbstractPublisherFrontend
from .widgets import (
StopBtn,
ResetBtn,
@ -31,8 +33,13 @@ class PublishFrame(QtWidgets.QWidget):
details_page_requested = QtCore.Signal()
def __init__(self, controller, borders, parent):
super(PublishFrame, self).__init__(parent)
def __init__(
self,
controller: AbstractPublisherFrontend,
borders: int,
parent: QtWidgets.QWidget
):
super().__init__(parent)
# Bottom part of widget where process and callback buttons are showed
# - QFrame used to be able set background using stylesheets easily
@ -179,7 +186,7 @@ class PublishFrame(QtWidgets.QWidget):
self._shrunk_anim = shrunk_anim
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._content_frame = content_frame
self._content_layout = content_layout

View file

@ -19,16 +19,18 @@ from ayon_core.tools.utils import (
paint_image_with_color,
SeparatorWidget,
)
from ayon_core.tools.publisher.abstract import AbstractPublisherFrontend
from ayon_core.tools.publisher.constants import (
INSTANCE_ID_ROLE,
CONTEXT_ID,
CONTEXT_LABEL,
)
from .widgets import IconValuePixmapLabel
from .icons import (
get_pixmap,
get_image,
)
from ..constants import (
INSTANCE_ID_ROLE,
CONTEXT_ID,
CONTEXT_LABEL,
)
LOG_DEBUG_VISIBLE = 1 << 0
LOG_INFO_VISIBLE = 1 << 1
@ -159,8 +161,10 @@ class ValidateActionsWidget(QtWidgets.QFrame):
Change actions based on selected validation error.
"""
def __init__(self, controller, parent):
super(ValidateActionsWidget, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
@ -172,7 +176,7 @@ class ValidateActionsWidget(QtWidgets.QFrame):
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(content_widget)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._content_widget = content_widget
self._content_layout = content_layout
@ -874,8 +878,10 @@ class PublishInstancesViewWidget(QtWidgets.QWidget):
_min_width_measure_string = 24 * "O"
selection_changed = QtCore.Signal()
def __init__(self, controller, parent):
super(PublishInstancesViewWidget, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
scroll_area = VerticalScrollArea(self)
scroll_area.setWidgetResizable(True)
@ -898,7 +904,7 @@ class PublishInstancesViewWidget(QtWidgets.QWidget):
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(scroll_area, 1)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._scroll_area = scroll_area
self._instance_view = instance_view
self._instance_layout = instance_layout
@ -1357,7 +1363,7 @@ class InstancesLogsView(QtWidgets.QFrame):
self._is_showed = False
def closeEvent(self, event):
super(InstancesLogsView, self).closeEvent(event)
super().closeEvent(event)
self._is_showed = False
def _update_instances(self):
@ -1455,8 +1461,10 @@ class CrashWidget(QtWidgets.QWidget):
actions.
"""
def __init__(self, controller, parent):
super(CrashWidget, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
main_label = QtWidgets.QLabel("This is not your fault", self)
main_label.setAlignment(QtCore.Qt.AlignCenter)
@ -1498,7 +1506,7 @@ class CrashWidget(QtWidgets.QWidget):
copy_clipboard_btn.clicked.connect(self._on_copy_to_clipboard)
save_to_disk_btn.clicked.connect(self._on_save_to_disk_click)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
def _on_copy_to_clipboard(self):
self._controller.emit_event(
@ -1623,8 +1631,10 @@ class ReportsWidget(QtWidgets.QWidget):
"""
def __init__(self, controller, parent):
super(ReportsWidget, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
# Instances view
views_widget = QtWidgets.QWidget(self)
@ -1708,7 +1718,7 @@ class ReportsWidget(QtWidgets.QWidget):
self._detail_input_scroll = detail_input_scroll
self._crash_widget = crash_widget
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._validation_errors_by_id = {}
@ -1818,8 +1828,10 @@ class ReportPageWidget(QtWidgets.QFrame):
and validation error detail with possible actions (repair).
"""
def __init__(self, controller, parent):
super(ReportPageWidget, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
header_label = QtWidgets.QLabel(self)
header_label.setAlignment(QtCore.Qt.AlignCenter)
@ -1845,7 +1857,7 @@ class ReportPageWidget(QtWidgets.QFrame):
self._header_label = header_label
self._publish_instances_widget = publish_instances_widget
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
def _update_label(self):
if not self._controller.publish_has_started():

View file

@ -1,7 +1,10 @@
from typing import Optional
from qtpy import QtCore, QtGui
from ayon_core.style import get_default_entity_icon_color
from ayon_core.tools.utils import get_qt_icon
from ayon_core.tools.publisher.abstract import AbstractPublisherFrontend
TASK_NAME_ROLE = QtCore.Qt.UserRole + 1
TASK_TYPE_ROLE = QtCore.Qt.UserRole + 2
@ -19,14 +22,19 @@ class TasksModel(QtGui.QStandardItemModel):
tasks with same names then model is empty too.
Args:
controller (PublisherController): Controller which handles creation and
controller (AbstractPublisherFrontend): Controller which handles creation and
publishing.
"""
def __init__(self, controller, allow_empty_task=False):
super(TasksModel, self).__init__()
def __init__(
self,
controller: AbstractPublisherFrontend,
allow_empty_task: Optional[bool] = False
):
super().__init__()
self._allow_empty_task = allow_empty_task
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._items_by_name = {}
self._folder_paths = []
self._task_names_by_folder_path = {}

View file

@ -19,7 +19,10 @@ from ayon_core.tools.utils import (
paint_image_with_color,
PixmapButton,
)
from ayon_core.tools.publisher.control import CardMessageTypes
from ayon_core.tools.publisher.abstract import (
CardMessageTypes,
AbstractPublisherFrontend,
)
from .icons import get_image
from .screenshot_widget import capture_to_file
@ -299,7 +302,9 @@ class ThumbnailWidget(QtWidgets.QWidget):
thumbnail_created = QtCore.Signal(str)
thumbnail_cleared = QtCore.Signal()
def __init__(self, controller, parent):
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
# Missing implementation for thumbnail
# - widget kept to make a visial offset of global attr widget offset
super(ThumbnailWidget, self).__init__(parent)
@ -355,7 +360,7 @@ class ThumbnailWidget(QtWidgets.QWidget):
paste_btn.clicked.connect(self._on_paste_from_clipboard)
browse_btn.clicked.connect(self._on_browse_clicked)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._output_dir = controller.get_thumbnail_temp_dir_path()
self._review_extensions = set(IMAGE_EXTENSIONS) | set(VIDEO_EXTENSIONS)

View file

@ -10,6 +10,11 @@ from qtpy import QtWidgets, QtCore, QtGui
import qtawesome
from ayon_core.lib.attribute_definitions import UnknownDef
from ayon_core.style import get_objected_colors
from ayon_core.pipeline.create import (
PRODUCT_NAME_ALLOWED_SYMBOLS,
TaskNotSetError,
)
from ayon_core.tools.attribute_defs import create_widget_for_attr_def
from ayon_core.tools import resources
from ayon_core.tools.flickcharm import FlickCharm
@ -20,11 +25,14 @@ from ayon_core.tools.utils import (
BaseClickableFrame,
set_style_property,
)
from ayon_core.style import get_objected_colors
from ayon_core.pipeline.create import (
PRODUCT_NAME_ALLOWED_SYMBOLS,
TaskNotSetError,
from ayon_core.tools.publisher.abstract import AbstractPublisherFrontend
from ayon_core.tools.publisher.constants import (
VARIANT_TOOLTIP,
ResetKeySequence,
INPUTS_LAYOUT_HSPACING,
INPUTS_LAYOUT_VSPACING,
)
from .thumbnail_widget import ThumbnailWidget
from .folders_dialog import FoldersDialog
from .tasks_model import TasksModel
@ -33,13 +41,6 @@ from .icons import (
get_icon_path
)
from ..constants import (
VARIANT_TOOLTIP,
ResetKeySequence,
INPUTS_LAYOUT_HSPACING,
INPUTS_LAYOUT_VSPACING,
)
FA_PREFIXES = ["", "fa.", "fa5.", "fa5b.", "fa5s.", "ei.", "mdi."]
@ -363,7 +364,9 @@ class AbstractInstanceView(QtWidgets.QWidget):
"{} Method 'get_selected_items' is not implemented."
).format(self.__class__.__name__))
def set_selected_items(self, instance_ids, context_selected):
def set_selected_items(
self, instance_ids, context_selected, convertor_identifiers
):
"""Change selection for instances and context.
Used to applying selection from one view to other.
@ -371,8 +374,9 @@ class AbstractInstanceView(QtWidgets.QWidget):
Args:
instance_ids (List[str]): Selected instance ids.
context_selected (bool): Context is selected.
"""
convertor_identifiers (List[str]): Selected convertor identifiers.
"""
raise NotImplementedError((
"{} Method 'set_selected_items' is not implemented."
).format(self.__class__.__name__))
@ -429,8 +433,10 @@ class FoldersFields(BaseClickableFrame):
"""
value_changed = QtCore.Signal()
def __init__(self, controller, parent):
super(FoldersFields, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
self.setObjectName("FolderPathInputWidget")
# Don't use 'self' for parent!
@ -465,7 +471,7 @@ class FoldersFields(BaseClickableFrame):
icon_btn.clicked.connect(self._mouse_release_callback)
dialog.finished.connect(self._on_dialog_finish)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._dialog = dialog
self._name_input = name_input
self._icon_btn = icon_btn
@ -613,8 +619,10 @@ class TasksCombobox(QtWidgets.QComboBox):
"""
value_changed = QtCore.Signal()
def __init__(self, controller, parent):
super(TasksCombobox, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
self.setObjectName("TasksCombobox")
# Set empty delegate to propagate stylesheet to a combobox
@ -1095,10 +1103,12 @@ class GlobalAttrsWidget(QtWidgets.QWidget):
multiselection_text = "< Multiselection >"
unknown_value = "N/A"
def __init__(self, controller, parent):
super(GlobalAttrsWidget, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._current_instances = []
variant_input = VariantInputWidget(self)
@ -1338,8 +1348,10 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
widgets are merged into one (different label does not count).
"""
def __init__(self, controller, parent):
super(CreatorAttrsWidget, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
scroll_area = QtWidgets.QScrollArea(self)
scroll_area.setWidgetResizable(True)
@ -1351,7 +1363,7 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
self._main_layout = main_layout
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._scroll_area = scroll_area
self._attr_def_id_to_instances = {}
@ -1476,8 +1488,10 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget):
does not count).
"""
def __init__(self, controller, parent):
super(PublishPluginAttrsWidget, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
scroll_area = QtWidgets.QScrollArea(self)
scroll_area.setWidgetResizable(True)
@ -1489,7 +1503,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget):
self._main_layout = main_layout
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._scroll_area = scroll_area
self._attr_def_id_to_instances = {}
@ -1635,8 +1649,10 @@ class ProductAttributesWidget(QtWidgets.QWidget):
instance_context_changed = QtCore.Signal()
convert_requested = QtCore.Signal()
def __init__(self, controller, parent):
super(ProductAttributesWidget, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
# TOP PART
top_widget = QtWidgets.QWidget(self)
@ -1738,7 +1754,7 @@ class ProductAttributesWidget(QtWidgets.QWidget):
"instance.thumbnail.changed", self._on_thumbnail_changed
)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._convert_widget = convert_widget

View file

@ -3,6 +3,8 @@ import json
import time
import collections
import copy
from typing import Optional
from qtpy import QtWidgets, QtCore, QtGui
from ayon_core import (
@ -19,7 +21,7 @@ from ayon_core.tools.utils.lib import center_window
from .constants import ResetKeySequence
from .publish_report_viewer import PublishReportViewerWidget
from .control import CardMessageTypes
from .abstract import CardMessageTypes, AbstractPublisherFrontend
from .control_qt import QtPublisherController
from .widgets import (
OverviewWidget,
@ -48,8 +50,13 @@ class PublisherWindow(QtWidgets.QDialog):
footer_border = 8
publish_footer_spacer = 2
def __init__(self, parent=None, controller=None, reset_on_show=None):
super(PublisherWindow, self).__init__(parent)
def __init__(
self,
parent: Optional[QtWidgets.QWidget] = None,
controller: Optional[AbstractPublisherFrontend] = None,
reset_on_show: Optional[bool] = None
):
super().__init__(parent)
self.setObjectName("PublishWindow")
@ -362,7 +369,7 @@ class PublisherWindow(QtWidgets.QDialog):
self._overlay_object = overlay_object
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._first_show = True
self._first_reset = True
@ -386,7 +393,8 @@ class PublisherWindow(QtWidgets.QDialog):
self._window_is_visible = False
@property
def controller(self):
def controller(self) -> AbstractPublisherFrontend:
"""Kept for compatibility with traypublisher."""
return self._controller
def show_and_publish(self, comment=None):
@ -520,7 +528,7 @@ class PublisherWindow(QtWidgets.QDialog):
)
if reset_match_result == QtGui.QKeySequence.ExactMatch:
if not self.controller.publish_is_running:
if not self._controller.publish_is_running:
self.reset()
event.accept()
return
@ -643,7 +651,7 @@ class PublisherWindow(QtWidgets.QDialog):
if not force and not self._is_on_details_tab():
return
report_data = self.controller.get_publish_report()
report_data = self._controller.get_publish_report()
self._publish_details_widget.set_report_data(report_data)
def _on_help_click(self):
@ -921,7 +929,7 @@ class PublisherWindow(QtWidgets.QDialog):
def _on_instances_refresh(self):
self._validate_create_instances()
context_title = self.controller.get_context_title()
context_title = self._controller.get_context_title()
self.set_context_label(context_title)
self._update_publish_details_widget()