def register_widget( element_type: str, element_proto: WidgetProto, deserializer: WidgetDeserializer, serializer: WidgetSerializer, user_key: Optional[str] = None, widget_func_name: Optional[str] = None, on_change_handler: Optional[WidgetCallback] = None, args: Optional[WidgetArgs] = None, kwargs: Optional[WidgetKwargs] = None, ) -> Tuple[Any, bool]: """Register a widget with Streamlit, and return its current value. NOTE: This function should be called after the proto has been filled. Parameters ---------- element_type : str The type of the element as stored in proto. element_proto : proto The proto of the specified type (e.g. Button/Multiselect/Slider proto) user_key : Optional[str] Optional user-specified string to use as the widget ID. If this is None, we'll generate an ID by hashing the element. widget_func_name : Optional[str] The widget's DeltaGenerator function name, if it's different from its element_type. Custom components are a special case: they all have the element_type "component_instance", but are instantiated with dynamically-named functions. on_change_handler : Optional[WidgetCallback] An optional callback invoked when the widget's value changes. deserializer : Optional[WidgetDeserializer] Called to convert a widget's protobuf value to the value returned by its st.<widget_name> function. args : Optional[WidgetArgs] args to pass to on_change_handler when invoked kwargs : Optional[WidgetKwargs] kwargs to pass to on_change_handler when invoked Returns ------- ui_value : Tuple[Any, bool] - If our ReportContext doesn't exist (meaning that we're running a "bare script" outside of streamlit), we'll return None. - Else if this is a new widget, it won't yet have a value and we'll return None. - Else if the widget has already been registered on a previous run but the user hasn't interacted with it on the client, it will have the default value it was first created with. - Else the widget has already been registered and the user *has* interacted with it, it will have that most recent user-specified value. """ widget_id = _get_widget_id(element_type, element_proto, user_key) element_proto.id = widget_id ctx = report_thread.get_report_ctx() if ctx is None: # Early-out if we're not running inside a ReportThread (which # probably means we're running as a "bare" Python script, and # not via `streamlit run`). return (deserializer(None, ""), False) # Register the widget, and ensure another widget with the same id hasn't # already been registered. added = ctx.widget_ids_this_run.add(widget_id) if not added: raise DuplicateWidgetID( _build_duplicate_widget_message( widget_func_name if widget_func_name is not None else element_type, user_key, )) session_state = ctx.session_state metadata = WidgetMetadata( widget_id, deserializer, serializer, value_type=element_type_to_value_type[element_type], callback=on_change_handler, callback_args=args, callback_kwargs=kwargs, ) # TODO: should these be merged into a more generic call so this code doesn't need to know about keyed vs unkeyed? if user_key is not None: session_state.set_keyed_widget(metadata, widget_id, user_key) else: session_state.set_unkeyed_widget(metadata, widget_id) value_changed = session_state.should_set_frontend_state_value( widget_id, user_key) val = session_state.get_value_for_registration(widget_id) return (val, value_changed)