Files
blender-portable-repo/scripts/addons/blender_kitsu/props.py
T
2026-03-17 14:58:51 -06:00

950 lines
30 KiB
Python

# SPDX-FileCopyrightText: 2021 Blender Studio Tools Authors
#
# SPDX-License-Identifier: GPL-3.0-or-later
from typing import Any, Union, List, Dict, Optional, Tuple
import bpy
from bpy.app.handlers import persistent
from . import propsdata, bkglobals
from .logger import LoggerFactory
from . import cache
from .types import Sequence
logger = LoggerFactory.getLogger()
class KITSU_sequence_colors(bpy.types.PropertyGroup):
# name: StringProperty() -> Instantiated by default
color: bpy.props.FloatVectorProperty(
name="Sequence Color",
subtype="COLOR",
default=(1.0, 1.0, 1.0),
min=0.0,
max=1.0,
description="Sequence color that will be used as line overlay",
)
class KITSU_property_group_sequence(bpy.types.PropertyGroup):
"""
Property group that will be registered on sequence strips.
They hold metadata that will be used to compose a data structure that can
be pushed to backend.
"""
def _get_shot_description(self):
return self.shot_description
def _get_sequence_entity(self):
try:
return Sequence.by_id(self.sequence_id)
except AttributeError:
return None
manual_shot_name: bpy.props.StringProperty(
name="Shot",
description="Enter a new Shot name to submit to Kitsu Server",
default="",
) # type: ignore
###########
# Shot
###########
shot_id: bpy.props.StringProperty( # type: ignore
name="Shot ID",
description="ID that refers to the strip's shot on server",
default="",
)
def get_shot_via_name(self):
return get_safely_string_prop(self, "shot_name")
def set_shot_via_name(self, input):
seq = self._get_sequence_entity()
if seq is None:
return
# Attempt to set with matching Kitsu Entry
kitsu_set = set_kitsu_entity_id_via_enum_name(
self=self,
input_name=input,
items=cache.get_shots_enum_for_seq(self, bpy.context, seq),
name_prop='shot_name',
id_prop='shot_id',
)
# Set manually so users can submit new shots
if not kitsu_set:
self['shot_name'] = input
return
def get_shot_search_list(self, context, edit_text):
seq = self._get_sequence_entity()
if seq is None:
return []
return get_enum_item_names(cache.get_shots_enum_for_seq(self, bpy.context, seq))
shot_name: bpy.props.StringProperty( # type: ignore
name="Shot",
description="Name that refers to the strip's shot on server, Reminder: press ENTER to save shot name after entering",
default="",
get=get_shot_via_name,
set=set_shot_via_name,
options=set(),
search=get_shot_search_list,
search_options={'SORT', 'SUGGESTION'},
)
shot_description: bpy.props.StringProperty(name="Description", default="", options={"HIDDEN"}) # type: ignore
###########
# Sequence
###########
sequence_id: bpy.props.StringProperty( # type: ignore
name="Seq ID",
description="ID that refers to the active sequence on server",
default="",
)
def get_sequences_via_name(self):
return get_safely_string_prop(self, "sequence_name")
def set_sequences_via_name(self, input):
key = set_kitsu_entity_id_via_enum_name(
self=self,
input_name=input,
items=cache.get_sequences_enum_list(self, bpy.context),
name_prop='sequence_name',
id_prop='sequence_id',
)
return
def get_sequence_search_list(self, context, edit_text):
return get_enum_item_names(cache.get_sequences_enum_list(self, bpy.context))
sequence_name: bpy.props.StringProperty( # type: ignore
name="Sequence",
description="Sequence",
default="",
get=get_sequences_via_name,
set=set_sequences_via_name,
options=set(),
search=get_sequence_search_list,
search_options={'SORT'},
)
# Project.
project_name: bpy.props.StringProperty(name="Project", default="") # type: ignore
project_id: bpy.props.StringProperty(name="Project ID", default="") # type: ignore
# Meta.
initialized: bpy.props.BoolProperty( # type: ignore
name="Initialized", default=False, description="Is Kitsu shot"
)
linked: bpy.props.BoolProperty( # type: ignore
name="Linked", default=False, description="Is linked to an ID on server"
)
# Frame range.
frame_start_offset: bpy.props.IntProperty(name="Frame Start Offset")
# Media.
media_outdated: bpy.props.BoolProperty(
name="Source Media Outdated",
default=False,
description="Indicated if there is a newer version of the source media available",
)
# Display props.
shot_description_display: bpy.props.StringProperty(name="Description", get=_get_shot_description) # type: ignore
def to_dict(self):
return {
"id": self.id,
"name": self.shot,
"sequence_name": self.sequence,
"description": self.description,
}
def clear(self):
self.shot_id = ""
self.shot_name = ""
self.shot_description = ""
self.sequence_id = ""
self.sequence_name = ""
self.project_name = ""
self.project_id = ""
self.initialized = False
self.linked = False
self.frame_start_offset = 0
def unlink(self):
self.sequence_id = ""
self.project_name = ""
self.project_id = ""
self.linked = False
def set_kitsu_entity_id_via_enum_name(
self,
items: List[Tuple[str, str, str]],
input_name: str,
name_prop: str,
id_prop: str,
) -> str:
"""Set the ID and Name of a Kitsu entity by finding the matching ID for a given name
and updating both values in their corrisponding String Properties (Kitsu Context Properties)
Args:
items (List[Tuple[str, str, str]]): Enum items in the format List[Tuple[id, name, description]]
input_name (str): Name, used to find matching ID in a tuple
name_prop (str): Name of String Property where Kitsu Enitity's Name is stored
id_prop (str): Name of String Property where Kitsu Enitity's ID is stored
Returns:
str: ID of Kitsu entity
"""
if input_name == "":
self[id_prop] = ""
self[name_prop] = input_name
return
for key, value, _ in items:
if value == input_name:
self[id_prop] = key
self[name_prop] = input_name
return key
def get_enum_item_names(enum_items: List[Tuple[str, str, str]]) -> List[str]:
"""Return a list of names from a list of enum items used by (Kitsu Context Properties)
Args:
enum_items (List[Tuple[str, str, str]]): Enum items in the format List[Tuple[id, name, description]]
Returns:
List[str]: List of avaliable names
"""
return [item[1] for item in enum_items]
def get_safely_string_prop(self, name: str) -> str:
"""Return Value of String Property, and return "" if value isn't set"""
try:
return self[name]
except KeyError:
return ""
class KITSU_property_group_scene(bpy.types.PropertyGroup):
""""""
################################
# Kitsu Context Properties
################################
"""
Kitsu Context Properties
These are properties used to store/manage the current "Context"
for a Kitsu Entity. Kitsu Entities represents things like Asset,
Shot and Sequence for a given production.
Each Entity has an 'ID' Property which is used internally by the add-on
and a 'Name' Property which is used as part of the user interface. When a user selects
the 'Name' of a Kitsu Entity a custom Set function on the property will also update
the Entity's ID.
NOTE: It would be nice to have searchable enums instead of doing all this work manually.
"""
asset_col: bpy.props.PointerProperty(type=bpy.types.Collection, name="Collection")
###########
# Sequence
###########
sequence_active_id: bpy.props.StringProperty( # type: ignore
name="Active Sequence ID",
description="ID that refers to the active sequence on server",
default="",
)
def get_sequences_via_name(self):
return get_safely_string_prop(self, "sequence_active_name")
def set_sequences_via_name(self, input):
key = set_kitsu_entity_id_via_enum_name(
self=self,
input_name=input,
items=cache.get_sequences_enum_list(self, bpy.context),
name_prop='sequence_active_name',
id_prop='sequence_active_id',
)
if key:
cache.sequence_active_set_by_id(bpy.context, key)
else:
cache.sequence_active_reset_entity()
return
def get_sequence_search_list(self, context, edit_text):
return get_enum_item_names(cache.get_sequences_enum_list(self, bpy.context))
sequence_active_name: bpy.props.StringProperty(
name="Sequence",
description="Sequence",
default="", # type: ignore
get=get_sequences_via_name,
set=set_sequences_via_name,
options=set(),
search=get_sequence_search_list,
search_options={'SORT'},
)
###########
# Episode
###########
episode_active_id: bpy.props.StringProperty( # type: ignore
name="Active Episode ID",
description="ID that refers to the active episode on server",
default="",
)
def get_episode_via_name(self):
return get_safely_string_prop(self, "episode_active_name")
def set_episode_via_name(self, input):
key = set_kitsu_entity_id_via_enum_name(
self=self,
input_name=input,
items=cache.get_episodes_enum_list(self, bpy.context),
name_prop='episode_active_name',
id_prop='episode_active_id',
)
# Clear active shot when sequence changes.
if cache.episode_active_get().id != key:
cache.sequence_active_reset(bpy.context)
cache.asset_type_active_reset(bpy.context)
cache.shot_active_reset(bpy.context)
cache.asset_active_reset(bpy.context)
if key:
cache.episode_active_set_by_id(bpy.context, key)
else:
cache.episode_active_reset_entity()
return
def get_episode_search_list(self, context, edit_text):
return get_enum_item_names(cache.get_episodes_enum_list(self, bpy.context))
episode_active_name: bpy.props.StringProperty(
name="Episode",
description="Selet Active Episode",
default="", # type: ignore
get=get_episode_via_name,
set=set_episode_via_name,
options=set(),
search=get_episode_search_list,
search_options={'SORT'},
)
###########
# Shot
###########
shot_active_id: bpy.props.StringProperty( # type: ignore
name="Active Shot ID",
description="IDthat refers to the active shot on server",
default="",
# update=propsdata.on_shot_change,
)
def get_shot_via_name(self):
return get_safely_string_prop(self, "shot_active_name")
def set_shot_via_name(self, input):
key = set_kitsu_entity_id_via_enum_name(
self=self,
input_name=input,
items=cache.get_shots_enum_for_active_seq(self, bpy.context),
name_prop='shot_active_name',
id_prop='shot_active_id',
)
if key:
cache.shot_active_set_by_id(bpy.context, key)
else:
cache.shot_active_reset_entity()
return
def get_shot_search_list(self, context, edit_text):
return get_enum_item_names(cache.get_shots_enum_for_active_seq(self, bpy.context))
shot_active_name: bpy.props.StringProperty(
name="Shot",
description="Shot",
default="", # type: ignore
get=get_shot_via_name,
set=set_shot_via_name,
options=set(),
search=get_shot_search_list,
search_options={'SORT'},
)
###########
# Asset
###########
asset_active_id: bpy.props.StringProperty( # type: ignore
name="Active Asset ID",
description="ID that refers to the active asset on server",
default="",
)
def get_asset_via_name(self):
return get_safely_string_prop(self, "asset_active_name")
def set_asset_via_name(self, input):
key = set_kitsu_entity_id_via_enum_name(
self=self,
input_name=input,
items=cache.get_assets_enum_for_active_asset_type(self, bpy.context),
name_prop='asset_active_name',
id_prop='asset_active_id',
)
if key:
cache.asset_active_set_by_id(bpy.context, key) # TODO
else:
cache.asset_active_reset_entity()
return
def get_asset_type_search_list(self, context, edit_text):
return get_enum_item_names(cache.get_assets_enum_for_active_asset_type(self, bpy.context))
asset_active_name: bpy.props.StringProperty(
name="Asset",
description="Active Asset",
default="", # type: ignore
get=get_asset_via_name,
set=set_asset_via_name,
options=set(),
search=get_asset_type_search_list,
search_options={'SORT'},
)
############
# Asset Type
############
asset_type_active_id: bpy.props.StringProperty( # type: ignore
name="Active Asset Type ID",
description="ID that refers to the active asset type on server",
default="",
)
def get_asset_type_via_name(self):
return get_safely_string_prop(self, "asset_type_active_name")
def set_asset_type_via_name(self, input):
key = set_kitsu_entity_id_via_enum_name(
self=self,
input_name=input,
items=cache.get_assetypes_enum_list(self, bpy.context),
name_prop='asset_type_active_name',
id_prop='asset_type_active_id',
)
if key:
cache.asset_type_active_set_by_id(bpy.context, key)
else:
cache.asset_type_active_reset_entity()
return
def get_asset_type_search_list(self, context, edit_text):
return get_enum_item_names(cache.get_assetypes_enum_list(self, bpy.context))
asset_type_active_name: bpy.props.StringProperty(
name="Asset Type",
description="Active Asset Type Name",
default="", # type: ignore
get=get_asset_type_via_name,
set=set_asset_type_via_name,
options=set(),
search=get_asset_type_search_list,
search_options={'SORT'},
)
############
# Task Type
############
task_type_active_id: bpy.props.StringProperty( # type: ignore
name="Active Task Type ID",
description="ID that refers to the active task type on server",
default="",
)
def get_task_type_via_name(self):
return get_safely_string_prop(self, "task_type_active_name")
def set_task_type_via_name(self, input):
key = set_kitsu_entity_id_via_enum_name(
self=self,
input_name=input,
items=cache.get_task_types_enum_for_current_context(self, bpy.context),
name_prop='task_type_active_name',
id_prop='task_type_active_id',
)
if key:
cache.task_type_active_set_by_id(bpy.context, key)
else:
cache.task_type_active_reset_entity()
return
def get_task_type_search_list(self, context, edit_text):
return get_enum_item_names(cache.get_task_types_enum_for_current_context(self, bpy.context))
task_type_active_name: bpy.props.StringProperty(
name="Task Type",
description="Active Task Type Name",
default="", # type: ignore
get=get_task_type_via_name,
set=set_task_type_via_name,
options=set(),
search=get_task_type_search_list,
search_options={'SORT'},
)
category: bpy.props.EnumProperty( # type: ignore
name="Type",
description="Kitsu entity type",
items=(
("ASSET", "Asset", "Asset related tasks", 0),
("SHOT", "Shot", "Shot related tasks", 1),
("SEQ", "Sequence", "Sequence related tasks", 2),
("EDIT", "Edit", "Edit related tasks", 3),
),
default="SHOT",
update=propsdata.reset_all_kitsu_props,
)
############
# Edit
############
edit_active_id: bpy.props.StringProperty( # type: ignore
name="Active Edit ID",
description="ID that refers to the active edit on server",
default="",
)
def get_edit_via_name(self):
return get_safely_string_prop(self, "edit_active_name")
def set_edit_via_name(self, input):
key = set_kitsu_entity_id_via_enum_name(
self=self,
input_name=input,
items=cache.get_all_edits_enum_for_active_project(self, bpy.context),
name_prop='edit_active_name',
id_prop='edit_active_id',
)
if key:
cache.edit_active_set_by_id(bpy.context, key)
else:
cache.edit_active_reset_entity()
return
def get_edit_search_list(self, context, edit_text):
return get_enum_item_names(cache.get_all_edits_enum_for_active_project(self, bpy.context))
edit_active_name: bpy.props.StringProperty(
name="Edit",
description="Active Edit Name",
default="", # type: ignore
get=get_edit_via_name,
set=set_edit_via_name,
options=set(),
search=get_edit_search_list,
search_options={'SORT'},
)
edit_export_version: bpy.props.StringProperty(name="Version", default="v001")
edit_export_file: bpy.props.StringProperty( # type: ignore
name="Edit Export Filepath",
description="Output filepath of Edit Export",
default="",
subtype="FILE_PATH",
get=propsdata.get_edit_export_file,
)
# Thumbnail props.
task_type_thumbnail_id: bpy.props.StringProperty( # type: ignore
name="Thumbnail Task Type ID",
description="ID that refers to the task type on server for which thumbnails will be uploaded",
default="",
)
task_type_thumbnail_name: bpy.props.StringProperty( # type: ignore
name="Thumbnail Task Type Name",
description="Name that refers to the task type on server for which thumbnails will be uploaded",
default="",
)
# Sqe render props.
task_type_sqe_render_id: bpy.props.StringProperty( # type: ignore
name="Sqe Render Task Type ID",
description="ID that refers to the task type on server for which the sqe render will be uploaded",
default="",
)
task_type_sqe_render_name: bpy.props.StringProperty( # type: ignore
name="Sqe Render Task Type Name",
description="Name that refers to the task type on server for which the sqe render will be uploaded",
default="",
)
# Playblast props.
playblast_version: bpy.props.StringProperty(name="Version", default="v001")
playblast_dir: bpy.props.StringProperty( # type: ignore
name="Playblast Directory",
description="Directory in which playblasts will be saved",
default="",
subtype="DIR_PATH",
get=propsdata.get_playblast_dir,
)
playblast_file: bpy.props.StringProperty( # type: ignore
name="Playblast Filepath",
description="Output filepath of playblast",
default="",
subtype="FILE_PATH",
get=propsdata.get_playblast_file,
)
playblast_task_status_id: bpy.props.StringProperty( # type: ignore
name="Plablast Task Status ID",
description="ID that refers to the task status on server which the playblast will set",
default="",
)
playblast_render_mode: bpy.props.EnumProperty( # type: ignore
name="Playblast Render Mode",
description="Choose to either Render Playblast from current Viewport or use scene's render settings",
items=[
(
"SCENE",
"Scene",
"Render using the scene's render settings, playblast will match 'Render Animation's' output exactly",
),
(
"VIEWPORT",
"Viewport",
"Render from the current viewport, with viewport shading settings and viewport overlays; what you see is what you get",
),
(
"VIEWPORT_PRESET",
"Viewport with Preset Shading",
"Render from the current viewport with Add-On's default shading settings, and automatically hide all overlays",
),
],
)
# Sequence editor tools.
pull_edit_channel: bpy.props.IntProperty(
name="Channel",
description="On which channel the operator will create the color strips",
default=1,
min=1,
max=32,
)
sequence_colors: bpy.props.CollectionProperty(type=KITSU_sequence_colors)
class KITSU_property_group_error(bpy.types.PropertyGroup):
""""""
frame_range: bpy.props.BoolProperty( # type: ignore
name="Frame Range Error",
description="Indicates if the scene frame range does not match the one in Kitsu",
default=False,
)
class KITSU_property_group_window_manager(bpy.types.PropertyGroup):
""""""
tasks_index: bpy.props.IntProperty(name="Tasks Index", default=0)
quick_duplicate_amount: bpy.props.IntProperty(
name="Amount",
default=1,
min=1,
description="Specifies the number of copies that will be created",
)
def clear(self):
pass
def _add_window_manager_props():
# Multi Edit Properties.
bpy.types.WindowManager.show_advanced = bpy.props.BoolProperty(
name="Show Advanced",
description="Shows advanced options to fine control shot pattern",
default=False,
)
bpy.types.WindowManager.var_use_custom_seq = bpy.props.BoolProperty(
name="Use Custom",
description="Enables to type in custom sequence name for <Sequence> wildcard",
default=False,
)
bpy.types.WindowManager.var_use_custom_project = bpy.props.BoolProperty(
name="Use Custom",
description="Enables to type in custom project name for <Project> wildcard",
default=False,
)
bpy.types.WindowManager.var_sequence_custom = bpy.props.StringProperty( # type: ignore
name="Custom Sequence Variable",
description="Value that will be used to insert in <Sequence> wildcard if custom sequence is enabled",
default="",
)
bpy.types.WindowManager.var_project_custom = bpy.props.StringProperty( # type: ignore
name="Custom Project Variable",
description="Value that will be used to insert in <Project> wildcard if custom project is enabled",
default="",
)
bpy.types.WindowManager.shot_counter_start = bpy.props.IntProperty(
description="Value that defines where the shot counter starts",
step=10,
min=0,
)
bpy.types.WindowManager.shot_preview = bpy.props.StringProperty(
name="Shot Pattern",
description="Preview result of current settings on how a shot will be named",
get=propsdata._gen_shot_preview,
)
bpy.types.WindowManager.var_project_active = bpy.props.StringProperty(
name="Active Project",
description="Value that will be inserted in <Project> wildcard",
get=propsdata._get_project_active,
)
###########
# Sequence
###########
bpy.types.WindowManager.selected_sequence_id = bpy.props.StringProperty( # type: ignore
name="Active Sequence ID",
description="ID that refers to the active sequence on server",
default="",
)
def get_sequences_via_name(self):
return get_safely_string_prop(self, "selected_sequence_name")
def set_sequences_via_name(self, input):
key = set_kitsu_entity_id_via_enum_name(
self=self,
input_name=input,
items=cache.get_sequences_enum_list(self, bpy.context),
name_prop='selected_sequence_name',
id_prop='selected_sequence_id',
)
if key:
cache.sequence_active_set_by_id(bpy.context, key)
else:
cache.sequence_active_reset_entity()
return
def get_sequence_search_list(self, context, edit_text):
return get_enum_item_names(cache.get_sequences_enum_list(self, bpy.context))
bpy.types.WindowManager.selected_sequence_name = bpy.props.StringProperty(
name="Sequence",
description="Name of Sequence the generated Shots will be assinged to",
default="", # type: ignore
get=get_sequences_via_name,
set=set_sequences_via_name,
options=set(),
search=get_sequence_search_list,
search_options={'SORT'},
)
# Advanced delete props.
bpy.types.WindowManager.advanced_delete = bpy.props.BoolProperty(
name="Advanced Delete",
description="Checkbox to show advanced shot deletion operations",
default=False,
)
def _clear_window_manager_props():
del bpy.types.WindowManager.show_advanced
del bpy.types.WindowManager.var_use_custom_seq
del bpy.types.WindowManager.var_use_custom_project
del bpy.types.WindowManager.var_sequence_custom
del bpy.types.WindowManager.var_project_custom
del bpy.types.WindowManager.shot_counter_start
del bpy.types.WindowManager.shot_preview
del bpy.types.WindowManager.var_project_active
del bpy.types.WindowManager.selected_sequence_id
del bpy.types.WindowManager.selected_sequence_name
def _calc_kitsu_3d_start(self):
"""
Calculates strip.kitsu_3d_start, little hack because it seems like we cant access the strip from a property group
But we need acess to seqeuence properties.
"""
return int(self.frame_final_start - self.frame_start + bkglobals.FRAME_START)
def _calc_kitsu_frame_end(self):
"""
Calculates strip.kitsu_frame_end, little hack because it seems like we cant access the strip from a property group
But we need acess to seqeuence properties.
"""
frame_start = _calc_kitsu_3d_start(self)
frame_end_final = frame_start + (self.frame_final_duration - 1)
return int(frame_end_final)
def _get_frame_final_duration(self):
return self.frame_final_duration
def _get_strip_filepath(self):
return self.filepath
@persistent
def update_sequence_colors_coll_prop(dummy: Any) -> None:
sqe = bpy.context.scene.sequence_editor
if not sqe:
return
sequences = sqe.sequences_all
sequence_colors = bpy.context.scene.kitsu.sequence_colors
existings_seq_ids: List[str] = []
# Append missing sequences to scene.kitsu.seqeuence_colors.
for seq in sequences:
if not seq.kitsu.sequence_id:
continue
if seq.kitsu.sequence_id not in sequence_colors.keys():
logger.info("Added %s to scene.kitsu.seqeuence_colors", seq.kitsu.sequence_name)
item = sequence_colors.add()
item.name = seq.kitsu.sequence_id
existings_seq_ids.append(seq.kitsu.sequence_id)
# Delete sequence colors that are not in edit anymore.
existings_seq_ids = set(existings_seq_ids)
to_be_removed = [seq_id for seq_id in sequence_colors.keys() if seq_id not in existings_seq_ids]
for seq_id in to_be_removed:
idx = sequence_colors.find(seq_id)
if idx == -1:
continue
sequence_colors.remove(idx)
logger.info(
"Removed %s from scene.kitsu.seqeuence_colors. Is not used in the sequence editor anymore",
seq_id,
)
# ----------------REGISTER--------------.
classes = [
KITSU_sequence_colors,
KITSU_property_group_sequence,
KITSU_property_group_scene,
KITSU_property_group_error,
KITSU_property_group_window_manager,
]
def register():
for cls in classes:
bpy.utils.register_class(cls)
# FRAME RANGE PROPERTIES
# because we cant acess strip properties from a sequence group we need to create this properties
# directly on the strip, as we need strip properties to calculate
bpy.types.Strip.kitsu_3d_start = bpy.props.IntProperty(
name="3D In",
get=_calc_kitsu_3d_start,
)
bpy.types.Strip.kitsu_frame_end = bpy.props.IntProperty(
name="3D Out",
get=_calc_kitsu_frame_end,
)
bpy.types.Strip.kitsu_frame_duration = bpy.props.IntProperty(
name="Duration",
get=_get_frame_final_duration,
)
# Used in general tools panel next to sqe_change_strip_source operator.
bpy.types.MovieStrip.filepath_display = bpy.props.StringProperty(
name="Filepath Display", get=_get_strip_filepath
)
# Sequence Properties.
bpy.types.Strip.kitsu = bpy.props.PointerProperty(
name="Kitsu",
type=KITSU_property_group_sequence,
description="Metadata that is required for blender_kitsu",
)
# Scene Properties.
bpy.types.Scene.kitsu = bpy.props.PointerProperty(
name="Kitsu",
type=KITSU_property_group_scene,
description="Metadata that is required for blender_kitsu",
)
# Window Manager.
bpy.types.WindowManager.kitsu = bpy.props.PointerProperty(
name="Kitsu",
type=KITSU_property_group_window_manager,
description="Metadata that is required for blender_kitsu",
)
# Error Properties.
bpy.types.Scene.kitsu_error = bpy.props.PointerProperty(
name="Kitsu Error",
type=KITSU_property_group_error,
description="Error property group",
)
# Window Manager Properties.
_add_window_manager_props()
# Handlers.
bpy.app.handlers.load_post.append(update_sequence_colors_coll_prop)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
# Clear properties.
_clear_window_manager_props()
# Handlers.
bpy.app.handlers.load_post.remove(update_sequence_colors_coll_prop)