AnyDataWithGui

AnyDataWithGui#

Introduction#

AnyDataWithGui associate a GUI to any type, with associated GUI callbacks, allowing for custom rendering, editing, serialization, and event handling within the Fiatlight framework.

It uses callbacks which are stored inside AnyDataGuiCallback.

Signature#

Below, we display the class header, i.e., the class without its methods bodies, to give a quick overview of its structure.

You can see its full code at AnyDataWithGui.

from fiatlight.fiat_notebook import look_at_code
%look_at_class_header fiatlight.fiat_core.AnyDataWithGui
class AnyDataWithGui(Generic[DataType]):
    """AnyDataWithGui: a GUI associated to a type.

    AnyDataWithGui[DataType]
    ========================

    This class manages data of any type with associated GUI callbacks, allowing for custom rendering, editing,
    serialization, and event handling within the Fiatlight framework.

    Members:
    --------
    # The type of the data, e.g. int, str, typing.List[int], typing.Tuple[int, str], typing.Optional[int], etc.
    _type: Type[DataType]

    # The value of the data - can be a DataType, Unspecified, or Error
    # It is accessed through the value property, which triggers the on_change callback (if set)
    _value: DataType | Unspecified | Error = UnspecifiedValue

    # Callbacks for the GUI
    # This is the heart of FiatLight: the GUI is defined by the callbacks.
    # Think of them as __dunder__ methods for the GUI.
    callbacks: AnyDataGuiCallbacks[DataType]

    # If True, the value can be None. This is useful when the data is optional.
    # Otherwise, any None value will be considered as an Error.
    # Note: when using Optional[any registered type], this flag is automatically set to True.
    can_be_none: bool = False

    Property:
    ---------
    # Custom attributes that can be set by the user, to give hints to the GUI.
    # For example, with this function declaration,
    #         def f(x: int, y: int) -> int:
    #             return x + y
    #        f.x__range = (0, 10)
    # fiat_attributes["range"] will be (0, 10) for the parameter x.
    @property
    fiat_attributes -> dict[str, Any]

    """
    _type: Type[DataType] | None
    _value: DataType | Unspecified | Error | Invalid[DataType] = UnspecifiedValue
    callbacks: AnyDataGuiCallbacks[DataType]
    can_be_none: bool = False
    _fiat_attributes: FiatAttributes
    _expanded: bool = False
    _can_set_unspecified_or_default: bool = False
    label: str | None = None
    label_color: ImVec4 | None = None
    tooltip: str | None = None
    status_tooltip: str | None = None

    class CollapseOrExpand(Enum):
        collapse = 'Collapse All'
        expand = 'Expand All'

    class PresentOrEdit(Enum):
        present = 'View'
        edit = 'Edit'

    class _Init_Section:
        """
        # ------------------------------------------------------------------------------------------------------------------
        #            Initialization
        # ------------------------------------------------------------------------------------------------------------------
        """
        pass

    def __init__(self, data_type: Type[DataType] | None) -> None:
        """Initialize the AnyDataWithGui with a type, an unspecified value, and no callbacks."""
        pass

    class _Value_Section:
        """
        # ------------------------------------------------------------------------------------------------------------------
        #            Value getter and setter + get_actual_value (which returns a DataType or raises an exception)
        # ------------------------------------------------------------------------------------------------------------------
        """
        pass

    @property
    def value(self) -> DataType | Unspecified | Error | Invalid[DataType]:
        """The value of the data, accessed through the value property.
        Warning: it might be an instance of `Unspecified` (user did not enter any value) or `Error` (an error was triggered)
        """
        pass

    @value.setter
    def value(self, new_value: DataType | Unspecified | Error | Invalid[DataType]) -> None:
        """Set the value of the data. This triggers the on_change callback (if set)"""
        pass

    def get_actual_value(self) -> DataType:
        """Returns the actual value of the data, or raises an exception if the value is Unspecified or Error or Invalid
        When we are inside a callback, we can be sure that the value is of the correct type, so we can call this method
        instead of accessing the value directly and checking for Unspecified or Error.
        """
        pass

    def get_actual_or_invalid_value(self) -> DataType:
        """Returns the actual value of the data, or raises an exception if the value is Unspecified or Error"""
        pass

    class _CustomAttributes_Section:
        """
        # ------------------------------------------------------------------------------------------------------------------
        #            Custom Attributes
        # ------------------------------------------------------------------------------------------------------------------
        """
        pass

    @staticmethod
    def possible_fiat_attributes() -> PossibleFiatAttributes | None:
        """Return the possible custom attributes for this type, if available.
        Should be overridden in subclasses, when custom attributes are available.

        It is strongly advised to return a class variable, or a global variable
        to avoid creating a new instance each time this method is called.
        """
        pass

    @final
    def possible_fiat_attributes_with_generic(self) -> tuple[PossibleFiatAttributes | None, PossibleFiatAttributes]:
        pass

    @property
    def fiat_attributes(self) -> FiatAttributes:
        pass

    def merge_fiat_attributes(self, fiat_attrs: FiatAttributes) -> None:
        """Merge custom attributes with the existing ones"""
        pass

    def _handle_generic_attrs(self) -> None:
        """Handle generic custom attributes"""
        pass

    @staticmethod
    def propagate_label_and_tooltip(a: 'AnyDataWithGui[Any]', b: 'AnyDataWithGui[Any]') -> None:
        """Propagate label and tooltip from one AnyDataWithGui to another
        Meant to be used with CompositeGui
        """
        pass

    class _Gui_Section:
        """
        # ------------------------------------------------------------------------------------------------------------------
        #            Gui sections
        #      (Can also be used outside a function Node)
        # ------------------------------------------------------------------------------------------------------------------
        """

    def sub_items_can_collapse(self, _present_or_edit: PresentOrEdit) -> bool:
        """Overwrite this in derived classes if they provide multiple sub-items that can be collapsed"""
        pass

    def sub_items_collapse_or_expand(self, _collapse_or_expand: CollapseOrExpand) -> None:
        """Overwrite this in derived classes if they provide multiple sub-items that can be collapsed"""
        pass

    def sub_items_will_collapse_or_expand(self, _present_or_edit: PresentOrEdit) -> CollapseOrExpand:
        """Overwrite this in derived classes if they provide multiple sub-items that can be collapsed"""
        pass

    def _show_collapse_sub_items_buttons(self, present_or_edit: PresentOrEdit) -> None:
        pass

    def can_show_present_popup(self) -> bool:
        pass

    def can_show_edit_popup(self) -> bool:
        pass

    def _show_collapse_button(self) -> None:
        pass

    def _show_copy_to_clipboard_button(self) -> None:
        pass

    def can_collapse_present(self) -> bool:
        pass

    def can_collapse_edit(self) -> bool:
        pass

    def can_edit_on_header_line(self) -> bool:
        pass

    def can_present_on_header_line(self) -> bool:
        pass

    def _can_edit_on_next_lines_if_expanded(self) -> bool:
        pass

    def _can_present_on_next_lines_if_expanded(self) -> bool:
        pass

    def _is_editing_on_next_lines(self) -> bool:
        pass

    def _is_presenting_on_next_lines(self) -> bool:
        pass

    def _popup_window_name(self, params: GuiHeaderLineParams[DataType], present_or_edit: PresentOrEdit) -> str:
        pass

    def _gui_present_header_line(self, params: GuiHeaderLineParams[DataType]) -> None:
        """Present the value as a string in one line, or as a widget if it fits on one line"""
        pass

    def _gui_edit_header_line(self, params: GuiHeaderLineParams[DataType]) -> bool:
        pass

    def _show_set_unspecified_or_default_button(self) -> bool:
        pass

    def _gui_edit_next_lines(self, in_popup: bool) -> bool:
        pass

    def _gui_present_next_lines(self, in_popup: bool) -> None:
        pass

    def gui_present_customizable(self, params: GuiHeaderLineParams[DataType]) -> None:
        """Present the value using either the present callback or the default str conversion
        May present on one line (if possible) or on multiple lines with an expand button
        """
        pass

    def gui_present(self) -> None:
        pass

    def gui_edit_customizable(self, params: GuiHeaderLineParams[DataType]) -> bool:
        """Call the edit callback. Returns True if the value has changed
        May edit on one line (if possible) or on multiple lines with an expand button
        """
        pass

    def gui_edit(self) -> bool:
        pass

    class _Callbacks_Section:
        """
        # ------------------------------------------------------------------------------------------------------------------
        #            Callbacks sections
        # ------------------------------------------------------------------------------------------------------------------
        """

    def set_edit_callback(self, edit_callback: DataEditFunction[DataType]) -> None:
        """Helper function to set the edit callback from a free function"""
        pass

    def set_present_callback(self, present_callback: DataPresentFunction[DataType], present_node_compatible: bool | None=None) -> None:
        """Helper function to set the present custom callback from a free function"""
        pass

    def add_validate_value_callback(self, cb: Callable[[DataType], None]) -> None:
        pass

    def _Serialization_Section(self) -> None:
        """
        # ------------------------------------------------------------------------------------------------------------------
        #            Serialization and deserialization
        # ------------------------------------------------------------------------------------------------------------------
        """
        pass

    @final
    def call_save_to_dict(self, value: DataType | Unspecified | Error | Invalid[DataType]) -> JsonDict:
        """Serialize the value to a dictionary

        Will call the save_to_dict callback if set, otherwise will use the default serialization, when available.
        A default serialization is available for primitive types, tuples, and Pydantic models.

        (This is how fiatlight saves the data to a JSON file)

        Do not override these methods in descendant classes!
        """
        pass

    @final
    def call_load_from_dict(self, json_data: JsonDict) -> DataType | Unspecified | Error:
        """Deserialize the value from a dictionary
        Do not override these methods in descendant classes!
        """
        pass

    @final
    def call_save_gui_options_to_json(self) -> JsonDict:
        pass

    @final
    def call_load_gui_options_from_json(self, json_data: JsonDict) -> None:
        pass

    class _Utilities_Section:
        """
        # ------------------------------------------------------------------------------------------------------------------
        #            Utilities
        # ------------------------------------------------------------------------------------------------------------------
        """

    def can_construct_default_value(self) -> bool:
        pass

    def construct_default_value(self) -> DataType:
        pass

    def datatype_qualified_name(self) -> str:
        pass

    def datatype_basename(self) -> str:
        pass

    def datatype_base_and_qualified_name(self) -> str:
        pass

    def datatype_value_to_str(self, value: DataType) -> str:
        """Convert the value to a string
        Uses either the present_str callback, or the default str conversion
        """
        pass

    def datatype_value_to_clipboard_str(self, value: DataType) -> str:
        """Convert the value to a string for the clipboard
        Uses either the clipboard_copy_str callback, or the default str conversion
        """
        pass

    def docstring_first_line(self) -> str | None:
        """Return the first line of the docstring, if available"""
        pass

Architecture#

Below is a PlantUML diagram showing the architecture of the fiat_core module. See the architecture page for the full architecture diagrams.

from fiatlight.fiat_notebook import plantuml_magic
%plantuml_include class_diagrams/fiat_core.puml
_images/5d8da358d4e7008b80a4e3f2aae1c1a32c7af7d2b9e4f83726bb669fe143b994.svg