@@ -39,25 +39,26 @@ def JupyterViz(
3939
4040 current_step , set_current_step = solara .use_state (0 )
4141
42- solara .Markdown (name )
43-
44- # 0. Split model params
45- model_params_input , model_params_fixed = split_model_params (model_params )
46-
47- # 1. User inputs
48- user_inputs = {}
49- for name , options in model_params_input .items ():
50- user_input = solara .use_reactive (options ["value" ])
51- user_inputs [name ] = user_input .value
52- make_user_input (user_input , name , options )
42+ # 1. Set up model parameters
43+ user_params , fixed_params = split_model_params (model_params )
44+ model_parameters , set_model_parameters = solara .use_state (
45+ fixed_params | {k : v ["value" ] for k , v in user_params .items ()}
46+ )
5347
54- # 2. Model
48+ # 2. Set up Model
5549 def make_model ():
56- return model_class (** user_inputs , ** model_params_fixed )
50+ model = model_class (** model_parameters )
51+ set_current_step (0 )
52+ return model
5753
58- model = solara .use_memo (make_model , dependencies = list (user_inputs .values ()))
54+ model = solara .use_memo (make_model , dependencies = list (model_parameters .values ()))
5955
60- # 3. Buttons
56+ def handle_change_model_params (name : str , value : any ):
57+ set_model_parameters (model_parameters | {name : value })
58+
59+ # 3. Set up UI
60+ solara .Markdown (name )
61+ UserInputs (user_params , on_change = handle_change_model_params )
6162 ModelController (model , play_interval , current_step , set_current_step )
6263
6364 with solara .GridFixed (columns = 2 ):
@@ -160,44 +161,53 @@ def check_param_is_fixed(param):
160161 return True
161162
162163
163- def make_user_input (user_input , name , options ):
164- """Initialize a user input for configurable model parameters.
164+ @solara .component
165+ def UserInputs (user_params , on_change = None ):
166+ """Initialize user inputs for configurable model parameters.
165167 Currently supports :class:`solara.SliderInt`, :class:`solara.SliderFloat`,
166168 and :class:`solara.Select`.
167169
168- Args:
169- user_input: :class:`solara.reactive` object with initial value
170- name: field name; used as fallback for label if 'label' is not in options
171- options: dictionary with options for the input, including label,
170+ Props:
171+ user_params: dictionary with options for the input, including label,
172172 min and max values, and other fields specific to the input type.
173+ on_change: function to be called with (name, value) when the value of an input changes.
173174 """
174- # label for the input is "label" from options or name
175- label = options .get ("label" , name )
176- input_type = options .get ("type" )
177- if input_type == "SliderInt" :
178- solara .SliderInt (
179- label ,
180- value = user_input ,
181- min = options .get ("min" ),
182- max = options .get ("max" ),
183- step = options .get ("step" ),
184- )
185- elif input_type == "SliderFloat" :
186- solara .SliderFloat (
187- label ,
188- value = user_input ,
189- min = options .get ("min" ),
190- max = options .get ("max" ),
191- step = options .get ("step" ),
192- )
193- elif input_type == "Select" :
194- solara .Select (
195- label ,
196- value = options .get ("value" ),
197- values = options .get ("values" ),
198- )
199- else :
200- raise ValueError (f"{ input_type } is not a supported input type" )
175+
176+ for name , options in user_params .items ():
177+ # label for the input is "label" from options or name
178+ label = options .get ("label" , name )
179+ input_type = options .get ("type" )
180+
181+ def change_handler (value , name = name ):
182+ on_change (name , value )
183+
184+ if input_type == "SliderInt" :
185+ solara .SliderInt (
186+ label ,
187+ value = options .get ("value" ),
188+ on_value = change_handler ,
189+ min = options .get ("min" ),
190+ max = options .get ("max" ),
191+ step = options .get ("step" ),
192+ )
193+ elif input_type == "SliderFloat" :
194+ solara .SliderFloat (
195+ label ,
196+ value = options .get ("value" ),
197+ on_value = change_handler ,
198+ min = options .get ("min" ),
199+ max = options .get ("max" ),
200+ step = options .get ("step" ),
201+ )
202+ elif input_type == "Select" :
203+ solara .Select (
204+ label ,
205+ value = options .get ("value" ),
206+ on_value = change_handler ,
207+ values = options .get ("values" ),
208+ )
209+ else :
210+ raise ValueError (f"{ input_type } is not a supported input type" )
201211
202212
203213def make_space (model , agent_portrayal ):
0 commit comments