FunctionWithGui#
Introduction#
FunctionWithGui
is one of the core classes of FiatLight: it wraps a function with a GUI that presents its inputs and outputs.
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