From 66ae18842ddd74c977678eec24b995fe865559d9 Mon Sep 17 00:00:00 2001 From: Neil Wilson Date: Sat, 4 Jul 2020 12:47:54 +0100 Subject: [PATCH] Move model instantiation to Socket Handler This allows multiple tab to be opened onto the same webserver, with each tab running their own model. Fixes #856 --- mesa/visualization/ModularVisualization.py | 102 +++++++++++---------- 1 file changed, 55 insertions(+), 47 deletions(-) diff --git a/mesa/visualization/ModularVisualization.py b/mesa/visualization/ModularVisualization.py index a66f845d01b..e6440baf3fe 100644 --- a/mesa/visualization/ModularVisualization.py +++ b/mesa/visualization/ModularVisualization.py @@ -179,21 +179,66 @@ def get(self): class SocketHandler(tornado.websocket.WebSocketHandler): - """ Handler for websocket. """ + """ + Handler for websocket. + + Each websocket creates a new instance of the model and renders its + output to the page. This allows you to have multiple tabs open to + the same server, each running its own version of the model. + """ def open(self): + self.model_kwargs = self.application.model_kwargs.copy() + self.reset_model() if self.application.verbose: print("Socket opened!") + print("Model ID:", id(self.model)) self.write_message( - {"type": "model_params", "params": self.application.user_params} + {"type": "model_params", "params": self.user_params} ) def check_origin(self, origin): return True + @property + def user_params(self): + result = {} + for param, val in self.model_kwargs.items(): + if isinstance(val, UserSettableParameter): + result[param] = val.json + + return result + + def reset_model(self): + """ Reinstantiate the model object, using the current parameters. """ + + model_params = {} + for key, val in self.model_kwargs.items(): + if isinstance(val, UserSettableParameter): + if ( + val.param_type == "static_text" + ): # static_text is never used for setting params + continue + model_params[key] = val.value + else: + model_params[key] = val + + self.model = self.application.model_cls(**model_params) + + def render_model(self): + """ Turn the current state of the model into a dictionary of + visualizations + + """ + visualization_state = [] + for element in self.application.visualization_elements: + element_state = element.render(self.model) + visualization_state.append(element_state) + return visualization_state + @property def viz_state_message(self): - return {"type": "viz_state", "data": self.application.render_model()} + return {"type": "viz_state", "data": self.render_model()} def on_message(self, message): """ Receiving a message from the websocket, parse, and act accordingly. @@ -204,14 +249,14 @@ def on_message(self, message): msg = tornado.escape.json_decode(message) if msg["type"] == "get_step": - if not self.application.model.running: + if not self.model.running: self.write_message({"type": "end"}) else: - self.application.model.step() + self.model.step() self.write_message(self.viz_state_message) elif msg["type"] == "reset": - self.application.reset_model() + self.reset_model() self.write_message(self.viz_state_message) elif msg["type"] == "submit_params": @@ -219,13 +264,13 @@ def on_message(self, message): value = msg["value"] # Is the param editable? - if param in self.application.user_params: + if param in self.user_params: if isinstance( - self.application.model_kwargs[param], UserSettableParameter + self.model_kwargs[param], UserSettableParameter ): - self.application.model_kwargs[param].value = value + self.model_kwargs[param].value = value else: - self.application.model_kwargs[param] = value + self.model_kwargs[param] = value else: if self.application.verbose: @@ -299,47 +344,10 @@ def __init__( self.description = model_cls.__doc__ self.model_kwargs = model_params - self.reset_model() # Initializing the application itself: super().__init__(self.handlers, **self.settings) - @property - def user_params(self): - result = {} - for param, val in self.model_kwargs.items(): - if isinstance(val, UserSettableParameter): - result[param] = val.json - - return result - - def reset_model(self): - """ Reinstantiate the model object, using the current parameters. """ - - model_params = {} - for key, val in self.model_kwargs.items(): - if isinstance(val, UserSettableParameter): - if ( - val.param_type == "static_text" - ): # static_text is never used for setting params - continue - model_params[key] = val.value - else: - model_params[key] = val - - self.model = self.model_cls(**model_params) - - def render_model(self): - """ Turn the current state of the model into a dictionary of - visualizations - - """ - visualization_state = [] - for element in self.visualization_elements: - element_state = element.render(self.model) - visualization_state.append(element_state) - return visualization_state - def launch(self, port=None, open_browser=True): """ Run the app. """ if port is not None: