We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
JupyterViz: Changing simulation parameters shouldn't automatically start the simulation
**Describe the bug**
Currently, changing the parameters auto-play the simulation.

**Expected behavior**
It shouldn't.

**To Reproduce**
Run any `JupyterViz` simulation.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mesa/experimental/jupyter_viz.py`
Content:
```
1 import threading
2 
3 import matplotlib.pyplot as plt
4 import networkx as nx
5 import reacton.ipywidgets as widgets
6 import solara
7 from matplotlib.figure import Figure
8 from matplotlib.ticker import MaxNLocator
9 
10 import mesa
11 
12 # Avoid interactive backend
13 plt.switch_backend("agg")
14 
15 
16 @solara.component
17 def JupyterViz(
18     model_class,
19     model_params,
20     measures=None,
21     name="Mesa Model",
22     agent_portrayal=None,
23     space_drawer="default",
24     play_interval=400,
25 ):
26     """Initialize a component to visualize a model.
27     Args:
28         model_class: class of the model to instantiate
29         model_params: parameters for initializing the model
30         measures: list of callables or data attributes to plot
31         name: name for display
32         agent_portrayal: options for rendering agents (dictionary)
33         space_drawer: method to render the agent space for
34             the model; default implementation is :meth:`make_space`;
35             simulations with no space to visualize should
36             specify `space_drawer=False`
37         play_interval: play interval (default: 400)
38     """
39 
40     current_step, set_current_step = solara.use_state(0)
41 
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     )
47 
48     # 2. Set up Model
49     def make_model():
50         model = model_class(**model_parameters)
51         set_current_step(0)
52         return model
53 
54     reset_counter = solara.use_reactive(0)
55     model = solara.use_memo(
56         make_model, dependencies=[*list(model_parameters.values()), reset_counter.value]
57     )
58 
59     def handle_change_model_params(name: str, value: any):
60         set_model_parameters({**model_parameters, name: value})
61 
62     # 3. Set up UI
63     solara.Markdown(name)
64     UserInputs(user_params, on_change=handle_change_model_params)
65     ModelController(model, play_interval, current_step, set_current_step, reset_counter)
66 
67     with solara.GridFixed(columns=2):
68         # 4. Space
69         if space_drawer == "default":
70             # draw with the default implementation
71             make_space(model, agent_portrayal)
72         elif space_drawer:
73             # if specified, draw agent space with an alternate renderer
74             space_drawer(model, agent_portrayal)
75         # otherwise, do nothing (do not draw space)
76 
77         # 5. Plots
78         for measure in measures:
79             if callable(measure):
80                 # Is a custom object
81                 measure(model)
82             else:
83                 make_plot(model, measure)
84 
85 
86 @solara.component
87 def ModelController(
88     model, play_interval, current_step, set_current_step, reset_counter
89 ):
90     playing = solara.use_reactive(False)
91     thread = solara.use_reactive(None)
92 
93     def on_value_play(change):
94         if model.running:
95             do_step()
96         else:
97             playing.value = False
98 
99     def do_step():
100         model.step()
101         set_current_step(model.schedule.steps)
102 
103     def do_play():
104         model.running = True
105         while model.running:
106             do_step()
107 
108     def threaded_do_play():
109         if thread is not None and thread.is_alive():
110             return
111         thread.value = threading.Thread(target=do_play)
112         thread.start()
113 
114     def do_pause():
115         if (thread is None) or (not thread.is_alive()):
116             return
117         model.running = False
118         thread.join()
119 
120     def do_reset():
121         reset_counter.value += 1
122 
123     with solara.Row():
124         solara.Button(label="Step", color="primary", on_click=do_step)
125         # This style is necessary so that the play widget has almost the same
126         # height as typical Solara buttons.
127         solara.Style(
128             """
129         .widget-play {
130             height: 30px;
131         }
132         """
133         )
134         widgets.Play(
135             value=0,
136             interval=play_interval,
137             repeat=True,
138             show_repeat=False,
139             on_value=on_value_play,
140             playing=playing.value,
141             on_playing=playing.set,
142         )
143         solara.Button(label="Reset", color="primary", on_click=do_reset)
144         solara.Markdown(md_text=f"**Step:** {current_step}")
145         # threaded_do_play is not used for now because it
146         # doesn't work in Google colab. We use
147         # ipywidgets.Play until it is fixed. The threading
148         # version is definite a much better implementation,
149         # if it works.
150         # solara.Button(label="▶", color="primary", on_click=viz.threaded_do_play)
151         # solara.Button(label="⏸︎", color="primary", on_click=viz.do_pause)
152         # solara.Button(label="Reset", color="primary", on_click=do_reset)
153 
154 
155 def split_model_params(model_params):
156     model_params_input = {}
157     model_params_fixed = {}
158     for k, v in model_params.items():
159         if check_param_is_fixed(v):
160             model_params_fixed[k] = v
161         else:
162             model_params_input[k] = v
163     return model_params_input, model_params_fixed
164 
165 
166 def check_param_is_fixed(param):
167     if not isinstance(param, dict):
168         return True
169     if "type" not in param:
170         return True
171 
172 
173 @solara.component
174 def UserInputs(user_params, on_change=None):
175     """Initialize user inputs for configurable model parameters.
176     Currently supports :class:`solara.SliderInt`, :class:`solara.SliderFloat`,
177     and :class:`solara.Select`.
178 
179     Props:
180         user_params: dictionary with options for the input, including label,
181         min and max values, and other fields specific to the input type.
182         on_change: function to be called with (name, value) when the value of an input changes.
183     """
184 
185     for name, options in user_params.items():
186         # label for the input is "label" from options or name
187         label = options.get("label", name)
188         input_type = options.get("type")
189 
190         def change_handler(value, name=name):
191             on_change(name, value)
192 
193         if input_type == "SliderInt":
194             solara.SliderInt(
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 == "SliderFloat":
203             solara.SliderFloat(
204                 label,
205                 value=options.get("value"),
206                 on_value=change_handler,
207                 min=options.get("min"),
208                 max=options.get("max"),
209                 step=options.get("step"),
210             )
211         elif input_type == "Select":
212             solara.Select(
213                 label,
214                 value=options.get("value"),
215                 on_value=change_handler,
216                 values=options.get("values"),
217             )
218         else:
219             raise ValueError(f"{input_type} is not a supported input type")
220 
221 
222 def make_space(model, agent_portrayal):
223     def portray(g):
224         x = []
225         y = []
226         s = []  # size
227         c = []  # color
228         for i in range(g.width):
229             for j in range(g.height):
230                 content = g._grid[i][j]
231                 if not content:
232                     continue
233                 if not hasattr(content, "__iter__"):
234                     # Is a single grid
235                     content = [content]
236                 for agent in content:
237                     data = agent_portrayal(agent)
238                     x.append(i)
239                     y.append(j)
240                     if "size" in data:
241                         s.append(data["size"])
242                     if "color" in data:
243                         c.append(data["color"])
244         out = {"x": x, "y": y}
245         if len(s) > 0:
246             out["s"] = s
247         if len(c) > 0:
248             out["c"] = c
249         return out
250 
251     space_fig = Figure()
252     space_ax = space_fig.subplots()
253     if isinstance(model.grid, mesa.space.NetworkGrid):
254         _draw_network_grid(model, space_ax, agent_portrayal)
255     else:
256         space_ax.scatter(**portray(model.grid))
257     space_ax.set_axis_off()
258     solara.FigureMatplotlib(space_fig)
259 
260 
261 def _draw_network_grid(model, space_ax, agent_portrayal):
262     graph = model.grid.G
263     pos = nx.spring_layout(graph, seed=0)
264     nx.draw(
265         graph,
266         ax=space_ax,
267         pos=pos,
268         **agent_portrayal(graph),
269     )
270 
271 
272 def make_plot(model, measure):
273     fig = Figure()
274     ax = fig.subplots()
275     df = model.datacollector.get_model_vars_dataframe()
276     ax.plot(df.loc[:, measure])
277     ax.set_ylabel(measure)
278     # Set integer x axis
279     ax.xaxis.set_major_locator(MaxNLocator(integer=True))
280     solara.FigureMatplotlib(fig)
281 
282 
283 def make_text(renderer):
284     def function(model):
285         solara.Markdown(renderer(model))
286 
287     return function
288
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py
--- a/mesa/experimental/jupyter_viz.py
+++ b/mesa/experimental/jupyter_viz.py
@@ -89,15 +89,25 @@
 ):
     playing = solara.use_reactive(False)
     thread = solara.use_reactive(None)
+    # We track the previous step to detect if user resets the model via
+    # clicking the reset button or changing the parameters. If previous_step >
+    # current_step, it means a model reset happens while the simulation is
+    # still playing.
+    previous_step = solara.use_reactive(0)
 
     def on_value_play(change):
-        if model.running:
+        if previous_step.value > current_step and current_step == 0:
+            # We add extra checks for current_step == 0, just to be sure.
+            # We automatically stop the playing if a model is reset.
+            playing.value = False
+        elif model.running:
             do_step()
         else:
             playing.value = False
 
     def do_step():
         model.step()
+        previous_step.value = current_step
         set_current_step(model.schedule.steps)
 
     def do_play():

{"golden_diff": "diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py\n--- a/mesa/experimental/jupyter_viz.py\n+++ b/mesa/experimental/jupyter_viz.py\n@@ -89,15 +89,25 @@\n ):\n     playing = solara.use_reactive(False)\n     thread = solara.use_reactive(None)\n+    # We track the previous step to detect if user resets the model via\n+    # clicking the reset button or changing the parameters. If previous_step >\n+    # current_step, it means a model reset happens while the simulation is\n+    # still playing.\n+    previous_step = solara.use_reactive(0)\n \n     def on_value_play(change):\n-        if model.running:\n+        if previous_step.value > current_step and current_step == 0:\n+            # We add extra checks for current_step == 0, just to be sure.\n+            # We automatically stop the playing if a model is reset.\n+            playing.value = False\n+        elif model.running:\n             do_step()\n         else:\n             playing.value = False\n \n     def do_step():\n         model.step()\n+        previous_step.value = current_step\n         set_current_step(model.schedule.steps)\n \n     def do_play():\n", "issue": "JupyterViz: Changing simulation parameters shouldn't automatically start the simulation\n**Describe the bug**\r\nCurrently, changing the parameters auto-play the simulation.\r\n\r\n**Expected behavior**\r\nIt shouldn't.\r\n\r\n**To Reproduce**\r\nRun any `JupyterViz` simulation.\n", "before_files": [{"content": "import threading\n\nimport matplotlib.pyplot as plt\nimport networkx as nx\nimport reacton.ipywidgets as widgets\nimport solara\nfrom matplotlib.figure import Figure\nfrom matplotlib.ticker import MaxNLocator\n\nimport mesa\n\n# Avoid interactive backend\nplt.switch_backend(\"agg\")\n\n\n@solara.component\ndef JupyterViz(\n    model_class,\n    model_params,\n    measures=None,\n    name=\"Mesa Model\",\n    agent_portrayal=None,\n    space_drawer=\"default\",\n    play_interval=400,\n):\n    \"\"\"Initialize a component to visualize a model.\n    Args:\n        model_class: class of the model to instantiate\n        model_params: parameters for initializing the model\n        measures: list of callables or data attributes to plot\n        name: name for display\n        agent_portrayal: options for rendering agents (dictionary)\n        space_drawer: method to render the agent space for\n            the model; default implementation is :meth:`make_space`;\n            simulations with no space to visualize should\n            specify `space_drawer=False`\n        play_interval: play interval (default: 400)\n    \"\"\"\n\n    current_step, set_current_step = solara.use_state(0)\n\n    # 1. Set up model parameters\n    user_params, fixed_params = split_model_params(model_params)\n    model_parameters, set_model_parameters = solara.use_state(\n        {**fixed_params, **{k: v[\"value\"] for k, v in user_params.items()}}\n    )\n\n    # 2. Set up Model\n    def make_model():\n        model = model_class(**model_parameters)\n        set_current_step(0)\n        return model\n\n    reset_counter = solara.use_reactive(0)\n    model = solara.use_memo(\n        make_model, dependencies=[*list(model_parameters.values()), reset_counter.value]\n    )\n\n    def handle_change_model_params(name: str, value: any):\n        set_model_parameters({**model_parameters, name: value})\n\n    # 3. Set up UI\n    solara.Markdown(name)\n    UserInputs(user_params, on_change=handle_change_model_params)\n    ModelController(model, play_interval, current_step, set_current_step, reset_counter)\n\n    with solara.GridFixed(columns=2):\n        # 4. Space\n        if space_drawer == \"default\":\n            # draw with the default implementation\n            make_space(model, agent_portrayal)\n        elif space_drawer:\n            # if specified, draw agent space with an alternate renderer\n            space_drawer(model, agent_portrayal)\n        # otherwise, do nothing (do not draw space)\n\n        # 5. Plots\n        for measure in measures:\n            if callable(measure):\n                # Is a custom object\n                measure(model)\n            else:\n                make_plot(model, measure)\n\n\n@solara.component\ndef ModelController(\n    model, play_interval, current_step, set_current_step, reset_counter\n):\n    playing = solara.use_reactive(False)\n    thread = solara.use_reactive(None)\n\n    def on_value_play(change):\n        if model.running:\n            do_step()\n        else:\n            playing.value = False\n\n    def do_step():\n        model.step()\n        set_current_step(model.schedule.steps)\n\n    def do_play():\n        model.running = True\n        while model.running:\n            do_step()\n\n    def threaded_do_play():\n        if thread is not None and thread.is_alive():\n            return\n        thread.value = threading.Thread(target=do_play)\n        thread.start()\n\n    def do_pause():\n        if (thread is None) or (not thread.is_alive()):\n            return\n        model.running = False\n        thread.join()\n\n    def do_reset():\n        reset_counter.value += 1\n\n    with solara.Row():\n        solara.Button(label=\"Step\", color=\"primary\", on_click=do_step)\n        # This style is necessary so that the play widget has almost the same\n        # height as typical Solara buttons.\n        solara.Style(\n            \"\"\"\n        .widget-play {\n            height: 30px;\n        }\n        \"\"\"\n        )\n        widgets.Play(\n            value=0,\n            interval=play_interval,\n            repeat=True,\n            show_repeat=False,\n            on_value=on_value_play,\n            playing=playing.value,\n            on_playing=playing.set,\n        )\n        solara.Button(label=\"Reset\", color=\"primary\", on_click=do_reset)\n        solara.Markdown(md_text=f\"**Step:** {current_step}\")\n        # threaded_do_play is not used for now because it\n        # doesn't work in Google colab. We use\n        # ipywidgets.Play until it is fixed. The threading\n        # version is definite a much better implementation,\n        # if it works.\n        # solara.Button(label=\"\u25b6\", color=\"primary\", on_click=viz.threaded_do_play)\n        # solara.Button(label=\"\u23f8\ufe0e\", color=\"primary\", on_click=viz.do_pause)\n        # solara.Button(label=\"Reset\", color=\"primary\", on_click=do_reset)\n\n\ndef split_model_params(model_params):\n    model_params_input = {}\n    model_params_fixed = {}\n    for k, v in model_params.items():\n        if check_param_is_fixed(v):\n            model_params_fixed[k] = v\n        else:\n            model_params_input[k] = v\n    return model_params_input, model_params_fixed\n\n\ndef check_param_is_fixed(param):\n    if not isinstance(param, dict):\n        return True\n    if \"type\" not in param:\n        return True\n\n\n@solara.component\ndef UserInputs(user_params, on_change=None):\n    \"\"\"Initialize user inputs for configurable model parameters.\n    Currently supports :class:`solara.SliderInt`, :class:`solara.SliderFloat`,\n    and :class:`solara.Select`.\n\n    Props:\n        user_params: dictionary with options for the input, including label,\n        min and max values, and other fields specific to the input type.\n        on_change: function to be called with (name, value) when the value of an input changes.\n    \"\"\"\n\n    for name, options in user_params.items():\n        # label for the input is \"label\" from options or name\n        label = options.get(\"label\", name)\n        input_type = options.get(\"type\")\n\n        def change_handler(value, name=name):\n            on_change(name, value)\n\n        if input_type == \"SliderInt\":\n            solara.SliderInt(\n                label,\n                value=options.get(\"value\"),\n                on_value=change_handler,\n                min=options.get(\"min\"),\n                max=options.get(\"max\"),\n                step=options.get(\"step\"),\n            )\n        elif input_type == \"SliderFloat\":\n            solara.SliderFloat(\n                label,\n                value=options.get(\"value\"),\n                on_value=change_handler,\n                min=options.get(\"min\"),\n                max=options.get(\"max\"),\n                step=options.get(\"step\"),\n            )\n        elif input_type == \"Select\":\n            solara.Select(\n                label,\n                value=options.get(\"value\"),\n                on_value=change_handler,\n                values=options.get(\"values\"),\n            )\n        else:\n            raise ValueError(f\"{input_type} is not a supported input type\")\n\n\ndef make_space(model, agent_portrayal):\n    def portray(g):\n        x = []\n        y = []\n        s = []  # size\n        c = []  # color\n        for i in range(g.width):\n            for j in range(g.height):\n                content = g._grid[i][j]\n                if not content:\n                    continue\n                if not hasattr(content, \"__iter__\"):\n                    # Is a single grid\n                    content = [content]\n                for agent in content:\n                    data = agent_portrayal(agent)\n                    x.append(i)\n                    y.append(j)\n                    if \"size\" in data:\n                        s.append(data[\"size\"])\n                    if \"color\" in data:\n                        c.append(data[\"color\"])\n        out = {\"x\": x, \"y\": y}\n        if len(s) > 0:\n            out[\"s\"] = s\n        if len(c) > 0:\n            out[\"c\"] = c\n        return out\n\n    space_fig = Figure()\n    space_ax = space_fig.subplots()\n    if isinstance(model.grid, mesa.space.NetworkGrid):\n        _draw_network_grid(model, space_ax, agent_portrayal)\n    else:\n        space_ax.scatter(**portray(model.grid))\n    space_ax.set_axis_off()\n    solara.FigureMatplotlib(space_fig)\n\n\ndef _draw_network_grid(model, space_ax, agent_portrayal):\n    graph = model.grid.G\n    pos = nx.spring_layout(graph, seed=0)\n    nx.draw(\n        graph,\n        ax=space_ax,\n        pos=pos,\n        **agent_portrayal(graph),\n    )\n\n\ndef make_plot(model, measure):\n    fig = Figure()\n    ax = fig.subplots()\n    df = model.datacollector.get_model_vars_dataframe()\n    ax.plot(df.loc[:, measure])\n    ax.set_ylabel(measure)\n    # Set integer x axis\n    ax.xaxis.set_major_locator(MaxNLocator(integer=True))\n    solara.FigureMatplotlib(fig)\n\n\ndef make_text(renderer):\n    def function(model):\n        solara.Markdown(renderer(model))\n\n    return function\n", "path": "mesa/experimental/jupyter_viz.py"}], "after_files": [{"content": "import threading\n\nimport matplotlib.pyplot as plt\nimport networkx as nx\nimport reacton.ipywidgets as widgets\nimport solara\nfrom matplotlib.figure import Figure\nfrom matplotlib.ticker import MaxNLocator\n\nimport mesa\n\n# Avoid interactive backend\nplt.switch_backend(\"agg\")\n\n\n@solara.component\ndef JupyterViz(\n    model_class,\n    model_params,\n    measures=None,\n    name=\"Mesa Model\",\n    agent_portrayal=None,\n    space_drawer=\"default\",\n    play_interval=400,\n):\n    \"\"\"Initialize a component to visualize a model.\n    Args:\n        model_class: class of the model to instantiate\n        model_params: parameters for initializing the model\n        measures: list of callables or data attributes to plot\n        name: name for display\n        agent_portrayal: options for rendering agents (dictionary)\n        space_drawer: method to render the agent space for\n            the model; default implementation is :meth:`make_space`;\n            simulations with no space to visualize should\n            specify `space_drawer=False`\n        play_interval: play interval (default: 400)\n    \"\"\"\n\n    current_step, set_current_step = solara.use_state(0)\n\n    # 1. Set up model parameters\n    user_params, fixed_params = split_model_params(model_params)\n    model_parameters, set_model_parameters = solara.use_state(\n        {**fixed_params, **{k: v[\"value\"] for k, v in user_params.items()}}\n    )\n\n    # 2. Set up Model\n    def make_model():\n        model = model_class(**model_parameters)\n        set_current_step(0)\n        return model\n\n    reset_counter = solara.use_reactive(0)\n    model = solara.use_memo(\n        make_model, dependencies=[*list(model_parameters.values()), reset_counter.value]\n    )\n\n    def handle_change_model_params(name: str, value: any):\n        set_model_parameters({**model_parameters, name: value})\n\n    # 3. Set up UI\n    solara.Markdown(name)\n    UserInputs(user_params, on_change=handle_change_model_params)\n    ModelController(model, play_interval, current_step, set_current_step, reset_counter)\n\n    with solara.GridFixed(columns=2):\n        # 4. Space\n        if space_drawer == \"default\":\n            # draw with the default implementation\n            make_space(model, agent_portrayal)\n        elif space_drawer:\n            # if specified, draw agent space with an alternate renderer\n            space_drawer(model, agent_portrayal)\n        # otherwise, do nothing (do not draw space)\n\n        # 5. Plots\n        for measure in measures:\n            if callable(measure):\n                # Is a custom object\n                measure(model)\n            else:\n                make_plot(model, measure)\n\n\n@solara.component\ndef ModelController(\n    model, play_interval, current_step, set_current_step, reset_counter\n):\n    playing = solara.use_reactive(False)\n    thread = solara.use_reactive(None)\n    # We track the previous step to detect if user resets the model via\n    # clicking the reset button or changing the parameters. If previous_step >\n    # current_step, it means a model reset happens while the simulation is\n    # still playing.\n    previous_step = solara.use_reactive(0)\n\n    def on_value_play(change):\n        if previous_step.value > current_step and current_step == 0:\n            # We add extra checks for current_step == 0, just to be sure.\n            # We automatically stop the playing if a model is reset.\n            playing.value = False\n        elif model.running:\n            do_step()\n        else:\n            playing.value = False\n\n    def do_step():\n        model.step()\n        previous_step.value = current_step\n        set_current_step(model.schedule.steps)\n\n    def do_play():\n        model.running = True\n        while model.running:\n            do_step()\n\n    def threaded_do_play():\n        if thread is not None and thread.is_alive():\n            return\n        thread.value = threading.Thread(target=do_play)\n        thread.start()\n\n    def do_pause():\n        if (thread is None) or (not thread.is_alive()):\n            return\n        model.running = False\n        thread.join()\n\n    def do_reset():\n        reset_counter.value += 1\n\n    with solara.Row():\n        solara.Button(label=\"Step\", color=\"primary\", on_click=do_step)\n        # This style is necessary so that the play widget has almost the same\n        # height as typical Solara buttons.\n        solara.Style(\n            \"\"\"\n        .widget-play {\n            height: 30px;\n        }\n        \"\"\"\n        )\n        widgets.Play(\n            value=0,\n            interval=play_interval,\n            repeat=True,\n            show_repeat=False,\n            on_value=on_value_play,\n            playing=playing.value,\n            on_playing=playing.set,\n        )\n        solara.Button(label=\"Reset\", color=\"primary\", on_click=do_reset)\n        solara.Markdown(md_text=f\"**Step:** {current_step}\")\n        # threaded_do_play is not used for now because it\n        # doesn't work in Google colab. We use\n        # ipywidgets.Play until it is fixed. The threading\n        # version is definite a much better implementation,\n        # if it works.\n        # solara.Button(label=\"\u25b6\", color=\"primary\", on_click=viz.threaded_do_play)\n        # solara.Button(label=\"\u23f8\ufe0e\", color=\"primary\", on_click=viz.do_pause)\n        # solara.Button(label=\"Reset\", color=\"primary\", on_click=do_reset)\n\n\ndef split_model_params(model_params):\n    model_params_input = {}\n    model_params_fixed = {}\n    for k, v in model_params.items():\n        if check_param_is_fixed(v):\n            model_params_fixed[k] = v\n        else:\n            model_params_input[k] = v\n    return model_params_input, model_params_fixed\n\n\ndef check_param_is_fixed(param):\n    if not isinstance(param, dict):\n        return True\n    if \"type\" not in param:\n        return True\n\n\n@solara.component\ndef UserInputs(user_params, on_change=None):\n    \"\"\"Initialize user inputs for configurable model parameters.\n    Currently supports :class:`solara.SliderInt`, :class:`solara.SliderFloat`,\n    and :class:`solara.Select`.\n\n    Props:\n        user_params: dictionary with options for the input, including label,\n        min and max values, and other fields specific to the input type.\n        on_change: function to be called with (name, value) when the value of an input changes.\n    \"\"\"\n\n    for name, options in user_params.items():\n        # label for the input is \"label\" from options or name\n        label = options.get(\"label\", name)\n        input_type = options.get(\"type\")\n\n        def change_handler(value, name=name):\n            on_change(name, value)\n\n        if input_type == \"SliderInt\":\n            solara.SliderInt(\n                label,\n                value=options.get(\"value\"),\n                on_value=change_handler,\n                min=options.get(\"min\"),\n                max=options.get(\"max\"),\n                step=options.get(\"step\"),\n            )\n        elif input_type == \"SliderFloat\":\n            solara.SliderFloat(\n                label,\n                value=options.get(\"value\"),\n                on_value=change_handler,\n                min=options.get(\"min\"),\n                max=options.get(\"max\"),\n                step=options.get(\"step\"),\n            )\n        elif input_type == \"Select\":\n            solara.Select(\n                label,\n                value=options.get(\"value\"),\n                on_value=change_handler,\n                values=options.get(\"values\"),\n            )\n        else:\n            raise ValueError(f\"{input_type} is not a supported input type\")\n\n\ndef make_space(model, agent_portrayal):\n    def portray(g):\n        x = []\n        y = []\n        s = []  # size\n        c = []  # color\n        for i in range(g.width):\n            for j in range(g.height):\n                content = g._grid[i][j]\n                if not content:\n                    continue\n                if not hasattr(content, \"__iter__\"):\n                    # Is a single grid\n                    content = [content]\n                for agent in content:\n                    data = agent_portrayal(agent)\n                    x.append(i)\n                    y.append(j)\n                    if \"size\" in data:\n                        s.append(data[\"size\"])\n                    if \"color\" in data:\n                        c.append(data[\"color\"])\n        out = {\"x\": x, \"y\": y}\n        if len(s) > 0:\n            out[\"s\"] = s\n        if len(c) > 0:\n            out[\"c\"] = c\n        return out\n\n    space_fig = Figure()\n    space_ax = space_fig.subplots()\n    if isinstance(model.grid, mesa.space.NetworkGrid):\n        _draw_network_grid(model, space_ax, agent_portrayal)\n    else:\n        space_ax.scatter(**portray(model.grid))\n    space_ax.set_axis_off()\n    solara.FigureMatplotlib(space_fig)\n\n\ndef _draw_network_grid(model, space_ax, agent_portrayal):\n    graph = model.grid.G\n    pos = nx.spring_layout(graph, seed=0)\n    nx.draw(\n        graph,\n        ax=space_ax,\n        pos=pos,\n        **agent_portrayal(graph),\n    )\n\n\ndef make_plot(model, measure):\n    fig = Figure()\n    ax = fig.subplots()\n    df = model.datacollector.get_model_vars_dataframe()\n    ax.plot(df.loc[:, measure])\n    ax.set_ylabel(measure)\n    # Set integer x axis\n    ax.xaxis.set_major_locator(MaxNLocator(integer=True))\n    solara.FigureMatplotlib(fig)\n\n\ndef make_text(renderer):\n    def function(model):\n        solara.Markdown(renderer(model))\n\n    return function\n", "path": "mesa/experimental/jupyter_viz.py"}]}