diff --git a/mdadash/backend/analyses/com_distance.py b/mdadash/backend/analyses/com_distance.py index 68cb2f1..8b63b1e 100644 --- a/mdadash/backend/analyses/com_distance.py +++ b/mdadash/backend/analyses/com_distance.py @@ -39,13 +39,13 @@ class COMDistance(WidgetBase): "attribute": "periodic", "name": "Periodic", "description": "Select with periodic boundary conditions", - "type": "switch", + "type": "bool", }, { "attribute": "updating", "name": "Updating", "description": "Update selection during each timestep", - "type": "switch", + "type": "bool", }, { "attribute": "custom_title", @@ -63,12 +63,12 @@ class COMDistance(WidgetBase): "attribute": "max_distance", "name": "Max distance", "description": "Max distance for alert check", - "type": "int", + "type": "float", }, { "attribute": "max_distance_alert", "name": "Alert if distance > 'Max distance'", - "type": "switch", + "type": "bool", }, { "attribute": "x_type", @@ -83,7 +83,8 @@ class COMDistance(WidgetBase): def __init__(self): super().__init__() - self.maxlen = 100 + self.default_maxlen = 100 + self.maxlen = self.default_maxlen self.steps = deque(maxlen=self.maxlen) self.times = deque(maxlen=self.maxlen) self.y_values = deque(maxlen=self.maxlen) @@ -95,7 +96,7 @@ def __init__(self): self.ag2 = None self.title = "Distance between COMs" self.custom_title = None - self.max_distance = 5 + self.max_distance = 5.0 self.max_distance_alert = False self.x_type = "step" self.x_values = self.steps @@ -130,6 +131,8 @@ def on_input_change(self, attribute, _old_value, new_value): """on_input_change handler""" reset_plot = False if attribute == "maxlen": + if new_value < 0: + self.maxlen = self.default_maxlen reset_plot = True elif attribute == "x_type": self._set_x_values() diff --git a/mdadash/backend/analyses/energies.py b/mdadash/backend/analyses/energies.py index bc71130..71177d3 100644 --- a/mdadash/backend/analyses/energies.py +++ b/mdadash/backend/analyses/energies.py @@ -43,7 +43,8 @@ class EnergyWidgetBase: def __init__(self): super().__init__() self.title = self.name - self.maxlen = 100 + self.default_maxlen = 100 + self.maxlen = self.default_maxlen self.steps = deque(maxlen=self.maxlen) self.times = deque(maxlen=self.maxlen) self.y_values = deque(maxlen=self.maxlen) @@ -63,9 +64,11 @@ def _set_x_values(self): def on_input_change(self, attribute, _old_value, new_value): """on_input_change handler""" if attribute == "maxlen": - self.steps = deque(maxlen=new_value) - self.times = deque(maxlen=new_value) - self.y_values = deque(maxlen=new_value) + if new_value < 0: + self.maxlen = self.default_maxlen + self.steps = deque(maxlen=self.maxlen) + self.times = deque(maxlen=self.maxlen) + self.y_values = deque(maxlen=self.maxlen) self._set_x_values() elif attribute == "x_type": self._set_x_values() diff --git a/mdadash/backend/analyses/rog.py b/mdadash/backend/analyses/rog.py index b6f37bb..f9060e2 100644 --- a/mdadash/backend/analyses/rog.py +++ b/mdadash/backend/analyses/rog.py @@ -19,8 +19,18 @@ class ROG(WidgetBase): name = "ROG" description = "Radii of Gyration of a selection" + _analysis_mode = "per-frame" _inputs = [ + { + "attribute": "_analysis_mode", + "name": "Analysis mode", + "description": "The mode to run this analysis widget", + "type": "select", + "items": [ + "per-frame", + ], + }, { "attribute": "selection", "name": "Selection", @@ -32,13 +42,13 @@ class ROG(WidgetBase): "attribute": "periodic", "name": "Periodic", "description": "Select with periodic boundary conditions", - "type": "switch", + "type": "bool", }, { "attribute": "updating", "name": "Updating", "description": "Update selection during each timestep", - "type": "switch", + "type": "bool", }, { "attribute": "custom_title", @@ -65,7 +75,8 @@ class ROG(WidgetBase): def __init__(self): super().__init__() - self.maxlen = 100 + self.default_maxlen = 100 + self.maxlen = self.default_maxlen self.steps = deque(maxlen=self.maxlen) self.times = deque(maxlen=self.maxlen) self.y_values = deque(maxlen=self.maxlen) @@ -99,10 +110,12 @@ def post_connect(self): """post_connect handler""" self._update_selection() - def on_input_change(self, attribute, _old_value, _new_value): + def on_input_change(self, attribute, _old_value, new_value): """on_input_change handler""" reset_plot = False if attribute == "maxlen": + if new_value < 0: + self.maxlen = self.default_maxlen reset_plot = True elif attribute == "x_type": self._set_x_values() diff --git a/mdadash/backend/tests/test_server.py b/mdadash/backend/tests/test_server.py index aec0a1d..884a996 100644 --- a/mdadash/backend/tests/test_server.py +++ b/mdadash/backend/tests/test_server.py @@ -314,7 +314,7 @@ async def test_widget_runs(_client, imd_server): assert uuid is not None # test input changes inputs = [ - ("maxlen", 10), + ("maxlen", -1), ("x_type", "time"), ] await _test_input_changes(uuid, inputs) @@ -332,7 +332,7 @@ async def test_widget_runs(_client, imd_server): maxlen = next( (i for i in response["inputs"] if i.get("attribute") == "maxlen"), None ) - assert maxlen["value"] == 10 + assert maxlen["value"] == 100 # connect to simulation # widgets can be added even when the dashboard is not connected @@ -379,7 +379,7 @@ async def test_widget_runs(_client, imd_server): # test input changes inputs = [ ("selection", "protein"), - ("maxlen", 10), + ("maxlen", -1), ("x_type", "time"), ("updating", True), ] @@ -395,7 +395,7 @@ async def test_widget_runs(_client, imd_server): inputs = [ ("selection1", "resid 1"), ("selection2", "resid 2"), - ("maxlen", 10), + ("maxlen", -1), ("x_type", "time"), ("updating", True), ] diff --git a/mdadash/backend/widgets/base.py b/mdadash/backend/widgets/base.py index 9a15a2e..018300e 100644 --- a/mdadash/backend/widgets/base.py +++ b/mdadash/backend/widgets/base.py @@ -313,7 +313,9 @@ def set_input(self, uuid: str, attribute: str, value: Any) -> bool: """ widget = self.instances[uuid] old_value = getattr(widget, attribute, value) - setattr(widget, attribute, value) + old_type = type(old_value) + # set input using the same existing type + setattr(widget, attribute, old_type(value)) try: widget.on_input_change(attribute, old_value, value) widget._set_input_state(attribute) diff --git a/mdadash/frontend/src/__tests__/views/WidgetView.spec.js b/mdadash/frontend/src/__tests__/views/WidgetView.spec.js index 31c5590..2842f2c 100644 --- a/mdadash/frontend/src/__tests__/views/WidgetView.spec.js +++ b/mdadash/frontend/src/__tests__/views/WidgetView.spec.js @@ -59,6 +59,13 @@ const widgetDetails = { ], value: 'step', }, + { + attribute: '_analysis_mode', + name: 'Analysis mode', + description: 'The mode to run this analysis widget', + type: 'select', + items: ['per-frame', 'batch'], + }, ], } @@ -193,6 +200,9 @@ describe('WidgetView.vue', () => { attribute: 'x_type', value: 'step', }) + // check select from dropdown + const mode = wrapper.findComponent('[data-attribute="_analysis_mode"]') + await mode.vm.$emit('update:modelValue', 'batch') // check invalid input update const maxlen = wrapper.find('[data-attribute="maxlen"]') maxlen.find('input').setValue(100) diff --git a/mdadash/frontend/src/views/SettingsView.vue b/mdadash/frontend/src/views/SettingsView.vue index cf47b80..e772cb3 100644 --- a/mdadash/frontend/src/views/SettingsView.vue +++ b/mdadash/frontend/src/views/SettingsView.vue @@ -104,7 +104,7 @@ label="buffer_size" variant="outlined" v-model="settings.universe_configs[0].buffer_size" - control-variant="default" + control-variant="hidden" hint="IMDFrameBuffer will be filled with as many IMDFrame fit in buffer_size bytes [10MB]" persistent-hint > @@ -117,6 +117,9 @@ control-variant="default" hint="Timeout for the socket in seconds [5]" persistent-hint + :min="0" + :max="30" + :step="1" > diff --git a/mdadash/frontend/src/views/WidgetView.vue b/mdadash/frontend/src/views/WidgetView.vue index 34584c8..8a4be8e 100644 --- a/mdadash/frontend/src/views/WidgetView.vue +++ b/mdadash/frontend/src/views/WidgetView.vue @@ -61,8 +61,10 @@ @change="(e) => handleInputChange(input)" @blur="input.error && handleInputChange(input)" @click="input.type === 'toggle' && handleInputChange(input)" + @update:model-value="input.type === 'select' && handleInputChange(input)" :rules="addRules(input.validations)" :error-messages="input.error" + :items="input.items || []" >