FunctionWithGui

FunctionWithGui#

Introduction#

FunctionWithGui is one of the core classes of FiatLight: it wraps a function with a GUI that presents its inputs and outputs.

  • Manual: Read the manual for a detailed guide on how to use it.

  • Source code: View its full code online.

Signature#

Below, you will find the “signature” of the FunctionWithGui class, with its main attributes and methods (but not their bodies)

Its full source code is available online.

from fiatlight.fiat_notebook import look_at_code
%look_at_class_header fiatlight.fiat_core.FunctionWithGui
class FunctionWithGui:
    """FunctionWithGui: add GUI to a function

    `FunctionWithGui` is one of the core classes of FiatLight: it wraps a function with a GUI that presents its
    inputs and its output(s).

    Public Members
    ==============
    # the name of the function
    name: str = ""

    #
    # Behavioral Flags
    # ----------------
    # invoke_async: if true, the function shall be called asynchronously
    invoke_async: bool = False

    # invoke_manually: if true, the function will be called only if the user clicks on the "invoke" button
    # (if inputs were changed, a "Refresh needed" label will be displayed)
    invoke_manually: bool = False

    # invoke_always_dirty: if true, the function output will always be considered out of date, and
    #   - if invoke_manually is true, the "Refresh needed" label will be displayed
    #   - if invoke_manually is false, the function will be called at each frame
    # Note: a "live" function is thus a function with invoke_manually=False and invoke_always_dirty=True
    invoke_always_dirty: bool = False

    # Optional user documentation to be displayed in the GUI
    #     - doc_display: if True, the doc string is displayed in the GUI (default: False)
    #     - doc_is_markdown: if True, the doc string is in Markdown format (default: True)
    #     - doc_user: the documentation string. If not provided, the function docstring will be used
    #     - doc_show_source: if True, the source code of the function will be displayed in the GUI
    doc_display: bool = True
    doc_markdown: bool = True
    doc_user: str = ""
    doc_show_source: bool = False

    #
    # Internal state GUI
    # ------------------
    # internal_state_gui: optional Gui for the internal state of the function
    # (this function may display a GUI to show the internal state of the function,
    #  and return True if the state has changed, and the function needs to be called)
    internal_state_gui: BoolFunction | None = None

    # internal_state_gui_node_compatible:
    # If True, the internal_state_gui function is incompatible with being presented in a node
    # (this is due to a limitation of the node editor, which cannot render scrollable widgets)
    # Note: instead of setting edit_node_compatible to False, you may query
    #       `fiatlight.is_rendering_in_node()` to know if you are rendering in a node
    #       and choose alternative widgets in this case.
    internal_state_gui_node_compatible: bool = True

    #
    # Heartbeat
    # ---------
    # on_heartbeat: optional function that will be called at each frame
    # (and return True if the function needs to be called to update the output)
    on_heartbeat: BoolFunction | None = None

    #
    # Serialization
    # -------------
    # save/load_internal_gui_options_from_json (Optional)
    # Optional serialization and deserialization of the internal state GUI presentation options
    # (i.e. anything that deals with how the GUI is presented, not the data itself)
    # If provided, these functions will be used to recreate the GUI presentation options when loading a graph,
    # so that the GUI looks the same when the application is restarted.
    save_internal_gui_options_to_json: Callable[[], JsonDict] | None = None
    load_internal_gui_options_from_json: Callable[[JsonDict], None] | None = None

    """
    function_name: str = ''
    label: str = ''
    invoke_async: bool = False
    invoke_manually: bool = False
    invoke_always_dirty: bool = False
    invoke_is_gui_only: bool = False
    doc_display: bool = True
    doc_markdown: bool = True
    doc_user: str = ''
    doc_show_source: bool = False
    internal_state_gui: BoolFunction | None = None
    internal_state_gui_node_compatible: bool = True
    save_internal_gui_options_to_json: Callable[[], JsonDict] | None = None
    load_internal_gui_options_from_json: Callable[[JsonDict], None] | None = None
    on_heartbeat: BoolFunction | None = None
    _dirty: bool = True
    _f_impl: Callable[..., Any] | None = None
    _inputs_with_gui: List[ParamWithGui[Any]]
    _outputs_with_gui: List[OutputWithGui[Any]]
    _last_exception_message: Optional[str] = None
    _last_exception_traceback: Optional[str] = None
    _accept_none_as_output: bool = False

    class _Construct_Section:
        """
        # --------------------------------------------------------------------------------------------
        #        Construction
        #  input_with_gui and output_with_gui should be filled soon after construction
        # --------------------------------------------------------------------------------------------
        """
        pass

    def __init__(self, fn: Callable[..., Any] | None, fn_name: str | None=None, *, signature_string: str | None=None, fiat_attributes: FiatAttributes | None=None, is_dataclass_init_method: bool=False) -> None:
        """Create a FunctionWithGui object, with the given function as implementation

        The function signature is automatically parsed, and the inputs and outputs are created
        with the correct GUI types.

        :param fn: the function for which we want to create a FunctionWithGui

        Notes:
        This function will capture the locals and globals of the caller to be able to evaluate the types.
        Make sure to call this function *from the module where the function and its input/output types are defined*

        If the function has attributes like invoke_manually or invoke_async, they will be taken into account:
            - if `invoke_async` is True, the function will be called asynchronously
            - if `invoke_manually` is True, the function will be called only if the user clicks on the "invoke" button


        Advanced parameters:
        ********************
        :param signature_string: a string representing the signature of the function
                                 used when the function signature cannot be retrieved automatically
        """
        pass

    class _FiatAttributes_Section:
        """
        # --------------------------------------------------------------------------------------------
        #        Fiat Attributes
        # --------------------------------------------------------------------------------------------
        """
        pass

    def handle_fiat_attributes(self, fiat_attributes: dict[str, Any]) -> None:
        """Handle custom attributes for the function"""
        pass

    def set_invoke_live(self) -> None:
        """Set flags to make this a live function (called automatically at each frame)"""
        pass

    def set_invoke_manually(self) -> None:
        """Set flags to make this a function that needs to be called manually"""
        pass

    def set_invoke_manually_io(self) -> None:
        """Set flags to make this a IO function that needs to be called manually
        and that is always considered dirty, because it depends on an external device
        or state (and likely has no input)"""
        pass

    def is_invoke_manually_io(self) -> bool:
        """Return True if the function is an IO function that needs to be called manually"""
        pass

    def set_invoke_async(self) -> None:
        """Set flags to make this a function that is called asynchronously"""
        pass

    def is_live(self) -> bool:
        """Return True if the function is live"""
        pass

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

    def call_for_tests(self, **params: Any) -> Any:
        """Call the function with the given parameters, for testing purposes"""
        pass

    def is_dirty(self) -> bool:
        """Return True if the function needs to be called, because the inputs have changed since the last call"""
        pass

    def set_dirty(self) -> None:
        """Set the function as dirty."""
        pass

    def get_last_exception_message(self) -> str | None:
        """Return the last exception message, if any"""
        pass

    def shall_display_refresh_needed_label(self) -> bool:
        """Return True if the "Refresh needed" label should be displayed
        i.e. if the function is dirty and invoke_manually is True"""
        pass

    def __str__(self) -> str:
        pass

    class _Inputs_Section:
        """
        # --------------------------------------------------------------------------------------------
        #        Inputs, aka parameters
        # --------------------------------------------------------------------------------------------
        """
        pass

    def nb_inputs(self) -> int:
        """Return the number of inputs of the function"""
        pass

    def all_inputs_names(self) -> List[str]:
        """Return the names of all the inputs of the function"""
        pass

    def input(self, name: str) -> AnyDataWithGui[Any]:
        """Return the input with the given name as a AnyDataWithGui[Any]
        The inner type of the returned value is Any in this case.
        You may have to cast it to the correct type, if you rely on type hints.

        Use input_as() if you want to get the input with the correct type.
        """
        pass

    def input_as(self, name: str, gui_type: Type[GuiType]) -> GuiType:
        """Return the input with the given name as a GuiType

        GuiType can be any descendant of AnyDataWithGui, like
            fiatlight.fiat_core.IntWithGui, fiatlight.fiat_core.FloatWithGui, etc.

        Raises a ValueError if the input is not found, and a TypeError if the input is not of the correct type.
        """
        pass

    def input_of_idx(self, idx: int) -> ParamWithGui[Any]:
        """Return the input with the given index as a ParamWithGui[Any]"""
        pass

    def input_of_idx_as(self, idx: int, gui_type: Type[GuiType]) -> GuiType:
        """Return the input with the given index as a GuiType"""
        pass

    def inputs_guis(self) -> List[AnyDataWithGui[Any]]:
        pass

    def set_input_gui(self, name: str, gui: AnyDataWithGui[Any]) -> None:
        """Set the GUI for the input with the given name"""
        pass

    def has_param(self, name: str) -> bool:
        """Return True if the function has a parameter with the given name"""
        pass

    def param(self, name: str) -> ParamWithGui[Any]:
        """Return the input with the given name as a ParamWithGui[Any]"""
        pass

    def param_gui(self, name: str) -> AnyDataWithGui[Any]:
        """Return the input with the given name as a AnyDataWithGui[Any]"""
        pass

    def set_param_value(self, name: str, value: Any) -> None:
        """Set the value of the input with the given name
        This is useful to set the value of an input programmatically, for example in tests.
        """
        pass

    def toggle_expand_inputs(self) -> None:
        pass

    def toggle_expand_outputs(self) -> None:
        pass

    class _Outputs_Section:
        """
        # --------------------------------------------------------------------------------------------
        #        Outputs
        # --------------------------------------------------------------------------------------------
        """
        pass

    def nb_outputs(self) -> int:
        """Return the number of outputs of the function.
        A function typically has 0 or 1 output, but it can have more if it returns a tuple.
        """
        pass

    def output(self, output_idx: int=0) -> AnyDataWithGui[Any]:
        """Return the output with the given index as a AnyDataWithGui[Any]
        The inner type of the returned value is Any in this case.
        You may have to cast it to the correct type, if you rely on type hints.

        Use output_as() if you want to get the output with the correct type.
        """
        pass

    def output_as(self, output_idx: int, gui_type: Type[GuiType]) -> GuiType:
        """Return the output with the given index as a GuiType

        GuiType can be any descendant of AnyDataWithGui, like
            fiatlight.fiat_core.IntWithGui, fiatlight.fiat_core.FloatWithGui, etc.

        Raises a ValueError if the output is not found, and a TypeError if the output is not of the correct type.
        """
        pass

    def outputs_guis(self) -> List[AnyDataWithGui[Any]]:
        pass

    class _Invoke_Section:
        """
        # --------------------------------------------------------------------------------------------
        #        Invoke the function
        # This is the heart of fiatlight: it calls the function with the current inputs
        # and stores the result in the outputs, stores the exception if any, etc.
        # --------------------------------------------------------------------------------------------
        """
        pass

    @final
    def has_bad_inputs(self) -> bool:
        pass

    @final
    def invoke(self) -> None:
        """Invoke the function with the current inputs, and store the result in the outputs.

        Will call the function if:
         - the inputs have changed since the last call
         - the function is dirty
         - none of the inputs is an error or unspecified

        If an exception is raised, the outputs will be set to ErrorValue, and the exception will be stored.

        If the function returned None and the output is not allowed to be None, a ValueError will be raised
        (this is inferred from the function signature)
        """
        pass

    def invoke_gui(self) -> None:
        pass

    @final
    def _invoke_impl(self) -> None:
        pass

    def on_exit(self) -> None:
        """Called when the application is exiting
        Will call the on_exit callback of all the inputs and outputs
        """
        pass

    def _can_emit_none_output(self) -> bool:
        """Return True if the function can emit None as output
        i.e.
        - either the function has no output
        - or the output can be None (i.e. the signature looks like `def f() -> int | None:`)
        if the function has multiple outputs, we consider that it can not emit None
        """
        pass

    class _Serialize_Section:
        """
        # --------------------------------------------------------------------------------------------
        #        Save and load to json
        # Here, we only save the options that the user entered manually in the GUI:
        #   - the options of the inputs
        #   - the options of the outputs
        # --------------------------------------------------------------------------------------------
        """
        pass

    def save_user_inputs_to_json(self) -> JsonDict:
        pass

    def load_user_inputs_from_json(self, json_data: JsonDict) -> None:
        pass

    def save_gui_options_to_json(self) -> JsonDict:
        """Save the GUI options to a JSON file
        (i.e. any presentation options of the inputs and outputs, as well as of the internal GUI)
        """
        pass

    def load_gui_options_from_json(self, json_data: JsonDict) -> None:
        """Load the GUI options from a JSON file"""
        pass

    class _Doc_Section:
        pass

    def get_function_doc(self) -> FunctionWithGuiDoc:
        pass

    def _get_function_userdoc(self) -> str | None:
        """Return the user documentation of the function"""
        pass

    def _get_function_docstring(self) -> str | None:
        """Return the docstring of the function"""
        pass

    def _get_function_source_code(self) -> str | None:
        """Return the source code of the function"""
        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