Source code for nanome.api.schemas.ui_schemas

from marshmallow import fields, Schema, post_load, pre_dump

from nanome.util import enums, Color, Vector3
from nanome.api import ui
from .util_schemas import EnumField


[docs]def init_object(obj, data: dict): for key in data: if not hasattr(obj, key): continue try: setattr(obj, key, data[key]) except AttributeError: raise AttributeError('Could not set attribute {}'.format(key))
[docs]class FloatRoundedField(fields.Float): """If decimal part of float is 0, round to int.""" def _serialize(self, value, attr, obj, **kwargs): if value % 1 == 0: return int(value) else: return round(value, 5)
[docs]class PositionSchema(Schema): x = FloatRoundedField() y = FloatRoundedField() z = FloatRoundedField()
[docs]class Vector3Schema(Schema): x = FloatRoundedField() y = FloatRoundedField() z = FloatRoundedField()
[docs] @post_load def make_vector3(self, data, **kwargs): new_obj = Vector3(**data) return new_obj
[docs]class ColorField(fields.Field): def _serialize(self, value: Color, attr, obj, **kwargs): return value._color def _deserialize(self, value, attr, data, **kwargs): return Color.from_int(value)
[docs]def create_multi_state_schema(field_class): """Create a schema that can serialize a MultiStateVariable the provided type.""" return Schema.from_dict({ 'idle': field_class(), 'selected': field_class(), 'highlighted': field_class(), 'selected_highlighted': field_class(), 'unusable': field_class(), 'default': field_class(), })()
[docs]class ButtonTextSchema(Schema): value = create_multi_state_schema(fields.String) bold = fields.Boolean() color = ColorField() active = fields.Boolean() auto_size = fields.Boolean() min_size = fields.Float() max_size = fields.Float() size = fields.Float() underlined = fields.Boolean() ellipsis = fields.Boolean() padding_top = fields.Float() padding_bottom = fields.Float() padding_left = fields.Float() padding_right = fields.Float() line_spacing = fields.Float() vertical_align = EnumField(enum=enums.VertAlignOptions) horizontal_align = EnumField(enum=enums.HorizAlignOptions)
[docs]class ButtonIconSchema(Schema): value = create_multi_state_schema(fields.String) color = create_multi_state_schema(ColorField) active = fields.Boolean() sharpness = fields.Float(min=0, max=1) size = fields.Boolean() ratio = fields.Float(min=0, max=1) position = fields.Nested(PositionSchema) rotation = fields.Nested(PositionSchema) min_size = fields.Float() max_size = fields.Float() size = fields.Float() padding_top = fields.Float() padding_bottom = fields.Float() padding_left = fields.Float() padding_right = fields.Float() vertical_align = EnumField(enum=enums.VertAlignOptions) horizontal_align = EnumField(enum=enums.HorizAlignOptions)
[docs]class ButtonMeshSchema(Schema): color = ColorField() enabled = create_multi_state_schema(fields.Boolean) active = fields.Boolean()
[docs]class ButtonOutlineSchema(Schema): size = create_multi_state_schema(FloatRoundedField) color = ColorField() active = fields.Boolean()
[docs]class ButtonSwitchSchema(Schema): active = fields.Boolean() on_color = ColorField() off_color = ColorField()
[docs]class ButtonToolTipSchema(Schema): title = fields.String() content = fields.String() bounds = fields.Nested(Vector3Schema()) positioning_target = EnumField(enum=enums.ToolTipPositioning) positioning_origin = EnumField(enum=enums.ToolTipPositioning)
[docs]class ButtonSchema(Schema): type_name = fields.String() name = fields.String() selected = fields.Bool() unusable = fields.Bool() # ButtonText text_active = fields.Bool() text_value_idle = fields.String() text_value_selected = fields.String() text_value_highlighted = fields.String() text_value_selected_highlighted = fields.String() text_value_unusable = fields.String() text_bold_idle = fields.Bool() text_bold_selected = fields.Bool() text_bold_highlighted = fields.Bool() text_bold_selected_highlighted = fields.Bool() text_bold_unusable = fields.Bool() text_color_idle = ColorField() text_color_selected = ColorField() text_color_highlighted = ColorField() text_color_selected_highlighted = ColorField() text_color_unusable = ColorField() text_auto_size = fields.Bool() text_min_size = FloatRoundedField() text_max_size = FloatRoundedField() text_size = FloatRoundedField() text_underlined = fields.Bool() text_vertical_align = EnumField(enum=enums.VertAlignOptions) text_horizontal_align = EnumField(enum=enums.HorizAlignOptions) text_ellipsis = fields.Boolean() text_padding_top = FloatRoundedField() text_padding_bottom = FloatRoundedField() text_padding_left = FloatRoundedField() text_padding_right = FloatRoundedField() text_line_spacing = FloatRoundedField() # Icon fields icon_active = fields.Bool() icon_value_idle = fields.Str() icon_value_selected = fields.Str() icon_value_highlighted = fields.Str() icon_value_selected_highlighted = fields.Str() icon_value_unusable = fields.Str() icon_color_idle = ColorField() icon_color_selected = ColorField() icon_color_highlighted = ColorField() icon_color_selected_highlighted = ColorField() icon_color_unusable = ColorField() icon_sharpness = FloatRoundedField() icon_size = FloatRoundedField() icon_ratio = FloatRoundedField() icon_position = fields.Nested(PositionSchema) icon_rotation = fields.Nested(PositionSchema) # Mesh Fields mesh_active = fields.Boolean() mesh_enabled_idle = fields.Boolean() mesh_enabled_selected = fields.Boolean() mesh_enabled_highlighted = fields.Boolean() mesh_enabled_selected_highlighted = fields.Boolean() mesh_enabled_unusable = fields.Boolean() mesh_color_idle = ColorField() mesh_color_selected = ColorField() mesh_color_highlighted = ColorField() mesh_color_selected_highlighted = ColorField() mesh_color_unusable = ColorField() # Outline fields outline_active = fields.Boolean() outline_size_idle = FloatRoundedField() outline_size_selected = FloatRoundedField() outline_size_highlighted = FloatRoundedField() outline_size_selected_highlighted = FloatRoundedField() outline_size_unusable = FloatRoundedField() outline_color_idle = ColorField() outline_color_selected = ColorField() outline_color_highlighted = ColorField() outline_color_selected_highlighted = ColorField() outline_color_unusable = ColorField() # Tooltip fields tooltip_title = fields.String() tooltip_content = fields.String() tooltip_bounds = fields.Nested(PositionSchema) tooltip_positioning_target = EnumField(enum=enums.ToolTipPositioning) tooltip_positioning_origin = EnumField(enum=enums.ToolTipPositioning) # Switch fields disable_on_press = fields.Bool() toggle_on_press = fields.Bool() switch_active = fields.Bool() switch_on_color = ColorField() switch_off_color = ColorField() # backwards compatibility text_bolded = fields.Bool()
[docs] @pre_dump def flatten_attributes(self, obj, *args, **kwargs): """Add flattened attributes from nested fields to object.""" key_map = { # Switch fields 'switch_active': obj.switch.active, 'switch_on_color': obj.switch.on_color, 'switch_off_color': obj.switch.off_color, # icon fields 'icon_color_idle': obj.icon.color.idle, 'icon_color_selected': obj.icon.color.selected, 'icon_color_highlighted': obj.icon.color.highlighted, 'icon_color_selected_highlighted': obj.icon.color.selected_highlighted, 'icon_color_unusable': obj.icon.color.unusable, 'icon_sharpness': obj.icon.sharpness, 'icon_active': obj.icon.active, 'icon_size': obj.icon.size, 'icon_ratio': obj.icon.ratio, 'icon_position': obj.icon.position, 'icon_rotation': obj.icon.rotation, 'icon_value_idle': obj.icon.value.idle, 'icon_value_selected': obj.icon.value.selected, 'icon_value_highlighted': obj.icon.value.highlighted, 'icon_value_selected_highlighted': obj.icon.value.selected_highlighted, 'icon_value_unusable': obj.icon.value.unusable, # Outline fields. 'outline_active': obj.outline.active, 'outline_size_idle': obj.outline.size.idle, 'outline_size_selected': obj.outline.size.selected, 'outline_size_highlighted': obj.outline.size.highlighted, 'outline_size_selected_highlighted': obj.outline.size.selected_highlighted, 'outline_size_unusable': obj.outline.size.unusable, 'outline_color_idle': obj.outline.color.idle, 'outline_color_selected': obj.outline.color.selected, 'outline_color_highlighted': obj.outline.color.highlighted, 'outline_color_selected_highlighted': obj.outline.color.selected_highlighted, 'outline_color_unusable': obj.outline.color.unusable, # ButtonText fields 'text_value_idle': obj.text.value.idle, 'text_value_highlighted': obj.text.value.highlighted, 'text_value_selected': obj.text.value.selected, 'text_value_unusable': obj.text.value.unusable, 'text_value_selected_highlighted': obj.text.value.selected_highlighted, 'text_bold_idle': obj.text.bold.idle, 'text_bold_highlighted': obj.text.bold.highlighted, 'text_bold_selected': obj.text.bold.selected, 'text_bold_selected_highlighted': obj.text.bold.selected_highlighted, 'text_bold_unusable': obj.text.bold.unusable, 'text_color_idle': obj.text.color.idle, 'text_color_highlighted': obj.text.color.highlighted, 'text_color_selected': obj.text.color.selected, 'text_color_selected_highlighted': obj.text.color.selected_highlighted, 'text_color_unusable': obj.text.color.unusable, 'text_min_size': obj.text.min_size, 'text_max_size': obj.text.max_size, 'text_size': obj.text.size, 'text_underlined': obj.text.underlined, 'text_vertical_align': obj.text.vertical_align, 'text_horizontal_align': obj.text.horizontal_align, 'text_ellipsis': obj.text.ellipsis, 'text_padding_top': obj.text.padding_top, 'text_padding_bottom': obj.text.padding_bottom, 'text_padding_left': obj.text.padding_left, 'text_padding_right': obj.text.padding_right, 'text_line_spacing': obj.text.line_spacing, # Mesh 'mesh_active': obj.mesh.active, 'mesh_enabled_idle': obj.mesh.enabled.idle, 'mesh_enabled_selected': obj.mesh.enabled.selected, 'mesh_enabled_highlighted': obj.mesh.enabled.highlighted, 'mesh_enabled_selected_highlighted': obj.mesh.enabled.selected_highlighted, 'mesh_enabled_unusable': obj.mesh.enabled.unusable, 'mesh_color_idle': obj.mesh.color.idle, 'mesh_color_selected': obj.mesh.color.selected, 'mesh_color_highlighted': obj.mesh.color.highlighted, 'mesh_color_selected_highlighted': obj.mesh.color.selected_highlighted, 'mesh_color_unusable': obj.mesh.color.unusable, # Tooltip 'tooltip_title': obj.tooltip.title, 'tooltip_content': obj.tooltip.content, 'tooltip_bounds': obj.tooltip.bounds, 'tooltip_positioning_target': obj.tooltip.positioning_target, 'tooltip_positioning_origin': obj.tooltip.positioning_origin, } for key, value in key_map.items(): setattr(obj, key, value) return obj
[docs] def load(self, data, *args, **kwargs): data = dict(data) btn = super().load(data, *args, **kwargs) self.load_text_values(data, btn) self.load_outline_values(data, btn) self.load_icon_values(data, btn) self.load_mesh_values(data, btn) self.load_tooltip_values(data, btn) return btn
[docs] def load_text_values(self, data: dict, btn: ui.Button): text_values = { 'idle': data.pop('text_value_idle'), 'selected': data.pop('text_value_selected'), 'highlighted': data.pop('text_value_highlighted'), 'selected_highlighted': data.pop('text_value_selected_highlighted'), 'unusable': data.pop('text_value_unusable'), } if any(key.startswith('text_color') for key in data.keys()): text_color = { 'idle': data.pop('text_color_idle'), 'selected': data.pop('text_color_selected'), 'highlighted': data.pop('text_color_highlighted'), 'selected_highlighted': data.pop('text_color_selected_highlighted'), 'unusable': data.pop('text_color_unusable'), } color_schema = create_multi_state_schema(ColorField) colors_data = color_schema.load(text_color) btn.text.color.set_each(**colors_data) if any(key.startswith('text_bold_') for key in data.keys()): text_bold = { 'idle': data.pop('text_bold_idle'), 'selected': data.pop('text_bold_selected'), 'highlighted': data.pop('text_bold_highlighted'), 'selected_highlighted': data.pop('text_bold_selected_highlighted'), 'unusable': data.pop('text_bold_unusable'), } bold_schema = create_multi_state_schema(fields.Bool) bolds_data = bold_schema.load(text_bold) btn.text.bold.set_each(**bolds_data) string_schema = create_multi_state_schema(fields.String) values_data = string_schema.load(text_values) btn.text.value.set_all("") # Makes default empty string. btn.text.value.set_each(**values_data)
[docs] def load_outline_values(self, data, btn): if any(key.startswith('outline') for key in data.keys()): outline_data = { 'active': data.pop('outline_active'), 'size': { 'idle': data.pop('outline_size_idle'), 'selected': data.pop('outline_size_selected'), 'highlighted': data.pop('outline_size_highlighted'), 'selected_highlighted': data.pop('outline_size_selected_highlighted'), 'unusable': data.pop('outline_size_unusable'), }, 'color': { 'idle': data.pop('outline_color_idle'), 'selected': data.pop('outline_color_selected'), 'highlighted': data.pop('outline_color_highlighted'), 'selected_highlighted': data.pop('outline_color_selected_highlighted'), 'unusable': data.pop('outline_color_unusable'), } } multi_state_color = create_multi_state_schema(ColorField).load(outline_data['color']) multi_state_size = create_multi_state_schema(FloatRoundedField).load(outline_data['size']) btn.outline.color.set_each(**multi_state_color) btn.outline.size.set_each(**multi_state_size)
[docs] def load_icon_values(self, data, btn): has_icon_data = any(key.startswith('icon') for key in data.keys()) if has_icon_data: icon_data = { 'active': data.pop('icon_active'), 'sharpness': data.pop('icon_sharpness'), 'size': data.pop('icon_size'), 'ratio': data.pop('icon_ratio'), 'position': data.pop('icon_position'), 'rotation': data.pop('icon_rotation'), 'padding_left': data.pop('icon_padding_left', 0), 'padding_right': data.pop('icon_padding_right', 0), 'padding_top': data.pop('icon_padding_top', 0), 'padding_bottom': data.pop('icon_padding_bottom', 0), } icon_color = { 'idle': data.pop('icon_color_idle'), 'selected': data.pop('icon_color_selected'), 'highlighted': data.pop('icon_color_highlighted'), 'selected_highlighted': data.pop('icon_color_selected_highlighted'), 'unusable': data.pop('icon_color_unusable') } # Icon values aren't present in json exported directly from StackStudio if any(key.startswith('icon_value') for key in data.keys()): icon_values = { 'idle': data.pop('icon_value_idle'), 'selected': data.pop('icon_value_selected'), 'highlighted': data.pop('icon_value_highlighted'), 'selected_highlighted': data.pop('icon_value_selected_highlighted'), 'unusable': data.pop('icon_value_unusable') } loaded_icon_values = create_multi_state_schema(fields.Str).load(icon_values) btn.icon.value.set_each(**loaded_icon_values) validated_icon_data = ButtonIconSchema().load(icon_data) for key, value in validated_icon_data.items(): setattr(btn.icon, key, value) loaded_icon_color = create_multi_state_schema(ColorField).load(icon_color) btn.icon.color.set_each(**loaded_icon_color)
[docs] def load_mesh_values(self, data, btn): has_mesh_data = any(key.startswith('mesh') for key in data.keys()) if has_mesh_data: mesh_data = { 'active': data.pop('mesh_active'), } mesh_enabled = { 'idle': data.pop('mesh_enabled_idle'), 'selected': data.pop('mesh_enabled_selected'), 'highlighted': data.pop('mesh_enabled_highlighted'), 'selected_highlighted': data.pop('mesh_enabled_selected_highlighted'), 'unusable': data.pop('mesh_enabled_unusable'), } mesh_color = { 'idle': data.pop('mesh_color_idle'), 'selected': data.pop('mesh_color_selected'), 'highlighted': data.pop('mesh_color_highlighted'), 'selected_highlighted': data.pop('mesh_color_selected_highlighted'), 'unusable': data.pop('mesh_color_unusable'), } for key, value in mesh_data.items(): setattr(btn.mesh, key, value) multi_state_color = create_multi_state_schema(ColorField).load(mesh_color) multi_state_enabled = create_multi_state_schema(fields.Boolean).load(mesh_enabled) btn.mesh.color.set_each(**multi_state_color) btn.mesh.enabled.set_each(**multi_state_enabled)
[docs] def load_tooltip_values(self, data, btn): has_tooltip_data = any(key.startswith('tooltip') for key in data.keys()) if has_tooltip_data: tooltip_data = { 'title': data.pop('tooltip_title'), 'content': data.pop('tooltip_content'), 'bounds': data.pop('tooltip_bounds'), 'positioning_target': data.pop('tooltip_positioning_target'), 'positioning_origin': data.pop('tooltip_positioning_origin'), } validated_data = ButtonToolTipSchema().load(tooltip_data) for key, value in validated_data.items(): setattr(btn.tooltip, key, value)
[docs] def load_switch_values(self, data, btn): has_switch_data = any(key.startswith('switch') for key in data.keys()) if has_switch_data: switch_data = { 'active': data.pop('switch_active'), 'on_color': data.pop('switch_on_color'), 'off_color': data.pop('switch_off_color'), } validated_data = ButtonSwitchSchema().load(switch_data) for key, value in validated_data.items(): setattr(btn.switch, key, value)
[docs] @post_load def make_obj(self, data, **kwargs): new_obj = ui.Button() for key in data: try: setattr(new_obj, key, data[key]) except AttributeError: raise AttributeError('Could not set attribute {}'.format(key)) return new_obj
[docs]class MeshSchema(Schema): type_name = fields.String() mesh_color = ColorField()
[docs] @post_load def make_obj(self, data, **kwargs): new_obj = ui.Mesh() init_object(new_obj, data) return new_obj
[docs]class ImageSchema(Schema): type_name = fields.String() color = ColorField() file_path = fields.String() scaling_option = EnumField(enum=enums.ScalingOptions)
[docs] @post_load def make_obj(self, data, **kwargs): new_obj = ui.Image() init_object(new_obj, data) return new_obj
[docs]class LoadingBarSchema(Schema): type_name = fields.String() percentage = FloatRoundedField() title = fields.Str() description = fields.Str() failure = fields.Bool()
[docs] @post_load def make_obj(self, data, **kwargs): new_obj = ui.LoadingBar() init_object(new_obj, data) return new_obj
[docs]class LabelSchema(Schema): type_name = fields.String() text = fields.String(attribute='text_value') text_vertical_align = EnumField(enum=enums.VertAlignOptions) text_horizontal_align = EnumField(enum=enums.HorizAlignOptions) text_auto_size = fields.Bool() text_max_size = FloatRoundedField() text_min_size = FloatRoundedField() text_size = FloatRoundedField() text_color = ColorField() text_bold = fields.Bool() text_italic = fields.Bool(data_key='text_italics') text_underlined = fields.Bool()
[docs] @post_load def make_obj(self, data, **kwargs): new_obj = ui.Label() init_object(new_obj, data) return new_obj
[docs]class TextInputSchema(Schema): type_name = fields.String() max_length = fields.Int() placeholder_text = fields.Str() placeholder_text_color = ColorField() text_color = ColorField() background_color = ColorField() text_size = FloatRoundedField() text_horizontal_align = EnumField(enum=enums.HorizAlignOptions) text_vertical_align = EnumField(enum=enums.VertAlignOptions) padding_left = FloatRoundedField() padding_right = FloatRoundedField() padding_top = FloatRoundedField() padding_bottom = FloatRoundedField() password = fields.Bool() number = fields.Bool() multi_line = fields.Bool() input_text = fields.Str()
[docs] @post_load def make_obj(self, data, **kwargs): new_obj = ui.TextInput() init_object(new_obj, data) return new_obj
[docs]class SliderSchema(Schema): type_name = fields.String() current_value = FloatRoundedField() min_value = FloatRoundedField() max_value = FloatRoundedField()
[docs] @post_load def make_obj(self, data, **kwargs): new_obj = ui.Slider() init_object(new_obj, data) return new_obj
[docs]class UIListSchema(Schema): type_name = fields.String() display_columns = fields.Int() display_rows = fields.Int() total_columns = fields.Int() unusable = fields.Bool()
[docs] @post_load def make_obj(self, data, **kwargs): new_obj = ui.UIList() init_object(new_obj, data) return new_obj
[docs]class ContentSchema(Schema): """Uses the type_name field to identify the type of content to be loaded/dumped.""" type_name = fields.String() type_name_schemas = { 'Button': ButtonSchema(), 'Mesh': MeshSchema(), 'Image': ImageSchema(), 'Label': LabelSchema(), 'Text Input': TextInputSchema(), 'TextInput': TextInputSchema(), 'Slider': SliderSchema(), 'Dropdown': DropdownSchema(), 'List': UIListSchema(), 'UIList': UIListSchema(), 'LoadingBar': LoadingBarSchema() }
[docs] def load(self, data, *args, **kwargs): type_name = data['type_name'] correct_schema = self.type_name_schemas[type_name] return correct_schema.load(data, *args, **kwargs)
[docs] def dump(self, obj, *args, **kwargs): type_name = obj.__class__.__name__ schema = self.type_name_schemas[type_name] dump_data = schema.dump(obj, *args, **kwargs) dump_data['type_name'] = type_name return dump_data
[docs]class LayoutNodeSchema(Schema): name = fields.String() enabled = fields.Boolean() layer = fields.Int() layout_orientation = EnumField(enum=enums.LayoutTypes) sizing_type = EnumField(enum=enums.SizingTypes) sizing_value = FloatRoundedField() forward_dist = FloatRoundedField() padding_type = EnumField(enum=enums.PaddingTypes) padding_x = FloatRoundedField() padding_y = FloatRoundedField() padding_z = FloatRoundedField() padding_w = FloatRoundedField() children = fields.List(fields.Nested(lambda: LayoutNodeSchema())) content = fields.Nested(lambda: ContentSchema(), allow_none=True)
[docs] @post_load def make_obj(self, data, **kwargs): new_obj = ui.LayoutNode() padding_data = { 'left': data.pop('padding_x', None), 'right': data.pop('padding_y', None), 'top': data.pop('padding_z', None), 'down': data.pop('padding_w', None) } new_obj.set_padding(**padding_data) init_object(new_obj, data) for child in new_obj.children: child._parent = new_obj return new_obj