Merge branch 'develop' of https://github.com/ynput/ayon-core into enhancement/create_context_typing

# Conflicts:
#	server_addon/jobqueue/client/ayon_jobqueue/addon.py
This commit is contained in:
Roy Nieterau 2024-06-27 18:13:25 +02:00
commit ad208251a3
1060 changed files with 3640 additions and 181796 deletions

View file

@ -87,7 +87,9 @@ class IntegrateHeroVersion(
]
# QUESTION/TODO this process should happen on server if crashed due to
# permissions error on files (files were used or user didn't have perms)
# *but all other plugins must be sucessfully completed
# *but all other plugins must be successfully completed
use_hardlinks = False
def process(self, instance):
if not self.is_active(instance.data):
@ -617,24 +619,32 @@ class IntegrateHeroVersion(
self.log.debug("Folder already exists: \"{}\"".format(dirname))
if self.use_hardlinks:
# First try hardlink and copy if paths are cross drive
self.log.debug("Hardlinking file \"{}\" to \"{}\"".format(
src_path, dst_path
))
try:
create_hard_link(src_path, dst_path)
# Return when successful
return
except OSError as exc:
# re-raise exception if different than
# EXDEV - cross drive path
# EINVAL - wrong format, must be NTFS
self.log.debug(
"Hardlink failed with errno:'{}'".format(exc.errno))
if exc.errno not in [errno.EXDEV, errno.EINVAL]:
raise
self.log.debug(
"Hardlinking failed, falling back to regular copy...")
self.log.debug("Copying file \"{}\" to \"{}\"".format(
src_path, dst_path
))
# First try hardlink and copy if paths are cross drive
try:
create_hard_link(src_path, dst_path)
# Return when successful
return
except OSError as exc:
# re-raise exception if different than
# EXDEV - cross drive path
# EINVAL - wrong format, must be NTFS
self.log.debug("Hardlink failed with errno:'{}'".format(exc.errno))
if exc.errno not in [errno.EXDEV, errno.EINVAL]:
raise
shutil.copy(src_path, dst_path)
def version_from_representations(self, project_name, repres):

View file

@ -7,6 +7,8 @@ from .projects import (
ProjectItem,
ProjectsModel,
PROJECTS_MODEL_SENDER,
FolderTypeItem,
TaskTypeItem,
)
from .hierarchy import (
FolderItem,
@ -28,6 +30,8 @@ __all__ = (
"ProjectItem",
"ProjectsModel",
"PROJECTS_MODEL_SENDER",
"FolderTypeItem",
"TaskTypeItem",
"FolderItem",
"TaskItem",

View file

@ -0,0 +1,587 @@
from abc import ABC, abstractmethod
from typing import (
Optional,
Dict,
List,
Tuple,
Any,
Callable,
Union,
Iterable,
TYPE_CHECKING,
)
from ayon_core.lib import AbstractAttrDef
from ayon_core.host import HostBase
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,
)
if TYPE_CHECKING:
from .models import CreatorItem
class CardMessageTypes:
standard = None
info = "info"
error = "error"
class AbstractPublisherCommon(ABC):
@abstractmethod
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.
"""
pass
@abstractmethod
def emit_event(
self, topic: str,
data: Optional[Dict[str, Any]] = None,
source: Optional[str] = None
):
"""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 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
def get_current_project_name(self) -> Union[str, None]:
"""Current context project name.
Returns:
str: Name of project.
"""
pass
@abstractmethod
def get_current_folder_path(self) -> Union[str, None]:
"""Current context folder path.
Returns:
Union[str, None]: Folder path.
"""
pass
@abstractmethod
def get_current_task_name(self) -> Union[str, None]:
"""Current context task name.
Returns:
Union[str, None]: Name of task.
"""
pass
@abstractmethod
def host_context_has_changed(self) -> bool:
"""Host context changed after last reset.
'CreateContext' has this option available using 'context_has_changed'.
Returns:
bool: Context has changed.
"""
pass
@abstractmethod
def reset(self):
"""Reset whole controller.
This should reset create context, publish context and all variables
that are related to it.
"""
pass
class AbstractPublisherBackend(AbstractPublisherCommon):
@abstractmethod
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_host(self) -> HostBase:
pass
@abstractmethod
def get_create_context(self) -> CreateContext:
pass
@abstractmethod
def get_task_item_by_name(
self,
project_name: str,
folder_id: str,
task_name: str,
sender: Optional[str] = None
) -> Union[TaskItem, None]:
pass
@abstractmethod
def get_folder_entity(
self, project_name: str, folder_id: str
) -> Union[Dict[str, Any], None]:
pass
@abstractmethod
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:
bool: Host can handle creation of instances.
"""
pass
@abstractmethod
def get_context_title(self) -> Union[str, None]:
"""Get context title for artist shown at the top of main window.
Returns:
Union[str, None]: Context title for window or None. In case of None
a warning is displayed (not nice for artists).
"""
pass
@abstractmethod
def get_task_items_by_folder_paths(
self, folder_paths: Iterable[str]
) -> Dict[str, List[TaskItem]]:
pass
@abstractmethod
def get_folder_items(
self, project_name: str, sender: Optional[str] = None
) -> List[FolderItem]:
pass
@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]
) -> List[Tuple[AbstractAttrDef, List[CreatedInstance], List[Any]]]:
pass
@abstractmethod
def get_publish_attribute_definitions(
self,
instances: List[CreatedInstance],
include_context: bool
) -> List[Tuple[
str,
List[AbstractAttrDef],
Dict[str, List[Tuple[CreatedInstance, Any]]]
]]:
pass
@abstractmethod
def get_product_name(
self,
creator_identifier: str,
variant: str,
task_name: Union[str, None],
folder_path: Union[str, None],
instance_id: Optional[str] = None
):
"""Get product name based on passed data.
Args:
creator_identifier (str): Identifier of creator which should be
responsible for product name creation.
variant (str): Variant value from user's input.
task_name (str): Name of task for which is instance created.
folder_path (str): Folder path for which is instance created.
instance_id (Union[str, None]): Existing instance id when product
name is updated.
"""
pass
@abstractmethod
def create(
self,
creator_identifier: str,
product_name: str,
instance_data: Dict[str, Any],
options: Dict[str, Any],
):
"""Trigger creation by creator identifier.
Should also trigger refresh of instanes.
Args:
creator_identifier (str): Identifier of Creator plugin.
product_name (str): Calculated product name.
instance_data (Dict[str, Any]): Base instance data with variant,
folder path and task name.
options (Dict[str, Any]): Data from pre-create attributes.
"""
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.
Save can crash because of unexpected errors.
Returns:
bool: Save was successful.
"""
pass
# --- Publish ---
@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 publish_has_started(self) -> bool:
"""Has publishing finished.
Returns:
bool: If publishing finished and all plugins were iterated.
"""
pass
@abstractmethod
def publish_has_finished(self) -> bool:
"""Has publishing finished.
Returns:
bool: If publishing finished and all plugins were iterated.
"""
pass
@abstractmethod
def publish_is_running(self) -> bool:
"""Publishing is running right now.
Returns:
bool: If publishing is in progress.
"""
pass
@abstractmethod
def publish_has_validated(self) -> bool:
"""Publish validation passed.
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
def publish_has_crashed(self) -> bool:
"""Publishing crashed for any reason.
Returns:
bool: Publishing crashed.
"""
pass
@abstractmethod
def publish_has_validation_errors(self) -> bool:
"""During validation happened at least one validation error.
Returns:
bool: Validation error was raised during validation.
"""
pass
@abstractmethod
def get_publish_progress(self) -> int:
"""Current progress number.
Returns:
int: Current progress value from 0 to 'publish_max_progress'.
"""
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.
Returns:
Union[str, None]: Message which will be showed to artist or
None.
"""
pass
@abstractmethod
def get_publish_report(self) -> Dict[str, Any]:
pass
@abstractmethod
def get_validation_errors(self):
pass
@abstractmethod
def set_comment(self, comment: str):
"""Set comment on pyblish context.
Set "comment" key on current pyblish.api.Context data.
Args:
comment (str): Artist's comment.
"""
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, Optional[str]]
):
pass
@abstractmethod
def get_thumbnail_temp_dir_path(self) -> str:
"""Return path to directory where thumbnails can be temporary stored.
Returns:
str: Path to a directory.
"""
pass
@abstractmethod
def clear_thumbnail_temp_dir_path(self):
"""Remove content of thumbnail temp directory."""
pass

View file

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

File diff suppressed because it is too large Load diff

View file

@ -1,16 +1,23 @@
import collections
from abc import abstractmethod, abstractproperty
from qtpy import QtCore
from ayon_core.lib.events import Event
from ayon_core.pipeline.create import CreatedInstance
from .control import PublisherController
from .control import (
MainThreadItem,
PublisherController,
BasePublisherController,
)
class MainThreadItem:
"""Callback with args and kwargs."""
def __init__(self, callback, *args, **kwargs):
self.callback = callback
self.args = args
self.kwargs = kwargs
def __call__(self):
self.process()
def process(self):
self.callback(*self.args, **self.kwargs)
class MainThreadProcess(QtCore.QObject):
@ -24,7 +31,7 @@ class MainThreadProcess(QtCore.QObject):
count_timeout = 2
def __init__(self):
super(MainThreadProcess, self).__init__()
super().__init__()
self._items_to_process = collections.deque()
timer = QtCore.QTimer()
@ -33,11 +40,6 @@ class MainThreadProcess(QtCore.QObject):
timer.timeout.connect(self._execute)
self._timer = timer
self._switch_counter = self.count_timeout
def process(self, func, *args, **kwargs):
item = MainThreadItem(func, *args, **kwargs)
self.add_item(item)
def add_item(self, item):
self._items_to_process.append(item)
@ -46,12 +48,6 @@ class MainThreadProcess(QtCore.QObject):
if not self._items_to_process:
return
if self._switch_counter > 0:
self._switch_counter -= 1
return
self._switch_counter = self.count_timeout
item = self._items_to_process.popleft()
item.process()
@ -73,18 +69,34 @@ class QtPublisherController(PublisherController):
def __init__(self, *args, **kwargs):
self._main_thread_processor = MainThreadProcess()
super(QtPublisherController, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.event_system.add_callback(
self.register_event_callback(
"publish.process.started", self._qt_on_publish_start
)
self.event_system.add_callback(
self.register_event_callback(
"publish.process.stopped", self._qt_on_publish_stop
)
def _reset_publish(self):
super(QtPublisherController, self)._reset_publish()
def reset(self):
self._main_thread_processor.clear()
super().reset()
def _start_publish(self, up_validation):
self._publish_model.set_publish_up_validation(up_validation)
self._publish_model.start_publish(wait=False)
self._process_main_thread_item(
MainThreadItem(self._next_publish_item_process)
)
def _next_publish_item_process(self):
if not self._publish_model.is_running():
return
func = self._publish_model.get_next_process_func()
self._process_main_thread_item(MainThreadItem(func))
self._process_main_thread_item(
MainThreadItem(self._next_publish_item_process)
)
def _process_main_thread_item(self, item):
self._main_thread_processor.add_item(item)
@ -94,347 +106,3 @@ class QtPublisherController(PublisherController):
def _qt_on_publish_stop(self):
self._main_thread_processor.stop()
class QtRemotePublishController(BasePublisherController):
"""Abstract Remote controller for Qt UI.
This controller should be used in process where UI is running and should
listen and ask for data on a client side.
All objects that are used during UI processing should be able to convert
on client side to json serializable data and then recreated here. Keep in
mind that all changes made here should be send back to client controller
before critical actions.
ATM Was not tested and will require some changes. All code written here is
based on theoretical idea how it could work.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._created_instances = {}
self._thumbnail_paths_by_instance_id = None
def _reset_attributes(self):
super()._reset_attributes()
self._thumbnail_paths_by_instance_id = None
@abstractmethod
def _get_serialized_instances(self):
"""Receive serialized instances from client process.
Returns:
List[Dict[str, Any]]: Serialized instances.
"""
pass
def _on_create_instance_change(self):
serialized_instances = self._get_serialized_instances()
created_instances = {}
for serialized_data in serialized_instances:
item = CreatedInstance.deserialize_on_remote(serialized_data)
created_instances[item.id] = item
self._created_instances = created_instances
self._emit_event("instances.refresh.finished")
def remote_events_handler(self, event_data):
event = Event.from_data(event_data)
# Topics that cause "replication" of controller changes
if event.topic == "publish.max_progress.changed":
self.publish_max_progress = event["value"]
return
if event.topic == "publish.progress.changed":
self.publish_progress = event["value"]
return
if event.topic == "publish.has_validated.changed":
self.publish_has_validated = event["value"]
return
if event.topic == "publish.is_running.changed":
self.publish_is_running = event["value"]
return
if event.topic == "publish.publish_error.changed":
self.publish_error_msg = event["value"]
return
if event.topic == "publish.has_crashed.changed":
self.publish_has_crashed = event["value"]
return
if event.topic == "publish.has_validation_errors.changed":
self.publish_has_validation_errors = event["value"]
return
if event.topic == "publish.finished.changed":
self.publish_has_finished = event["value"]
return
if event.topic == "publish.host_is_valid.changed":
self.host_is_valid = event["value"]
return
# Don't skip because UI want know about it too
if event.topic == "instance.thumbnail.changed":
for instance_id, path in event["mapping"].items():
self.thumbnail_paths_by_instance_id[instance_id] = path
# Topics that can be just passed by because are not affecting
# controller itself
# - "show.card.message"
# - "show.detailed.help"
# - "publish.reset.finished"
# - "instances.refresh.finished"
# - "plugins.refresh.finished"
# - "controller.reset.finished"
# - "publish.process.started"
# - "publish.process.stopped"
# - "publish.process.plugin.changed"
# - "publish.process.instance.changed"
self.event_system.emit_event(event)
@abstractproperty
def project_name(self):
"""Current context project name from client.
Returns:
str: Name of project.
"""
pass
@abstractproperty
def current_folder_path(self):
"""Current context folder path from host.
Returns:
Union[str, None]: Folder path.
"""
pass
@abstractproperty
def current_task_name(self):
"""Current context task name from client.
Returns:
Union[str, None]: Name of task.
"""
pass
@property
def instances(self):
"""Collected/created instances.
Returns:
List[CreatedInstance]: List of created instances.
"""
return self._created_instances
def get_context_title(self):
"""Get context title for artist shown at the top of main window.
Returns:
Union[str, None]: Context title for window or None. In case of None
a warning is displayed (not nice for artists).
"""
pass
def get_existing_product_names(self, folder_path):
pass
@property
def thumbnail_paths_by_instance_id(self):
if self._thumbnail_paths_by_instance_id is None:
self._thumbnail_paths_by_instance_id = (
self._collect_thumbnail_paths_by_instance_id()
)
return self._thumbnail_paths_by_instance_id
def get_thumbnail_path_for_instance(self, instance_id):
return self.thumbnail_paths_by_instance_id.get(instance_id)
def set_thumbnail_path_for_instance(self, instance_id, thumbnail_path):
self._set_thumbnail_path_on_context(self, instance_id, thumbnail_path)
@abstractmethod
def _collect_thumbnail_paths_by_instance_id(self):
"""Collect thumbnail paths by instance id in remote controller.
These should be collected from 'CreatedContext' there.
Returns:
Dict[str, str]: Mapping of thumbnail path by instance id.
"""
pass
@abstractmethod
def _set_thumbnail_path_on_context(self, instance_id, thumbnail_path):
"""Send change of thumbnail path in remote controller.
That should trigger event 'instance.thumbnail.changed' which is
captured and handled in default implementation in this class.
"""
pass
@abstractmethod
def get_product_name(
self,
creator_identifier,
variant,
task_name,
folder_path,
instance_id=None
):
"""Get product name based on passed data.
Args:
creator_identifier (str): Identifier of creator which should be
responsible for product name creation.
variant (str): Variant value from user's input.
task_name (str): Name of task for which is instance created.
folder_path (str): Folder path for which is instance created.
instance_id (Union[str, None]): Existing instance id when product
name is updated.
"""
pass
@abstractmethod
def create(
self, creator_identifier, product_name, instance_data, options
):
"""Trigger creation by creator identifier.
Should also trigger refresh of instanes.
Args:
creator_identifier (str): Identifier of Creator plugin.
product_name (str): Calculated product name.
instance_data (Dict[str, Any]): Base instance data with variant,
folder path and task name.
options (Dict[str, Any]): Data from pre-create attributes.
"""
pass
def _get_instance_changes_for_client(self):
"""Preimplemented method to receive instance changes for client."""
created_instance_changes = {}
for instance_id, instance in self._created_instances.items():
created_instance_changes[instance_id] = (
instance.remote_changes()
)
return created_instance_changes
@abstractmethod
def _send_instance_changes_to_client(self):
# TODO Implement to send 'instance_changes' value to client
# instance_changes = self._get_instance_changes_for_client()
pass
@abstractmethod
def save_changes(self):
"""Save changes happened during creation."""
self._send_instance_changes_to_client()
@abstractmethod
def remove_instances(self, instance_ids):
"""Remove list of instances from create context."""
# TODO add Args:
pass
@abstractmethod
def get_publish_report(self):
pass
@abstractmethod
def get_validation_errors(self):
pass
@abstractmethod
def reset(self):
"""Reset whole controller.
This should reset create context, publish context and all variables
that are related to it.
"""
self._send_instance_changes_to_client()
pass
@abstractmethod
def publish(self):
"""Trigger publishing without any order limitations."""
self._send_instance_changes_to_client()
pass
@abstractmethod
def validate(self):
"""Trigger publishing which will stop after validation order."""
self._send_instance_changes_to_client()
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, action_id):
"""Trigger pyblish action on a plugin.
Args:
plugin_id (str): Id of publish plugin.
action_id (str): Id of publish action.
"""
pass
@abstractmethod
def set_comment(self, comment):
"""Set comment on pyblish context.
Set "comment" key on current pyblish.api.Context data.
Args:
comment (str): Artist's comment.
"""
pass
@abstractmethod
def emit_card_message(self, message):
"""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.
"""
pass

View file

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

View file

@ -0,0 +1,758 @@
import logging
import re
from typing import Union, List, Dict, Tuple, Any, Optional, Iterable, Pattern
from ayon_core.lib.attribute_definitions import (
serialize_attr_defs,
deserialize_attr_defs,
AbstractAttrDef,
)
from ayon_core.lib.profiles_filtering import filter_profiles
from ayon_core.lib.attribute_definitions import UIDef
from ayon_core.pipeline.create import (
BaseCreator,
AutoCreator,
HiddenCreator,
Creator,
CreateContext,
CreatedInstance,
)
from ayon_core.pipeline.create.context import (
CreatorsOperationFailed,
ConvertorsOperationFailed,
ConvertorItem,
)
from ayon_core.tools.publisher.abstract import (
AbstractPublisherBackend,
CardMessageTypes,
)
CREATE_EVENT_SOURCE = "publisher.create.model"
class CreatorType:
def __init__(self, name: str):
self.name: str = name
def __str__(self):
return self.name
def __eq__(self, other):
return self.name == str(other)
def __ne__(self, other):
# This is implemented only because of Python 2
return not self == other
class CreatorTypes:
base = CreatorType("base")
auto = CreatorType("auto")
hidden = CreatorType("hidden")
artist = CreatorType("artist")
@classmethod
def from_str(cls, value: str) -> CreatorType:
for creator_type in (
cls.base,
cls.auto,
cls.hidden,
cls.artist
):
if value == creator_type:
return creator_type
raise ValueError("Unknown type \"{}\"".format(str(value)))
class CreatorItem:
"""Wrapper around Creator plugin.
Object can be serialized and recreated.
"""
def __init__(
self,
identifier: str,
creator_type: CreatorType,
product_type: str,
label: str,
group_label: str,
icon: Union[str, Dict[str, Any], None],
description: Union[str, None],
detailed_description: Union[str, None],
default_variant: Union[str, None],
default_variants: Union[List[str], None],
create_allow_context_change: Union[bool, None],
create_allow_thumbnail: Union[bool, None],
show_order: int,
pre_create_attributes_defs: List[AbstractAttrDef],
):
self.identifier: str = identifier
self.creator_type: CreatorType = creator_type
self.product_type: str = product_type
self.label: str = label
self.group_label: str = group_label
self.icon: Union[str, Dict[str, Any], None] = icon
self.description: Union[str, None] = description
self.detailed_description: Union[bool, None] = detailed_description
self.default_variant: Union[bool, None] = default_variant
self.default_variants: Union[List[str], None] = default_variants
self.create_allow_context_change: Union[bool, None] = (
create_allow_context_change
)
self.create_allow_thumbnail: Union[bool, None] = create_allow_thumbnail
self.show_order: int = show_order
self.pre_create_attributes_defs: List[AbstractAttrDef] = (
pre_create_attributes_defs
)
def get_group_label(self) -> str:
return self.group_label
@classmethod
def from_creator(cls, creator: BaseCreator):
creator_type: CreatorType = CreatorTypes.base
if isinstance(creator, AutoCreator):
creator_type = CreatorTypes.auto
elif isinstance(creator, HiddenCreator):
creator_type = CreatorTypes.hidden
elif isinstance(creator, Creator):
creator_type = CreatorTypes.artist
description = None
detail_description = None
default_variant = None
default_variants = None
pre_create_attr_defs = None
create_allow_context_change = None
create_allow_thumbnail = None
show_order = creator.order
if creator_type is CreatorTypes.artist:
description = creator.get_description()
detail_description = creator.get_detail_description()
default_variant = creator.get_default_variant()
default_variants = creator.get_default_variants()
pre_create_attr_defs = creator.get_pre_create_attr_defs()
create_allow_context_change = creator.create_allow_context_change
create_allow_thumbnail = creator.create_allow_thumbnail
show_order = creator.show_order
identifier = creator.identifier
return cls(
identifier,
creator_type,
creator.product_type,
creator.label or identifier,
creator.get_group_label(),
creator.get_icon(),
description,
detail_description,
default_variant,
default_variants,
create_allow_context_change,
create_allow_thumbnail,
show_order,
pre_create_attr_defs,
)
def to_data(self) -> Dict[str, Any]:
pre_create_attributes_defs = None
if self.pre_create_attributes_defs is not None:
pre_create_attributes_defs = serialize_attr_defs(
self.pre_create_attributes_defs
)
return {
"identifier": self.identifier,
"creator_type": str(self.creator_type),
"product_type": self.product_type,
"label": self.label,
"group_label": self.group_label,
"icon": self.icon,
"description": self.description,
"detailed_description": self.detailed_description,
"default_variant": self.default_variant,
"default_variants": self.default_variants,
"create_allow_context_change": self.create_allow_context_change,
"create_allow_thumbnail": self.create_allow_thumbnail,
"show_order": self.show_order,
"pre_create_attributes_defs": pre_create_attributes_defs,
}
@classmethod
def from_data(cls, data: Dict[str, Any]) -> "CreatorItem":
pre_create_attributes_defs = data["pre_create_attributes_defs"]
if pre_create_attributes_defs is not None:
data["pre_create_attributes_defs"] = deserialize_attr_defs(
pre_create_attributes_defs
)
data["creator_type"] = CreatorTypes.from_str(data["creator_type"])
return cls(**data)
class CreateModel:
def __init__(self, controller: AbstractPublisherBackend):
self._log = None
self._controller: AbstractPublisherBackend = controller
self._create_context = CreateContext(
controller.get_host(),
headless=controller.is_headless(),
reset=False
)
# State flags to prevent executing method which is already in progress
self._creator_items = None
@property
def log(self) -> logging.Logger:
if self._log is None:
self._log = logging.getLogger(self.__class__.__name__)
return self._log
def is_host_valid(self) -> bool:
return self._create_context.host_is_valid
def get_create_context(self) -> CreateContext:
return self._create_context
def get_current_project_name(self) -> Union[str, None]:
"""Current project context defined by host.
Returns:
str: Project name.
"""
return self._create_context.get_current_project_name()
def get_current_folder_path(self) -> Union[str, None]:
"""Current context folder path defined by host.
Returns:
Union[str, None]: Folder path or None if folder is not set.
"""
return self._create_context.get_current_folder_path()
def get_current_task_name(self) -> Union[str, None]:
"""Current context task name defined by host.
Returns:
Union[str, None]: Task name or None if task is not set.
"""
return self._create_context.get_current_task_name()
def host_context_has_changed(self) -> bool:
return self._create_context.context_has_changed
def reset(self):
self._create_context.reset_preparation()
# Reset current context
self._create_context.reset_current_context()
self._create_context.reset_plugins()
# Reset creator items
self._creator_items = None
self._reset_instances()
self._create_context.reset_finalization()
def get_creator_items(self) -> Dict[str, CreatorItem]:
"""Creators that can be shown in create dialog."""
if self._creator_items is None:
self._creator_items = self._collect_creator_items()
return self._creator_items
def get_creator_item_by_id(
self, identifier: str
) -> Union[CreatorItem, None]:
items = self.get_creator_items()
return items.get(identifier)
def get_creator_icon(
self, identifier: str
) -> Union[str, Dict[str, Any], None]:
"""Function to receive icon for creator identifier.
Args:
identifier (str): Creator's identifier for which should
be icon returned.
"""
creator_item = self.get_creator_item_by_id(identifier)
if creator_item is not None:
return creator_item.icon
return None
def get_instances(self) -> List[CreatedInstance]:
"""Current instances in create context."""
return list(self._create_context.instances_by_id.values())
def get_instance_by_id(
self, instance_id: str
) -> Union[CreatedInstance, None]:
return self._create_context.instances_by_id.get(instance_id)
def get_instances_by_id(
self, instance_ids: Optional[Iterable[str]] = None
) -> Dict[str, Union[CreatedInstance, None]]:
if instance_ids is None:
instance_ids = self._create_context.instances_by_id.keys()
return {
instance_id: self.get_instance_by_id(instance_id)
for instance_id in instance_ids
}
def get_convertor_items(self) -> Dict[str, ConvertorItem]:
return self._create_context.convertor_items_by_id
def get_product_name(
self,
creator_identifier: str,
variant: str,
task_name: Union[str, None],
folder_path: Union[str, None],
instance_id: Optional[str] = None
) -> str:
"""Get product name based on passed data.
Args:
creator_identifier (str): Identifier of creator which should be
responsible for product name creation.
variant (str): Variant value from user's input.
task_name (str): Name of task for which is instance created.
folder_path (str): Folder path for which is instance created.
instance_id (Union[str, None]): Existing instance id when product
name is updated.
"""
creator = self._creators[creator_identifier]
instance = None
if instance_id:
instance = self.get_instance_by_id(instance_id)
project_name = self._controller.get_current_project_name()
folder_item = self._controller.get_folder_item_by_path(
project_name, folder_path
)
folder_entity = None
task_item = None
task_entity = None
if folder_item is not None:
folder_entity = self._controller.get_folder_entity(
project_name, folder_item.entity_id
)
task_item = self._controller.get_task_item_by_name(
project_name,
folder_item.entity_id,
task_name,
CREATE_EVENT_SOURCE
)
if task_item is not None:
task_entity = self._controller.get_task_entity(
project_name, task_item.task_id
)
return creator.get_product_name(
project_name,
folder_entity,
task_entity,
variant,
instance=instance
)
def create(
self,
creator_identifier: str,
product_name: str,
instance_data: Dict[str, Any],
options: Dict[str, Any],
):
"""Trigger creation and refresh of instances in UI."""
success = True
try:
self._create_context.create_with_unified_error(
creator_identifier, product_name, instance_data, options
)
except CreatorsOperationFailed as exc:
success = False
self._emit_event(
"instances.create.failed",
{
"title": "Creation failed",
"failed_info": exc.failed_info
}
)
self._on_create_instance_change()
return success
def trigger_convertor_items(self, convertor_identifiers: List[str]):
"""Trigger legacy item convertors.
This functionality requires to save and reset CreateContext. The reset
is needed so Creators can collect converted items.
Args:
convertor_identifiers (list[str]): Identifiers of convertor
plugins.
"""
success = True
try:
self._create_context.run_convertors(convertor_identifiers)
except ConvertorsOperationFailed as exc:
success = False
self._emit_event(
"convertors.convert.failed",
{
"title": "Conversion failed",
"failed_info": exc.failed_info
}
)
if success:
self._controller.emit_card_message(
"Conversion finished"
)
else:
self._controller.emit_card_message(
"Conversion failed",
CardMessageTypes.error
)
def save_changes(self, show_message: Optional[bool] = True) -> bool:
"""Save changes happened during creation.
Trigger save of changes using host api. This functionality does not
validate anything. It is required to do checks before this method is
called to be able to give user actionable response e.g. check of
context using 'host_context_has_changed'.
Args:
show_message (bool): Show message that changes were
saved successfully.
Returns:
bool: Save of changes was successful.
"""
if not self._create_context.host_is_valid:
# TODO remove
# Fake success save when host is not valid for CreateContext
# this is for testing as experimental feature
return True
try:
self._create_context.save_changes()
if show_message:
self._controller.emit_card_message("Saved changes..")
return True
except CreatorsOperationFailed as exc:
self._emit_event(
"instances.save.failed",
{
"title": "Instances save failed",
"failed_info": exc.failed_info
}
)
return False
def remove_instances(self, instance_ids: List[str]):
"""Remove instances based on instance ids.
Args:
instance_ids (List[str]): List of instance ids to remove.
"""
# QUESTION Expect that instances are really removed? In that case reset
# is not required.
self._remove_instances_from_context(instance_ids)
self._on_create_instance_change()
def get_creator_attribute_definitions(
self, instances: List[CreatedInstance]
) -> List[Tuple[AbstractAttrDef, List[CreatedInstance], List[Any]]]:
"""Collect creator attribute definitions for multuple instances.
Args:
instances (List[CreatedInstance]): List of created instances for
which should be attribute definitions returned.
"""
# NOTE it would be great if attrdefs would have hash method implemented
# so they could be used as keys in dictionary
output = []
_attr_defs = {}
for instance in instances:
for attr_def in instance.creator_attribute_defs:
found_idx = None
for idx, _attr_def in _attr_defs.items():
if attr_def == _attr_def:
found_idx = idx
break
value = None
if attr_def.is_value_def:
value = instance.creator_attributes[attr_def.key]
if found_idx is None:
idx = len(output)
output.append((attr_def, [instance], [value]))
_attr_defs[idx] = attr_def
else:
item = output[found_idx]
item[1].append(instance)
item[2].append(value)
return output
def get_publish_attribute_definitions(
self,
instances: List[CreatedInstance],
include_context: bool
) -> List[Tuple[
str,
List[AbstractAttrDef],
Dict[str, List[Tuple[CreatedInstance, Any]]]
]]:
"""Collect publish attribute definitions for passed instances.
Args:
instances (list[CreatedInstance]): List of created instances for
which should be attribute definitions returned.
include_context (bool): Add context specific attribute definitions.
"""
_tmp_items = []
if include_context:
_tmp_items.append(self._create_context)
for instance in instances:
_tmp_items.append(instance)
all_defs_by_plugin_name = {}
all_plugin_values = {}
for item in _tmp_items:
for plugin_name, attr_val in item.publish_attributes.items():
attr_defs = attr_val.attr_defs
if not attr_defs:
continue
if plugin_name not in all_defs_by_plugin_name:
all_defs_by_plugin_name[plugin_name] = attr_val.attr_defs
plugin_values = all_plugin_values.setdefault(plugin_name, {})
for attr_def in attr_defs:
if isinstance(attr_def, UIDef):
continue
attr_values = plugin_values.setdefault(attr_def.key, [])
value = attr_val[attr_def.key]
attr_values.append((item, value))
output = []
for plugin in self._create_context.plugins_with_defs:
plugin_name = plugin.__name__
if plugin_name not in all_defs_by_plugin_name:
continue
output.append((
plugin_name,
all_defs_by_plugin_name[plugin_name],
all_plugin_values
))
return output
def get_thumbnail_paths_for_instances(
self, instance_ids: List[str]
) -> Dict[str, Union[str, None]]:
thumbnail_paths_by_instance_id = (
self._create_context.thumbnail_paths_by_instance_id
)
return {
instance_id: thumbnail_paths_by_instance_id.get(instance_id)
for instance_id in instance_ids
}
def set_thumbnail_paths_for_instances(
self, thumbnail_path_mapping: Dict[str, str]
):
thumbnail_paths_by_instance_id = (
self._create_context.thumbnail_paths_by_instance_id
)
for instance_id, thumbnail_path in thumbnail_path_mapping.items():
thumbnail_paths_by_instance_id[instance_id] = thumbnail_path
self._emit_event(
"instance.thumbnail.changed",
{
"mapping": thumbnail_path_mapping
}
)
def _emit_event(self, topic: str, data: Optional[Dict[str, Any]] = None):
self._controller.emit_event(topic, data)
def _get_current_project_settings(self) -> Dict[str, Any]:
"""Current project settings.
Returns:
dict
"""
return self._create_context.get_current_project_settings()
@property
def _creators(self) -> Dict[str, BaseCreator]:
"""All creators loaded in create context."""
return self._create_context.creators
def _reset_instances(self):
"""Reset create instances."""
self._create_context.reset_context_data()
with self._create_context.bulk_instances_collection():
try:
self._create_context.reset_instances()
except CreatorsOperationFailed as exc:
self._emit_event(
"instances.collection.failed",
{
"title": "Instance collection failed",
"failed_info": exc.failed_info
}
)
try:
self._create_context.find_convertor_items()
except ConvertorsOperationFailed as exc:
self._emit_event(
"convertors.find.failed",
{
"title": "Collection of unsupported product failed",
"failed_info": exc.failed_info
}
)
try:
self._create_context.execute_autocreators()
except CreatorsOperationFailed as exc:
self._emit_event(
"instances.create.failed",
{
"title": "AutoCreation failed",
"failed_info": exc.failed_info
}
)
self._on_create_instance_change()
def _remove_instances_from_context(self, instance_ids: List[str]):
instances_by_id = self._create_context.instances_by_id
instances = [
instances_by_id[instance_id]
for instance_id in instance_ids
]
try:
self._create_context.remove_instances(instances)
except CreatorsOperationFailed as exc:
self._emit_event(
"instances.remove.failed",
{
"title": "Instance removement failed",
"failed_info": exc.failed_info
}
)
def _on_create_instance_change(self):
self._emit_event("instances.refresh.finished")
def _collect_creator_items(self) -> Dict[str, CreatorItem]:
# TODO add crashed initialization of create plugins to report
output = {}
allowed_creator_pattern = self._get_allowed_creators_pattern()
for identifier, creator in self._create_context.creators.items():
try:
if self._is_label_allowed(
creator.label, allowed_creator_pattern
):
output[identifier] = CreatorItem.from_creator(creator)
continue
self.log.debug(f"{creator.label} not allowed for context")
except Exception:
self.log.error(
"Failed to create creator item for '%s'",
identifier,
exc_info=True
)
return output
def _get_allowed_creators_pattern(self) -> Union[Pattern, None]:
"""Provide regex pattern for configured creator labels in this context
If no profile matches current context, it shows all creators.
Support usage of regular expressions for configured values.
Returns:
(re.Pattern)[optional]: None or regex compiled patterns
into single one ('Render|Image.*')
"""
task_type = self._create_context.get_current_task_type()
project_settings = self._get_current_project_settings()
filter_creator_profiles = (
project_settings
["core"]
["tools"]
["creator"]
["filter_creator_profiles"]
)
filtering_criteria = {
"task_names": self.get_current_task_name(),
"task_types": task_type,
"host_names": self._create_context.host_name
}
profile = filter_profiles(
filter_creator_profiles,
filtering_criteria,
logger=self.log
)
allowed_creator_pattern = None
if profile:
allowed_creator_labels = {
label
for label in profile["creator_labels"]
if label
}
self.log.debug(f"Only allowed `{allowed_creator_labels}` creators")
allowed_creator_pattern = (
re.compile("|".join(allowed_creator_labels)))
return allowed_creator_pattern
def _is_label_allowed(
self,
label: str,
allowed_labels_regex: Union[Pattern, None]
) -> bool:
"""Implement regex support for allowed labels.
Args:
label (str): Label of creator - shown in Publisher
allowed_labels_regex (re.Pattern): compiled regular expression
"""
if not allowed_labels_regex:
return True
return bool(allowed_labels_regex.match(label))

File diff suppressed because it is too large Load diff

View file

@ -17,7 +17,7 @@ from .constants import (
class InstancesModel(QtGui.QStandardItemModel):
def __init__(self, *args, **kwargs):
super(InstancesModel, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self._items_by_id = {}
self._plugin_items_by_id = {}
@ -83,7 +83,7 @@ class InstancesModel(QtGui.QStandardItemModel):
class InstanceProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(InstanceProxyModel, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self._ignore_removed = True
@ -116,7 +116,7 @@ class PluginsModel(QtGui.QStandardItemModel):
)
def __init__(self, *args, **kwargs):
super(PluginsModel, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self._items_by_id = {}
self._plugin_items_by_id = {}
@ -185,7 +185,7 @@ class PluginsModel(QtGui.QStandardItemModel):
class PluginProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(PluginProxyModel, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self._ignore_skipped = True

View file

@ -52,7 +52,7 @@ class PluginLoadReportModel(QtGui.QStandardItemModel):
class DetailWidget(QtWidgets.QTextEdit):
def __init__(self, text, *args, **kwargs):
super(DetailWidget, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.setReadOnly(True)
self.setHtml(text)
@ -73,7 +73,7 @@ class DetailWidget(QtWidgets.QTextEdit):
class PluginLoadReportWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(PluginLoadReportWidget, self).__init__(parent)
super().__init__(parent)
view = QtWidgets.QTreeView(self)
view.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
@ -101,11 +101,11 @@ class PluginLoadReportWidget(QtWidgets.QWidget):
self._create_widget(child_index)
def showEvent(self, event):
super(PluginLoadReportWidget, self).showEvent(event)
super().showEvent(event)
self._update_widgets_size_hints()
def resizeEvent(self, event):
super(PluginLoadReportWidget, self).resizeEvent(event)
super().resizeEvent(event)
self._update_widgets_size_hints()
def _update_widgets_size_hints(self):
@ -146,7 +146,7 @@ class ZoomPlainText(QtWidgets.QPlainTextEdit):
max_point_size = 200.0
def __init__(self, *args, **kwargs):
super(ZoomPlainText, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
anim_timer = QtCore.QTimer()
anim_timer.setInterval(20)
@ -160,7 +160,7 @@ class ZoomPlainText(QtWidgets.QPlainTextEdit):
def wheelEvent(self, event):
modifiers = QtWidgets.QApplication.keyboardModifiers()
if modifiers != QtCore.Qt.ControlModifier:
super(ZoomPlainText, self).wheelEvent(event)
super().wheelEvent(event)
return
if hasattr(event, "angleDelta"):
@ -219,7 +219,7 @@ class ZoomPlainText(QtWidgets.QPlainTextEdit):
class DetailsWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(DetailsWidget, self).__init__(parent)
super().__init__(parent)
output_widget = ZoomPlainText(self)
output_widget.setObjectName("PublishLogConsole")
@ -327,7 +327,7 @@ class DetailsPopup(QtWidgets.QDialog):
closed = QtCore.Signal()
def __init__(self, parent, center_widget):
super(DetailsPopup, self).__init__(parent)
super().__init__(parent)
self.setWindowTitle("Report Details")
layout = QtWidgets.QHBoxLayout(self)
@ -338,19 +338,19 @@ class DetailsPopup(QtWidgets.QDialog):
def showEvent(self, event):
layout = self.layout()
layout.insertWidget(0, self._center_widget)
super(DetailsPopup, self).showEvent(event)
super().showEvent(event)
if self._first_show:
self._first_show = False
self.resize(700, 400)
def closeEvent(self, event):
super(DetailsPopup, self).closeEvent(event)
super().closeEvent(event)
self.closed.emit()
class PublishReportViewerWidget(QtWidgets.QFrame):
def __init__(self, parent=None):
super(PublishReportViewerWidget, self).__init__(parent)
super().__init__(parent)
instances_model = InstancesModel()
instances_proxy = InstanceProxyModel()

View file

@ -302,7 +302,7 @@ class LoadedFilesModel(QtGui.QStandardItemModel):
header_labels = ("Reports", "Created")
def __init__(self, *args, **kwargs):
super(LoadedFilesModel, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
# Column count must be set before setting header data
self.setColumnCount(len(self.header_labels))
@ -350,7 +350,7 @@ class LoadedFilesModel(QtGui.QStandardItemModel):
if col != 0:
index = self.index(index.row(), 0, index.parent())
return super(LoadedFilesModel, self).data(index, role)
return super().data(index, role)
def setData(self, index, value, role=None):
if role is None:
@ -364,13 +364,13 @@ class LoadedFilesModel(QtGui.QStandardItemModel):
report_item.save()
value = report_item.label
return super(LoadedFilesModel, self).setData(index, value, role)
return super().setData(index, value, role)
def flags(self, index):
# Allow editable flag only for first column
if index.column() > 0:
return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
return super(LoadedFilesModel, self).flags(index)
return super().flags(index)
def _create_item(self, report_item):
if report_item.id in self._items_by_id:
@ -451,7 +451,7 @@ class LoadedFilesView(QtWidgets.QTreeView):
selection_changed = QtCore.Signal()
def __init__(self, *args, **kwargs):
super(LoadedFilesView, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.setEditTriggers(
QtWidgets.QAbstractItemView.EditKeyPressed
| QtWidgets.QAbstractItemView.SelectedClicked
@ -502,11 +502,11 @@ class LoadedFilesView(QtWidgets.QTreeView):
self._update_remove_btn()
def resizeEvent(self, event):
super(LoadedFilesView, self).resizeEvent(event)
super().resizeEvent(event)
self._update_remove_btn()
def showEvent(self, event):
super(LoadedFilesView, self).showEvent(event)
super().showEvent(event)
self._model.refresh()
header = self.header()
header.resizeSections(QtWidgets.QHeaderView.ResizeToContents)
@ -548,7 +548,7 @@ class LoadedFilesWidget(QtWidgets.QWidget):
report_changed = QtCore.Signal()
def __init__(self, parent):
super(LoadedFilesWidget, self).__init__(parent)
super().__init__(parent)
self.setAcceptDrops(True)
@ -598,7 +598,7 @@ class PublishReportViewerWindow(QtWidgets.QWidget):
default_height = 600
def __init__(self, parent=None):
super(PublishReportViewerWindow, self).__init__(parent)
super().__init__(parent)
self.setWindowTitle("Publish report viewer")
icon = QtGui.QIcon(get_ayon_icon_filepath())
self.setWindowIcon(icon)

View file

@ -15,7 +15,7 @@ class _VLineWidget(QtWidgets.QWidget):
It is expected that parent widget will set width.
"""
def __init__(self, color, line_size, left, parent):
super(_VLineWidget, self).__init__(parent)
super().__init__(parent)
self._color = color
self._left = left
self._line_size = line_size
@ -69,7 +69,7 @@ class _HBottomLineWidget(QtWidgets.QWidget):
It is expected that parent widget will set height and radius.
"""
def __init__(self, color, line_size, parent):
super(_HBottomLineWidget, self).__init__(parent)
super().__init__(parent)
self._color = color
self._radius = 0
self._line_size = line_size
@ -128,7 +128,7 @@ class _HTopCornerLineWidget(QtWidgets.QWidget):
"""
def __init__(self, color, line_size, left_side, parent):
super(_HTopCornerLineWidget, self).__init__(parent)
super().__init__(parent)
self._left_side = left_side
self._line_size = line_size
self._color = color
@ -192,7 +192,7 @@ class BorderedLabelWidget(QtWidgets.QFrame):
"""
def __init__(self, label, parent):
super(BorderedLabelWidget, self).__init__(parent)
super().__init__(parent)
color_value = get_objected_colors("border")
color = None
if color_value:
@ -269,7 +269,7 @@ class BorderedLabelWidget(QtWidgets.QFrame):
self._recalculate_sizes()
def showEvent(self, event):
super(BorderedLabelWidget, self).showEvent(event)
super().showEvent(event)
self._recalculate_sizes()
def _recalculate_sizes(self):

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:
@ -55,7 +58,7 @@ class BaseGroupWidget(QtWidgets.QWidget):
double_clicked = QtCore.Signal()
def __init__(self, group_name, parent):
super(BaseGroupWidget, self).__init__(parent)
super().__init__(parent)
label_widget = QtWidgets.QLabel(group_name, self)
@ -207,7 +210,7 @@ class InstanceGroupWidget(BaseGroupWidget):
active_changed = QtCore.Signal(str, str, bool)
def __init__(self, group_icons, *args, **kwargs):
super(InstanceGroupWidget, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self._group_icons = group_icons
@ -277,14 +280,14 @@ class CardWidget(BaseClickableFrame):
double_clicked = QtCore.Signal()
def __init__(self, parent):
super(CardWidget, self).__init__(parent)
super().__init__(parent)
self.setObjectName("CardViewWidget")
self._selected = False
self._id = None
def mouseDoubleClickEvent(self, event):
super(CardWidget, self).mouseDoubleClickEvent(event)
super().mouseDoubleClickEvent(event)
if self._is_valid_double_click(event):
self.double_clicked.emit()
@ -332,7 +335,7 @@ class ContextCardWidget(CardWidget):
"""
def __init__(self, parent):
super(ContextCardWidget, self).__init__(parent)
super().__init__(parent)
self._id = CONTEXT_ID
self._group_identifier = CONTEXT_GROUP
@ -362,7 +365,7 @@ class ConvertorItemCardWidget(CardWidget):
"""
def __init__(self, item, parent):
super(ConvertorItemCardWidget, self).__init__(parent)
super().__init__(parent)
self._id = item.id
self.identifier = item.identifier
@ -395,7 +398,7 @@ class InstanceCardWidget(CardWidget):
active_changed = QtCore.Signal(str, bool)
def __init__(self, instance, group_icon, parent):
super(InstanceCardWidget, self).__init__(parent)
super().__init__(parent)
self._id = instance.id
self._group_identifier = instance.group_label
@ -558,9 +561,9 @@ class InstanceCardView(AbstractInstanceView):
double_clicked = QtCore.Signal()
def __init__(self, controller, parent):
super(InstanceCardView, self).__init__(parent)
super().__init__(parent)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
scroll_area = QtWidgets.QScrollArea(self)
scroll_area.setWidgetResizable(True)
@ -610,7 +613,7 @@ class InstanceCardView(AbstractInstanceView):
+ scroll_bar.sizeHint().width()
)
result = super(InstanceCardView, self).sizeHint()
result = super().sizeHint()
result.setWidth(width)
return result
@ -651,7 +654,7 @@ class InstanceCardView(AbstractInstanceView):
self._toggle_instances(1)
return True
return super(InstanceCardView, self).keyPressEvent(event)
return super().keyPressEvent(event)
def _get_selected_widgets(self):
output = []
@ -694,7 +697,7 @@ class InstanceCardView(AbstractInstanceView):
# Prepare instances by group and identifiers by group
instances_by_group = collections.defaultdict(list)
identifiers_by_group = collections.defaultdict(set)
for instance in self._controller.instances.values():
for instance in self._controller.get_instances():
group_name = instance.group_label
instances_by_group[group_name].append(instance)
identifiers_by_group[group_name].add(
@ -787,7 +790,7 @@ class InstanceCardView(AbstractInstanceView):
self._content_layout.insertWidget(0, widget)
def _update_convertor_items_group(self):
convertor_items = self._controller.convertor_items
convertor_items = self._controller.get_convertor_items()
if not convertor_items and self._convertor_items_group is None:
return

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
@ -118,7 +119,7 @@ class CreateHierarchyController:
self.event_system.add_callback(topic, callback)
def get_project_name(self):
return self._controller.project_name
return self._controller.get_current_project_name()
def get_folder_items(self, project_name, sender=None):
return self._controller.get_folder_items(project_name, sender)
@ -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
@ -234,12 +235,12 @@ class CreateContextWidget(QtWidgets.QWidget):
def update_current_context_btn(self):
# Hide set current folder if there is no one
folder_path = self._controller.current_folder_path
folder_path = self._controller.get_current_folder_path()
self._current_context_btn.setVisible(bool(folder_path))
def set_selected_context(self, folder_id, task_name):
self._hierarchy_controller.set_expected_selection(
self._controller.project_name,
self._controller.get_current_project_name(),
folder_id,
task_name
)
@ -270,13 +271,13 @@ class CreateContextWidget(QtWidgets.QWidget):
)
def refresh(self):
self._last_project_name = self._controller.project_name
self._last_project_name = self._controller.get_current_project_name()
folder_id = self._last_folder_id
task_name = self._last_selected_task_name
if folder_id is None:
folder_path = self._controller.current_folder_path
folder_path = self._controller.get_current_folder_path()
folder_id = self._controller.get_folder_id_from_path(folder_path)
task_name = self._controller.current_task_name
task_name = self._controller.get_current_task_name()
self._hierarchy_controller.set_selected_project(
self._last_project_name
)
@ -295,8 +296,8 @@ class CreateContextWidget(QtWidgets.QWidget):
self.task_changed.emit()
def _on_current_context_click(self):
folder_path = self._controller.current_folder_path
task_name = self._controller.current_task_name
folder_path = self._controller.get_current_folder_path()
task_name = self._controller.get_current_task_name()
folder_id = self._controller.get_folder_id_from_path(folder_path)
self._hierarchy_controller.set_expected_selection(
self._last_project_name, folder_id, task_name

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---", "---")
@ -33,14 +35,14 @@ class ResizeControlWidget(QtWidgets.QWidget):
resized = QtCore.Signal()
def resizeEvent(self, event):
super(ResizeControlWidget, self).resizeEvent(event)
super().resizeEvent(event)
self.resized.emit()
# TODO add creator identifier/label to details
class CreatorShortDescWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(CreatorShortDescWidget, self).__init__(parent=parent)
super().__init__(parent=parent)
# --- Short description widget ---
icon_widget = IconValuePixmapLabel(None, self)
@ -98,15 +100,15 @@ class CreatorsProxyModel(QtCore.QSortFilterProxyModel):
l_show_order = left.data(CREATOR_SORT_ROLE)
r_show_order = right.data(CREATOR_SORT_ROLE)
if l_show_order == r_show_order:
return super(CreatorsProxyModel, self).lessThan(left, right)
return super().lessThan(left, right)
return l_show_order < r_show_order
class CreateWidget(QtWidgets.QWidget):
def __init__(self, controller, parent=None):
super(CreateWidget, self).__init__(parent)
super().__init__(parent)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._folder_path = None
self._product_names = None
@ -274,11 +276,11 @@ class CreateWidget(QtWidgets.QWidget):
thumbnail_widget.thumbnail_created.connect(self._on_thumbnail_create)
thumbnail_widget.thumbnail_cleared.connect(self._on_thumbnail_clear)
controller.event_system.add_callback(
controller.register_event_callback(
"main.window.closed", self._on_main_window_close
)
controller.event_system.add_callback(
"plugins.refresh.finished", self._on_plugins_refresh
controller.register_event_callback(
"controller.reset.finished", self._on_controler_reset
)
self._main_splitter_widget = main_splitter_widget
@ -313,13 +315,11 @@ class CreateWidget(QtWidgets.QWidget):
self._last_current_context_task = None
self._use_current_context = True
@property
def current_folder_path(self):
return self._controller.current_folder_path
def get_current_folder_path(self):
return self._controller.get_current_folder_path()
@property
def current_task_name(self):
return self._controller.current_task_name
def get_current_task_name(self):
return self._controller.get_current_task_name()
def _context_change_is_enabled(self):
return self._context_widget.is_enabled()
@ -330,7 +330,7 @@ class CreateWidget(QtWidgets.QWidget):
folder_path = self._context_widget.get_selected_folder_path()
if folder_path is None:
folder_path = self.current_folder_path
folder_path = self.get_current_folder_path()
return folder_path or None
def _get_folder_id(self):
@ -348,7 +348,7 @@ class CreateWidget(QtWidgets.QWidget):
task_name = self._context_widget.get_selected_task_name()
if not task_name:
task_name = self.current_task_name
task_name = self.get_current_task_name()
return task_name
def _set_context_enabled(self, enabled):
@ -364,8 +364,8 @@ class CreateWidget(QtWidgets.QWidget):
self._use_current_context = True
def refresh(self):
current_folder_path = self._controller.current_folder_path
current_task_name = self._controller.current_task_name
current_folder_path = self._controller.get_current_folder_path()
current_task_name = self._controller.get_current_task_name()
# Get context before refresh to keep selection of folder and
# task widgets
@ -481,7 +481,7 @@ class CreateWidget(QtWidgets.QWidget):
# Add new create plugins
new_creators = set()
creator_items_by_identifier = self._controller.creator_items
creator_items_by_identifier = self._controller.get_creator_items()
for identifier, creator_item in creator_items_by_identifier.items():
if creator_item.creator_type != "artist":
continue
@ -531,7 +531,7 @@ class CreateWidget(QtWidgets.QWidget):
self._set_creator(create_item)
def _on_plugins_refresh(self):
def _on_controler_reset(self):
# Trigger refresh only if is visible
self.refresh()
@ -562,7 +562,7 @@ class CreateWidget(QtWidgets.QWidget):
description = ""
if creator_item is not None:
description = creator_item.detailed_description or description
self._controller.event_system.emit(
self._controller.emit_event(
"show.detailed.help",
{
"message": description
@ -571,7 +571,7 @@ class CreateWidget(QtWidgets.QWidget):
)
def _set_creator_by_identifier(self, identifier):
creator_item = self._controller.creator_items.get(identifier)
creator_item = self._controller.get_creator_item_by_id(identifier)
self._set_creator(creator_item)
def _set_creator(self, creator_item):
@ -755,7 +755,7 @@ class CreateWidget(QtWidgets.QWidget):
self._creators_splitter.setSizes([part, rem_width])
def showEvent(self, event):
super(CreateWidget, self).showEvent(event)
super().showEvent(event)
if self._first_show:
self._first_show = False
self._on_first_show()

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):
@ -39,7 +40,7 @@ class FoldersDialog(QtWidgets.QDialog):
"""Dialog to select folder for a context of instance."""
def __init__(self, controller, parent):
super(FoldersDialog, self).__init__(parent)
super().__init__(parent)
self.setWindowTitle("Select folder")
filter_input = PlaceholderLineEdit(self)
@ -62,7 +63,7 @@ class FoldersDialog(QtWidgets.QDialog):
layout.addWidget(folders_widget, 1)
layout.addLayout(btns_layout, 0)
controller.event_system.add_callback(
controller.register_event_callback(
"controller.reset.finished", self._on_controller_reset
)
@ -104,7 +105,7 @@ class FoldersDialog(QtWidgets.QDialog):
def showEvent(self, event):
"""Refresh folders widget on show."""
super(FoldersDialog, self).showEvent(event)
super().showEvent(event)
if self._first_show:
self._first_show = False
self._on_first_show()
@ -119,7 +120,9 @@ class FoldersDialog(QtWidgets.QDialog):
if self._soft_reset_enabled:
self._soft_reset_enabled = False
self._folders_widget.set_project_name(self._controller.project_name)
self._folders_widget.set_project_name(
self._controller.get_current_project_name()
)
def _on_filter_change(self, text):
"""Trigger change of filter of folders."""

View file

@ -5,12 +5,14 @@ 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."""
def __init__(self, parent):
super(HelpButton, self).__init__(parent)
super().__init__(parent)
self.setObjectName("CreateDialogHelpButton")
self.setText("?")
@ -19,7 +21,7 @@ class HelpWidget(QtWidgets.QWidget):
"""Widget showing help for single functionality."""
def __init__(self, parent):
super(HelpWidget, self).__init__(parent)
super().__init__(parent)
# TODO add hints what to help with?
detail_description_input = QtWidgets.QTextEdit(self)
@ -54,8 +56,10 @@ class HelpDialog(QtWidgets.QDialog):
default_width = 530
default_height = 340
def __init__(self, controller, parent):
super(HelpDialog, self).__init__(parent)
def __init__(
self, controller: AbstractPublisherFrontend, parent: QtWidgets.QWidget
):
super().__init__(parent)
self.setWindowTitle("Help dialog")
@ -64,11 +68,11 @@ class HelpDialog(QtWidgets.QDialog):
main_layout = QtWidgets.QHBoxLayout(self)
main_layout.addWidget(help_content, 1)
controller.event_system.add_callback(
controller.register_event_callback(
"show.detailed.help", self._on_help_request
)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._help_content = help_content
@ -80,5 +84,5 @@ class HelpDialog(QtWidgets.QDialog):
self._help_content.set_detailed_text(text)
def showEvent(self, event):
super(HelpDialog, self).showEvent(event)
super().showEvent(event)
self.resize(self.default_width, self.default_height)

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.
@ -55,7 +58,7 @@ class ListItemDelegate(QtWidgets.QStyledItemDelegate):
radius_ratio = 0.3
def __init__(self, parent):
super(ListItemDelegate, self).__init__(parent)
super().__init__(parent)
group_color_info = get_objected_colors("publisher", "list-view-group")
@ -68,7 +71,7 @@ class ListItemDelegate(QtWidgets.QStyledItemDelegate):
if index.data(IS_GROUP_ROLE):
self.group_item_paint(painter, option, index)
else:
super(ListItemDelegate, self).paint(painter, option, index)
super().paint(painter, option, index)
def group_item_paint(self, painter, option, index):
"""Paint group item."""
@ -113,7 +116,7 @@ class InstanceListItemWidget(QtWidgets.QWidget):
double_clicked = QtCore.Signal()
def __init__(self, instance, parent):
super(InstanceListItemWidget, self).__init__(parent)
super().__init__(parent)
self.instance = instance
@ -152,7 +155,7 @@ class InstanceListItemWidget(QtWidgets.QWidget):
def mouseDoubleClickEvent(self, event):
widget = self.childAt(event.pos())
super(InstanceListItemWidget, self).mouseDoubleClickEvent(event)
super().mouseDoubleClickEvent(event)
if widget is not self._active_checkbox:
self.double_clicked.emit()
@ -219,7 +222,7 @@ class ListContextWidget(QtWidgets.QFrame):
double_clicked = QtCore.Signal()
def __init__(self, parent):
super(ListContextWidget, self).__init__(parent)
super().__init__(parent)
label_widget = QtWidgets.QLabel(CONTEXT_LABEL, self)
@ -235,7 +238,7 @@ class ListContextWidget(QtWidgets.QFrame):
self.label_widget = label_widget
def mouseDoubleClickEvent(self, event):
super(ListContextWidget, self).mouseDoubleClickEvent(event)
super().mouseDoubleClickEvent(event)
self.double_clicked.emit()
@ -249,7 +252,7 @@ class InstanceListGroupWidget(QtWidgets.QFrame):
toggle_requested = QtCore.Signal(str, int)
def __init__(self, group_name, parent):
super(InstanceListGroupWidget, self).__init__(parent)
super().__init__(parent)
self.setObjectName("InstanceListGroupWidget")
self.group_name = group_name
@ -333,7 +336,7 @@ class InstanceTreeView(QtWidgets.QTreeView):
double_clicked = QtCore.Signal()
def __init__(self, *args, **kwargs):
super(InstanceTreeView, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.setObjectName("InstanceListView")
self.setHeaderHidden(True)
@ -384,7 +387,7 @@ class InstanceTreeView(QtWidgets.QTreeView):
self.toggle_requested.emit(1)
return True
return super(InstanceTreeView, self).event(event)
return super().event(event)
def _mouse_press(self, event):
"""Store index of pressed group.
@ -404,11 +407,11 @@ class InstanceTreeView(QtWidgets.QTreeView):
def mousePressEvent(self, event):
self._mouse_press(event)
super(InstanceTreeView, self).mousePressEvent(event)
super().mousePressEvent(event)
def mouseDoubleClickEvent(self, event):
self._mouse_press(event)
super(InstanceTreeView, self).mouseDoubleClickEvent(event)
super().mouseDoubleClickEvent(event)
def _mouse_release(self, event, pressed_index):
if event.button() != QtCore.Qt.LeftButton:
@ -431,7 +434,7 @@ class InstanceTreeView(QtWidgets.QTreeView):
self._pressed_group_index = None
result = self._mouse_release(event, pressed_index)
if not result:
super(InstanceTreeView, self).mouseReleaseEvent(event)
super().mouseReleaseEvent(event)
class InstanceListView(AbstractInstanceView):
@ -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)
@ -581,7 +586,7 @@ class InstanceListView(AbstractInstanceView):
# Prepare instances by their groups
instances_by_group_name = collections.defaultdict(list)
group_names = set()
for instance in self._controller.instances.values():
for instance in self._controller.get_instances():
group_label = instance.group_label
group_names.add(group_label)
instances_by_group_name[group_label].append(instance)
@ -745,7 +750,7 @@ class InstanceListView(AbstractInstanceView):
def _update_convertor_items_group(self):
created_new_items = False
convertor_items_by_id = self._controller.convertor_items
convertor_items_by_id = self._controller.get_convertor_items()
group_item = self._convertor_group_item
if not convertor_items_by_id and group_item is None:
return created_new_items

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)
@ -139,16 +142,16 @@ class OverviewWidget(QtWidgets.QFrame):
)
# --- Controller callbacks ---
controller.event_system.add_callback(
controller.register_event_callback(
"publish.process.started", self._on_publish_start
)
controller.event_system.add_callback(
controller.register_event_callback(
"controller.reset.started", self._on_controller_reset_start
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.reset.finished", self._on_publish_reset
)
controller.event_system.add_callback(
controller.register_event_callback(
"instances.refresh.finished", self._on_instances_refresh
)
@ -291,7 +294,7 @@ class OverviewWidget(QtWidgets.QFrame):
# Disable delete button if nothing is selected
self._delete_btn.setEnabled(len(instance_ids) > 0)
instances_by_id = self._controller.instances
instances_by_id = self._controller.get_instances_by_id(instance_ids)
instances = [
instances_by_id[instance_id]
for instance_id in instance_ids
@ -454,7 +457,9 @@ class OverviewWidget(QtWidgets.QFrame):
self._create_btn.setEnabled(True)
self._product_attributes_wrap.setEnabled(True)
self._product_content_widget.setEnabled(self._controller.host_is_valid)
self._product_content_widget.setEnabled(
self._controller.is_host_valid()
)
def _on_instances_refresh(self):
"""Controller refreshed instances."""

View file

@ -7,7 +7,7 @@ from ..constants import INPUTS_LAYOUT_HSPACING, INPUTS_LAYOUT_VSPACING
class PreCreateWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(PreCreateWidget, self).__init__(parent)
super().__init__(parent)
# Precreate attribute defininitions of Creator
scroll_area = QtWidgets.QScrollArea(self)
@ -79,7 +79,7 @@ class PreCreateWidget(QtWidgets.QWidget):
class AttributesWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(AttributesWidget, self).__init__(parent)
super().__init__(parent)
layout = QtWidgets.QGridLayout(self)
layout.setContentsMargins(0, 0, 0, 0)

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
@ -157,29 +164,29 @@ class PublishFrame(QtWidgets.QWidget):
shrunk_anim.valueChanged.connect(self._on_shrunk_anim)
shrunk_anim.finished.connect(self._on_shrunk_anim_finish)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.reset.finished", self._on_publish_reset
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.process.started", self._on_publish_start
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.has_validated.changed", self._on_publish_validated_change
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.process.stopped", self._on_publish_stop
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.process.instance.changed", self._on_instance_change
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.process.plugin.changed", self._on_plugin_change
)
self._shrunk_anim = shrunk_anim
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._content_frame = content_frame
self._content_layout = content_layout
@ -208,7 +215,7 @@ class PublishFrame(QtWidgets.QWidget):
self._last_plugin_label = None
def mouseReleaseEvent(self, event):
super(PublishFrame, self).mouseReleaseEvent(event)
super().mouseReleaseEvent(event)
self._change_shrunk_state()
def _change_shrunk_state(self):
@ -314,8 +321,12 @@ class PublishFrame(QtWidgets.QWidget):
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)
self._progress_bar.setValue(
self._controller.get_publish_progress()
)
self._progress_bar.setMaximum(
self._controller.get_publish_max_progress()
)
def _on_publish_start(self):
if self._last_plugin_label:
@ -351,12 +362,12 @@ class PublishFrame(QtWidgets.QWidget):
"""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._progress_bar.setValue(self._controller.get_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._progress_bar.setValue(self._controller.get_publish_progress())
self._reset_btn.setEnabled(True)
self._stop_btn.setEnabled(False)
@ -364,31 +375,21 @@ class PublishFrame(QtWidgets.QWidget):
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
publish_enabled = self._controller.publish_can_continue()
validate_enabled = (
publish_enabled and not self._controller.publish_has_validated()
)
self._validate_btn.setEnabled(validate_enabled)
self._publish_btn.setEnabled(publish_enabled)
if self._controller.publish_has_crashed:
if self._controller.publish_has_crashed():
self._set_error_msg()
elif self._controller.publish_has_validation_errors:
elif self._controller.publish_has_validation_errors():
self._set_progress_visibility(False)
self._set_validation_errors()
elif self._controller.publish_has_finished:
elif self._controller.publish_has_finished():
self._set_finished()
else:
@ -411,7 +412,9 @@ class PublishFrame(QtWidgets.QWidget):
self._set_main_label("Error happened")
self._message_label_top.setText(self._controller.publish_error_msg)
self._message_label_top.setText(
self._controller.get_publish_error_msg()
)
self._set_success_property(1)
@ -467,11 +470,11 @@ class PublishFrame(QtWidgets.QWidget):
def _on_report_triggered(self, identifier):
if identifier == "export_report":
self._controller.event_system.emit(
self._controller.emit_event(
"export_report.request", {}, "publish_frame")
elif identifier == "copy_report":
self._controller.event_system.emit(
self._controller.emit_event(
"copy_report.request", {}, "publish_frame")
elif identifier == "go_to_report":

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
@ -50,7 +52,7 @@ class VerticalScrollArea(QtWidgets.QScrollArea):
"""
def __init__(self, *args, **kwargs):
super(VerticalScrollArea, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
@ -80,7 +82,7 @@ class VerticalScrollArea(QtWidgets.QScrollArea):
if old_widget:
old_widget.removeEventFilter(self)
super(VerticalScrollArea, self).setVerticalScrollBar(widget)
super().setVerticalScrollBar(widget)
if widget:
widget.installEventFilter(self)
@ -89,7 +91,7 @@ class VerticalScrollArea(QtWidgets.QScrollArea):
if old_widget:
old_widget.removeEventFilter(self)
super(VerticalScrollArea, self).setWidget(widget)
super().setWidget(widget)
if widget:
widget.installEventFilter(self)
@ -105,7 +107,7 @@ class VerticalScrollArea(QtWidgets.QScrollArea):
and (obj is self.widget() or obj is self.verticalScrollBar())
):
self._size_changed_timer.start()
return super(VerticalScrollArea, self).eventFilter(obj, event)
return super().eventFilter(obj, event)
# --- Publish actions widget ---
@ -122,7 +124,7 @@ class ActionButton(BaseClickableFrame):
action_clicked = QtCore.Signal(str, str)
def __init__(self, plugin_action_item, parent):
super(ActionButton, self).__init__(parent)
super().__init__(parent)
self.setObjectName("ValidationActionButton")
@ -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
@ -246,7 +250,7 @@ class ValidationErrorInstanceList(QtWidgets.QListView):
Instances are collected per plugin's validation error title.
"""
def __init__(self, *args, **kwargs):
super(ValidationErrorInstanceList, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.setObjectName("ValidationErrorInstanceList")
@ -257,7 +261,7 @@ class ValidationErrorInstanceList(QtWidgets.QListView):
return self.sizeHint()
def sizeHint(self):
result = super(ValidationErrorInstanceList, self).sizeHint()
result = super().sizeHint()
row_count = self.model().rowCount()
height = 0
if row_count > 0:
@ -280,7 +284,7 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget):
instance_changed = QtCore.Signal(str)
def __init__(self, title_id, error_info, parent):
super(ValidationErrorTitleWidget, self).__init__(parent)
super().__init__(parent)
self._title_id = title_id
self._error_info = error_info
@ -371,7 +375,7 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget):
self._expanded = False
def sizeHint(self):
result = super(ValidationErrorTitleWidget, self).sizeHint()
result = super().sizeHint()
expected_width = max(
self._view_widget.minimumSizeHint().width(),
self._view_widget.sizeHint().width()
@ -475,7 +479,7 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget):
class ValidationArtistMessage(QtWidgets.QWidget):
def __init__(self, message, parent):
super(ValidationArtistMessage, self).__init__(parent)
super().__init__(parent)
artist_msg_label = QtWidgets.QLabel(message, self)
artist_msg_label.setAlignment(QtCore.Qt.AlignCenter)
@ -491,7 +495,7 @@ class ValidationErrorsView(QtWidgets.QWidget):
selection_changed = QtCore.Signal()
def __init__(self, parent):
super(ValidationErrorsView, self).__init__(parent)
super().__init__(parent)
errors_scroll = VerticalScrollArea(self)
errors_scroll.setWidgetResizable(True)
@ -715,7 +719,7 @@ class _InstanceItem:
class FamilyGroupLabel(QtWidgets.QWidget):
def __init__(self, family, parent):
super(FamilyGroupLabel, self).__init__(parent)
super().__init__(parent)
self.setLayoutDirection(QtCore.Qt.LeftToRight)
@ -742,8 +746,8 @@ class PublishInstanceCardWidget(BaseClickableFrame):
_success_pix = None
_in_progress_pix = None
def __init__(self, instance, icon, publish_finished, parent):
super(PublishInstanceCardWidget, self).__init__(parent)
def __init__(self, instance, icon, publish_can_continue, parent):
super().__init__(parent)
self.setObjectName("CardViewWidget")
@ -756,10 +760,10 @@ class PublishInstanceCardWidget(BaseClickableFrame):
state_pix = self.get_error_pix()
elif instance.warned:
state_pix = self.get_warning_pix()
elif publish_finished:
state_pix = self.get_success_pix()
else:
elif publish_can_continue:
state_pix = self.get_in_progress_pix()
else:
state_pix = self.get_success_pix()
state_label = IconValuePixmapLabel(state_pix, self)
@ -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
@ -927,7 +933,7 @@ class PublishInstancesViewWidget(QtWidgets.QWidget):
+ scroll_bar.sizeHint().width()
)
result = super(PublishInstancesViewWidget, self).sizeHint()
result = super().sizeHint()
result.setWidth(width)
return result
@ -970,11 +976,7 @@ class PublishInstancesViewWidget(QtWidgets.QWidget):
widgets = []
group_widgets = []
publish_finished = (
self._controller.publish_has_crashed
or self._controller.publish_has_validation_errors
or self._controller.publish_has_finished
)
publish_can_continue = self._controller.publish_can_continue()
instances_by_family = collections.defaultdict(list)
for instance_item in instance_items:
if not instance_item.exists:
@ -996,7 +998,10 @@ class PublishInstancesViewWidget(QtWidgets.QWidget):
icon = identifier_icons[instance_item.creator_identifier]
widget = PublishInstanceCardWidget(
instance_item, icon, publish_finished, self._instance_view
instance_item,
icon,
publish_can_continue,
self._instance_view
)
widget.selection_requested.connect(self._on_selection_request)
self._instance_layout.addWidget(widget, 0)
@ -1040,7 +1045,7 @@ class LogIconFrame(QtWidgets.QFrame):
_validation_error_pix = None
def __init__(self, parent, log_type, log_level, is_validation_error):
super(LogIconFrame, self).__init__(parent)
super().__init__(parent)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
@ -1108,7 +1113,7 @@ class LogItemWidget(QtWidgets.QWidget):
}
def __init__(self, log, parent):
super(LogItemWidget, self).__init__(parent)
super().__init__(parent)
type_flag, level_n = self._get_log_info(log)
icon_label = LogIconFrame(
@ -1185,7 +1190,7 @@ class LogsWithIconsView(QtWidgets.QWidget):
"""
def __init__(self, logs, parent):
super(LogsWithIconsView, self).__init__(parent)
super().__init__(parent)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
logs_layout = QtWidgets.QVBoxLayout(self)
@ -1265,7 +1270,7 @@ class InstanceLogsWidget(QtWidgets.QWidget):
"""
def __init__(self, instance, parent):
super(InstanceLogsWidget, self).__init__(parent)
super().__init__(parent)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
@ -1296,7 +1301,7 @@ class InstancesLogsView(QtWidgets.QFrame):
"""Publish instances logs view widget."""
def __init__(self, parent):
super(InstancesLogsView, self).__init__(parent)
super().__init__(parent)
self.setObjectName("InstancesLogsView")
scroll_area = QtWidgets.QScrollArea(self)
@ -1349,16 +1354,16 @@ class InstancesLogsView(QtWidgets.QFrame):
self._plugin_ids_filter = None
def showEvent(self, event):
super(InstancesLogsView, self).showEvent(event)
super().showEvent(event)
self._is_showed = True
self._update_instances()
def hideEvent(self, event):
super(InstancesLogsView, self).hideEvent(event)
super().hideEvent(event)
self._is_showed = False
def closeEvent(self, event):
super(InstancesLogsView, self).closeEvent(event)
super().closeEvent(event)
self._is_showed = False
def _update_instances(self):
@ -1456,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)
@ -1499,20 +1506,20 @@ 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.event_system.emit(
self._controller.emit_event(
"copy_report.request", {}, "report_page")
def _on_save_to_disk_click(self):
self._controller.event_system.emit(
self._controller.emit_event(
"export_report.request", {}, "report_page")
class ErrorDetailsWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(ErrorDetailsWidget, self).__init__(parent)
super().__init__(parent)
inputs_widget = QtWidgets.QWidget(self)
# Error 'Description' input
@ -1624,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)
@ -1709,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 = {}
@ -1744,8 +1753,8 @@ class ReportsWidget(QtWidgets.QWidget):
view = self._instances_view
validation_error_mode = False
if (
not self._controller.publish_has_crashed
and self._controller.publish_has_validation_errors
not self._controller.publish_has_crashed()
and self._controller.publish_has_validation_errors()
):
view = self._validation_error_view
validation_error_mode = True
@ -1755,8 +1764,9 @@ class ReportsWidget(QtWidgets.QWidget):
self._detail_input_scroll.setVisible(validation_error_mode)
self._views_layout.setCurrentWidget(view)
self._crash_widget.setVisible(self._controller.publish_has_crashed)
self._logs_view.setVisible(not self._controller.publish_has_crashed)
is_crashed = self._controller.publish_has_crashed()
self._crash_widget.setVisible(is_crashed)
self._logs_view.setVisible(not is_crashed)
# Instance view & logs update
instance_items = self._get_instance_items()
@ -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)
@ -1832,30 +1844,30 @@ class ReportPageWidget(QtWidgets.QFrame):
layout.addWidget(header_label, 0)
layout.addWidget(publish_instances_widget, 0)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.process.started", self._on_publish_start
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.reset.finished", self._on_publish_reset
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.process.stopped", self._on_publish_stop
)
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:
if not self._controller.publish_has_started():
# This probably never happen when this widget is visible
header_label = "Nothing to report until you run publish"
elif self._controller.publish_has_crashed:
elif self._controller.publish_has_crashed():
header_label = "Publish error report"
elif self._controller.publish_has_validation_errors:
elif self._controller.publish_has_validation_errors():
header_label = "Publish validation report"
elif self._controller.publish_has_finished:
elif self._controller.publish_has_finished():
header_label = "Publish success report"
else:
header_label = "Publish report"
@ -1863,7 +1875,7 @@ class ReportPageWidget(QtWidgets.QFrame):
def _update_state(self):
self._update_label()
publish_started = self._controller.publish_has_started
publish_started = self._controller.publish_has_started()
self._publish_instances_widget.setVisible(publish_started)
if publish_started:
self._publish_instances_widget.update_data()

View file

@ -15,7 +15,7 @@ class ScreenMarquee(QtWidgets.QDialog):
"""
def __init__(self, parent=None):
super(ScreenMarquee, self).__init__(parent=parent)
super().__init__(parent=parent)
self.setWindowFlags(
QtCore.Qt.Window
@ -138,7 +138,7 @@ class ScreenMarquee(QtWidgets.QDialog):
event.accept()
self.close()
return
return super(ScreenMarquee, self).keyPressEvent(event)
return super().keyPressEvent(event)
def showEvent(self, event):
self._fit_screen_geometry()

View file

@ -6,7 +6,7 @@ class PublisherTabBtn(QtWidgets.QPushButton):
tab_clicked = QtCore.Signal(str)
def __init__(self, identifier, label, parent):
super(PublisherTabBtn, self).__init__(label, parent)
super().__init__(label, parent)
self._identifier = identifier
self._active = False
@ -36,7 +36,7 @@ class PublisherTabsWidget(QtWidgets.QFrame):
tab_changed = QtCore.Signal(str, str)
def __init__(self, parent=None):
super(PublisherTabsWidget, self).__init__(parent)
super().__init__(parent)
btns_widget = QtWidgets.QWidget(self)
btns_layout = QtWidgets.QHBoxLayout(btns_widget)

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 = {}
@ -135,7 +143,7 @@ class TasksModel(QtGui.QStandardItemModel):
task_type_items = {
task_type_item.name: task_type_item
for task_type_item in self._controller.get_task_type_items(
self._controller.project_name
self._controller.get_current_project_name()
)
}
icon_name_by_task_name = {}

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
@ -34,7 +37,7 @@ class ThumbnailPainterWidget(QtWidgets.QWidget):
checker_boxes_count = 20
def __init__(self, parent):
super(ThumbnailPainterWidget, self).__init__(parent)
super().__init__(parent)
border_color = get_objected_colors("bg-buttons").get_qcolor()
thumbnail_bg_color = get_objected_colors("bg-view").get_qcolor()
@ -299,10 +302,12 @@ 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)
super().__init__(parent)
self.setAcceptDrops(True)
thumbnail_painter = ThumbnailPainterWidget(self)
@ -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)
@ -570,12 +575,12 @@ class ThumbnailWidget(QtWidgets.QWidget):
)
def resizeEvent(self, event):
super(ThumbnailWidget, self).resizeEvent(event)
super().resizeEvent(event)
self._adapt_to_size()
self._update_buttons_position()
def showEvent(self, event):
super(ThumbnailWidget, self).showEvent(event)
super().showEvent(event)
self._adapt_to_size()
self._update_buttons_position()

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."]
@ -94,7 +95,7 @@ class IconValuePixmapLabel(PublishPixmapLabel):
def __init__(self, icon_def, parent):
source_pixmap = self._parse_icon_def(icon_def)
super(IconValuePixmapLabel, self).__init__(source_pixmap, parent)
super().__init__(source_pixmap, parent)
def set_icon_def(self, icon_def):
"""Set icon by it's definition name.
@ -122,7 +123,7 @@ class ContextWarningLabel(PublishPixmapLabel):
def __init__(self, parent):
pix = get_pixmap("warning")
super(ContextWarningLabel, self).__init__(pix, parent)
super().__init__(pix, parent)
self.setToolTip(
"Contain invalid context. Please check details."
@ -145,7 +146,7 @@ class PublishIconBtn(IconButton):
"""
def __init__(self, pixmap_path, *args, **kwargs):
super(PublishIconBtn, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
colors = get_objected_colors()
icon = self.generate_icon(
@ -208,7 +209,7 @@ class CreateBtn(PublishIconBtn):
def __init__(self, parent=None):
icon_path = get_icon_path("create")
super(CreateBtn, self).__init__(icon_path, "Create", parent)
super().__init__(icon_path, "Create", parent)
self.setToolTip("Create new product/s")
self.setLayoutDirection(QtCore.Qt.RightToLeft)
@ -217,7 +218,7 @@ class SaveBtn(PublishIconBtn):
"""Save context and instances information."""
def __init__(self, parent=None):
icon_path = get_icon_path("save")
super(SaveBtn, self).__init__(icon_path, parent)
super().__init__(icon_path, parent)
self.setToolTip(
"Save changes ({})".format(
QtGui.QKeySequence(QtGui.QKeySequence.Save).toString()
@ -229,7 +230,7 @@ class ResetBtn(PublishIconBtn):
"""Publish reset button."""
def __init__(self, parent=None):
icon_path = get_icon_path("refresh")
super(ResetBtn, self).__init__(icon_path, parent)
super().__init__(icon_path, parent)
self.setToolTip(
"Reset & discard changes ({})".format(ResetKeySequence.toString())
)
@ -239,7 +240,7 @@ class StopBtn(PublishIconBtn):
"""Publish stop button."""
def __init__(self, parent):
icon_path = get_icon_path("stop")
super(StopBtn, self).__init__(icon_path, parent)
super().__init__(icon_path, parent)
self.setToolTip("Stop/Pause publishing")
@ -247,7 +248,7 @@ class ValidateBtn(PublishIconBtn):
"""Publish validate button."""
def __init__(self, parent=None):
icon_path = get_icon_path("validate")
super(ValidateBtn, self).__init__(icon_path, parent)
super().__init__(icon_path, parent)
self.setToolTip("Validate")
@ -255,7 +256,7 @@ class PublishBtn(PublishIconBtn):
"""Publish start publish button."""
def __init__(self, parent=None):
icon_path = get_icon_path("play")
super(PublishBtn, self).__init__(icon_path, "Publish", parent)
super().__init__(icon_path, "Publish", parent)
self.setToolTip("Publish")
@ -263,7 +264,7 @@ class CreateInstanceBtn(PublishIconBtn):
"""Create add button."""
def __init__(self, parent=None):
icon_path = get_icon_path("add")
super(CreateInstanceBtn, self).__init__(icon_path, parent)
super().__init__(icon_path, parent)
self.setToolTip("Create new instance")
@ -274,7 +275,7 @@ class PublishReportBtn(PublishIconBtn):
def __init__(self, parent=None):
icon_path = get_icon_path("view_report")
super(PublishReportBtn, self).__init__(icon_path, parent)
super().__init__(icon_path, parent)
self.setToolTip("Copy report")
self._actions = []
@ -287,7 +288,7 @@ class PublishReportBtn(PublishIconBtn):
self.triggered.emit(identifier)
def mouseReleaseEvent(self, event):
super(PublishReportBtn, self).mouseReleaseEvent(event)
super().mouseReleaseEvent(event)
menu = QtWidgets.QMenu(self)
actions = []
for item in self._actions:
@ -305,7 +306,7 @@ class RemoveInstanceBtn(PublishIconBtn):
"""Create remove button."""
def __init__(self, parent=None):
icon_path = resources.get_icon_path("delete")
super(RemoveInstanceBtn, self).__init__(icon_path, parent)
super().__init__(icon_path, parent)
self.setToolTip("Remove selected instances")
@ -313,7 +314,7 @@ class ChangeViewBtn(PublishIconBtn):
"""Create toggle view button."""
def __init__(self, parent=None):
icon_path = get_icon_path("change_view")
super(ChangeViewBtn, self).__init__(icon_path, parent)
super().__init__(icon_path, parent)
self.setToolTip("Swap between views")
@ -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__))
@ -399,7 +403,7 @@ class ClickableLineEdit(QtWidgets.QLineEdit):
clicked = QtCore.Signal()
def __init__(self, *args, **kwargs):
super(ClickableLineEdit, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.setReadOnly(True)
self._mouse_pressed = False
@ -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
@ -582,7 +588,7 @@ class FoldersFields(BaseClickableFrame):
class TasksComboboxProxy(QtCore.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(TasksComboboxProxy, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self._filter_empty = False
def set_filter_empty(self, filter_empty):
@ -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
@ -892,7 +900,7 @@ class VariantInputWidget(PlaceholderLineEdit):
value_changed = QtCore.Signal()
def __init__(self, parent):
super(VariantInputWidget, self).__init__(parent)
super().__init__(parent)
self.setObjectName("VariantInput")
self.setToolTip(VARIANT_TOOLTIP)
@ -1003,7 +1011,7 @@ class MultipleItemWidget(QtWidgets.QWidget):
"""
def __init__(self, parent):
super(MultipleItemWidget, self).__init__(parent)
super().__init__(parent)
model = QtGui.QStandardItemModel()
@ -1043,7 +1051,7 @@ class MultipleItemWidget(QtWidgets.QWidget):
self.setMaximumHeight(height + (2 * self._view.spacing()))
def showEvent(self, event):
super(MultipleItemWidget, self).showEvent(event)
super().showEvent(event)
tmp_item = None
if not self._value:
# Add temp item to be able calculate maximum height of widget
@ -1055,7 +1063,7 @@ class MultipleItemWidget(QtWidgets.QWidget):
self._model.clear()
def resizeEvent(self, event):
super(MultipleItemWidget, self).resizeEvent(event)
super().resizeEvent(event)
self._update_size()
def set_value(self, value=None):
@ -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)
@ -1734,11 +1750,11 @@ class ProductAttributesWidget(QtWidgets.QWidget):
thumbnail_widget.thumbnail_created.connect(self._on_thumbnail_create)
thumbnail_widget.thumbnail_cleared.connect(self._on_thumbnail_clear)
controller.event_system.add_callback(
controller.register_event_callback(
"instance.thumbnail.changed", self._on_thumbnail_changed
)
self._controller = controller
self._controller: AbstractPublisherFrontend = controller
self._convert_widget = convert_widget
@ -1877,7 +1893,7 @@ class CreateNextPageOverlay(QtWidgets.QWidget):
clicked = QtCore.Signal()
def __init__(self, parent):
super(CreateNextPageOverlay, self).__init__(parent)
super().__init__(parent)
self.setCursor(QtCore.Qt.PointingHandCursor)
self._arrow_color = (
get_objected_colors("font").get_qcolor()
@ -1967,7 +1983,7 @@ class CreateNextPageOverlay(QtWidgets.QWidget):
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self._mouse_pressed = True
super(CreateNextPageOverlay, self).mousePressEvent(event)
super().mousePressEvent(event)
def mouseReleaseEvent(self, event):
if self._mouse_pressed:
@ -1975,7 +1991,7 @@ class CreateNextPageOverlay(QtWidgets.QWidget):
if self.rect().contains(event.pos()):
self.clicked.emit()
super(CreateNextPageOverlay, self).mouseReleaseEvent(event)
super().mouseReleaseEvent(event)
def paintEvent(self, event):
painter = QtGui.QPainter()

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")
@ -273,55 +280,55 @@ class PublisherWindow(QtWidgets.QDialog):
self._on_create_overlay_button_click
)
controller.event_system.add_callback(
controller.register_event_callback(
"instances.refresh.finished", self._on_instances_refresh
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.reset.finished", self._on_publish_reset
)
controller.event_system.add_callback(
controller.register_event_callback(
"controller.reset.finished", self._on_controller_reset
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.process.started", self._on_publish_start
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.has_validated.changed", self._on_publish_validated_change
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.finished.changed", self._on_publish_finished_change
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.process.stopped", self._on_publish_stop
)
controller.event_system.add_callback(
controller.register_event_callback(
"show.card.message", self._on_overlay_message
)
controller.event_system.add_callback(
controller.register_event_callback(
"instances.collection.failed", self._on_creator_error
)
controller.event_system.add_callback(
controller.register_event_callback(
"instances.save.failed", self._on_creator_error
)
controller.event_system.add_callback(
controller.register_event_callback(
"instances.remove.failed", self._on_creator_error
)
controller.event_system.add_callback(
controller.register_event_callback(
"instances.create.failed", self._on_creator_error
)
controller.event_system.add_callback(
controller.register_event_callback(
"convertors.convert.failed", self._on_convertor_error
)
controller.event_system.add_callback(
controller.register_event_callback(
"convertors.find.failed", self._on_convertor_error
)
controller.event_system.add_callback(
controller.register_event_callback(
"publish.action.failed", self._on_action_error
)
controller.event_system.add_callback(
controller.register_event_callback(
"export_report.request", self._export_report
)
controller.event_system.add_callback(
controller.register_event_callback(
"copy_report.request", self._copy_report
)
@ -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):
@ -437,7 +445,7 @@ class PublisherWindow(QtWidgets.QDialog):
def showEvent(self, event):
self._window_is_visible = True
super(PublisherWindow, self).showEvent(event)
super().showEvent(event)
if self._first_show:
self._first_show = False
self._on_first_show()
@ -445,7 +453,7 @@ class PublisherWindow(QtWidgets.QDialog):
self._show_timer.start()
def resizeEvent(self, event):
super(PublisherWindow, self).resizeEvent(event)
super().resizeEvent(event)
self._update_publish_frame_rect()
self._update_create_overlay_size()
@ -453,24 +461,24 @@ class PublisherWindow(QtWidgets.QDialog):
self._window_is_visible = False
self._uninstall_app_event_listener()
# TODO capture changes and ask user if wants to save changes on close
if not self._controller.host_context_has_changed:
if not self._controller.host_context_has_changed():
self._save_changes(False)
self._comment_input.setText("") # clear comment
self._reset_on_show = True
self._controller.clear_thumbnail_temp_dir_path()
# Trigger custom event that should be captured only in UI
# - backend (controller) must not be dependent on this event topic!!!
self._controller.event_system.emit("main.window.closed", {}, "window")
super(PublisherWindow, self).closeEvent(event)
self._controller.emit_event("main.window.closed", {}, "window")
super().closeEvent(event)
def leaveEvent(self, event):
super(PublisherWindow, self).leaveEvent(event)
super().leaveEvent(event)
self._update_create_overlay_visibility()
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.MouseMove:
self._update_create_overlay_visibility(event.globalPos())
return super(PublisherWindow, self).eventFilter(obj, event)
return super().eventFilter(obj, event)
def _install_app_event_listener(self):
if self._app_event_listener_installed:
@ -520,12 +528,12 @@ 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
super(PublisherWindow, self).keyPressEvent(event)
super().keyPressEvent(event)
def _on_overlay_message(self, event):
self._overlay_object.add_message(
@ -574,7 +582,7 @@ class PublisherWindow(QtWidgets.QDialog):
bool: Save can happen.
"""
if not self._controller.host_context_has_changed:
if not self._controller.host_context_has_changed():
return True
title = "Host context changed"
@ -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):
@ -831,7 +839,6 @@ class PublisherWindow(QtWidgets.QDialog):
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_controller_reset(self):
@ -885,24 +892,13 @@ class PublisherWindow(QtWidgets.QDialog):
self._set_publish_overlay_visibility(False)
self._reset_btn.setEnabled(True)
self._stop_btn.setEnabled(False)
publish_has_crashed = self._controller.publish_has_crashed
validate_enabled = not publish_has_crashed
publish_enabled = not publish_has_crashed
if self._is_on_publish_tab():
self._go_to_report_tab()
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
publish_enabled = self._controller.publish_can_continue()
validate_enabled = (
publish_enabled and not self._controller.publish_has_validated()
)
self._validate_btn.setEnabled(validate_enabled)
self._publish_btn.setEnabled(publish_enabled)
@ -912,12 +908,12 @@ class PublisherWindow(QtWidgets.QDialog):
self._update_publish_details_widget()
def _validate_create_instances(self):
if not self._controller.host_is_valid:
if not self._controller.is_host_valid():
self._set_footer_enabled(True)
return
all_valid = None
for instance in self._controller.instances.values():
for instance in self._controller.get_instances():
if not instance["active"]:
continue
@ -933,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()
@ -1091,7 +1087,7 @@ class ErrorsMessageBox(ErrorMessageBox):
self._tabs_widget = None
self._stack_layout = None
super(ErrorsMessageBox, self).__init__(error_title, parent)
super().__init__(error_title, parent)
layout = self.layout()
layout.setContentsMargins(0, 0, 0, 0)

View file

@ -96,7 +96,7 @@ class FoldersQtModel(QtGui.QStandardItemModel):
Union[str, None]: Folder id or None if folder is not available.
"""
for folder_id, item in self._items_by_id.values():
for folder_id, item in self._items_by_id.items():
if item.data(FOLDER_PATH_ROLE) == folder_path:
return folder_id
return None
@ -165,7 +165,7 @@ class FoldersQtModel(QtGui.QStandardItemModel):
folder_items = self._controller.get_folder_items(
project_name, FOLDERS_MODEL_SENDER_NAME
)
folder_type_items = {}
folder_type_items = []
if hasattr(self._controller, "get_folder_type_items"):
folder_type_items = self._controller.get_folder_type_items(
project_name, FOLDERS_MODEL_SENDER_NAME
@ -194,7 +194,7 @@ class FoldersQtModel(QtGui.QStandardItemModel):
return
if thread.failed:
# TODO visualize that refresh failed
folder_items, folder_type_items = {}, {}
folder_items, folder_type_items = {}, []
else:
folder_items, folder_type_items = thread.get_result()
self._fill_items(folder_items, folder_type_items)