@@ -39,25 +39,26 @@ def JupyterViz(
39
39
40
40
current_step , set_current_step = solara .use_state (0 )
41
41
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
+ )
53
47
54
- # 2. Model
48
+ # 2. Set up Model
55
49
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
57
53
58
- model = solara .use_memo (make_model , dependencies = list (user_inputs .values ()))
54
+ model = solara .use_memo (make_model , dependencies = list (model_parameters .values ()))
59
55
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 )
61
62
ModelController (model , play_interval , current_step , set_current_step )
62
63
63
64
with solara .GridFixed (columns = 2 ):
@@ -160,44 +161,53 @@ def check_param_is_fixed(param):
160
161
return True
161
162
162
163
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.
165
167
Currently supports :class:`solara.SliderInt`, :class:`solara.SliderFloat`,
166
168
and :class:`solara.Select`.
167
169
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,
172
172
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.
173
174
"""
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" )
201
211
202
212
203
213
def make_space (model , agent_portrayal ):
0 commit comments