From 7b6443624f436498e022147105c8b98f2243c232 Mon Sep 17 00:00:00 2001 From: dylansdaniels-berkeley Date: Fri, 22 Nov 2024 18:25:09 -0500 Subject: [PATCH 1/8] visual updates to gui, including placeholders for default values for min/max spectral frequency --- hnn_core/gui/gui.py | 110 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 99 insertions(+), 11 deletions(-) diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index ba7c882d1..96aa4f292 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -23,7 +23,7 @@ from ipywidgets import (HTML, Accordion, AppLayout, BoundedFloatText, BoundedIntText, Button, Dropdown, FileUpload, VBox, HBox, IntText, Layout, Output, RadioButtons, Tab, Text, - Checkbox) + Checkbox, Box) from ipywidgets.embed import embed_minimal_html import hnn_core from hnn_core import JoblibBackend, MPIBackend, simulate_dipole @@ -290,7 +290,7 @@ def __init__(self, theme_color="#802989", height=f"{operation_box_height}px", flex_wrap="wrap", ), - "config_box": Layout(width=f"{left_sidebar_width}px", + "config_box": Layout(width=f"{left_sidebar_width-40}px", height=f"{config_box_height - 100}px"), "drive_widget": Layout(width="auto"), "drive_textbox": Layout(width='270px', height='auto'), @@ -324,9 +324,32 @@ def __init__(self, theme_color="#802989", self.simulation_data = defaultdict(lambda: dict(net=None, dpls=list())) # Default visualization params for figures + analysis_style = {'description_width': '200px'} + layout = Layout(width="300px") + self.widget_default_smoothing = BoundedFloatText( value=30.0, description='Smoothing:', - min=0.0, max=100.0, step=1.0, disabled=False) + min=0.0, max=100.0, step=1.0, disabled=False, + layout=layout, style=analysis_style, + ) + + self.min_spectral_frequency = BoundedFloatText( + value=10, + min=0.1, + max=1000, + description='Min Spectral Frequency (Hz):', + disabled=False, + layout=layout, + style=analysis_style) + + self.max_spectral_frequency = BoundedFloatText( + value=100, + min=0.1, + max=1000, + description='Max Spectral Frequency (Hz):', + disabled=False, + layout=layout, + style=analysis_style) self.fig_default_params = { 'default_smoothing': self.widget_default_smoothing.value @@ -677,11 +700,31 @@ def compose(self, return_layout=True): If the method returns the layout object which can be rendered by IPython.display.display() method. """ + box_style = """ + style=" + background: gray; + color: white; + # font-weight: bold; + width: 290px; + padding: 0px 5px; + margin-bottom: 2px; + " + """ simulation_box = VBox([ + HTML(f"
Simulation Parameters
"), VBox([ self.widget_simulation_name, self.widget_tstop, self.widget_dt, - self.widget_ntrials, self.widget_default_smoothing, + self.widget_ntrials, self.widget_backend_selection, self._backend_config_out]), + Box(layout=Layout(height="20px")), + HTML( + f"
Default Visualization Parameters
", + ), + VBox([ + self.widget_default_smoothing, + self.min_spectral_frequency, + self.max_spectral_frequency, + ]) ], layout=self.layout['config_box']) connectivity_configuration = Tab() @@ -1157,21 +1200,35 @@ def create_expanded_button(description, button_style, layout, disabled=False, def _get_connectivity_widgets(conn_data): """Create connectivity box widgets from specified weight and probability""" - style = {'description_width': '150px'} - style = {} + style = {'description_width': '100px'} sliders = list() for receptor_name in conn_data.keys(): w_text_input = BoundedFloatText( value=conn_data[receptor_name]['weight'], disabled=False, continuous_update=False, min=0, max=1e6, step=0.01, - description="weight", style=style) + description="Weight:", style=style) + + display_name = conn_data[receptor_name]['receptor'].upper() + + map_display_names = { + 'GABAA': 'GABAA', + 'GABAB': 'GABAB', + } + + if display_name in map_display_names: + display_name = map_display_names[display_name] + + html_tab = ' ' conn_widget = VBox([ - HTML(value=f"""

- Receptor: {conn_data[receptor_name]['receptor']}

"""), - w_text_input, HTML(value="
") + HTML(value=f"""

{html_tab}{html_tab} + Receptor: {display_name}

"""), + w_text_input ]) + # Add class to child Vboxes for targeted CSS + conn_widget.add_class('connectivity-subsection') + conn_widget._belongsto = { "receptor": conn_data[receptor_name]['receptor'], "location": conn_data[receptor_name]['location'], @@ -1672,13 +1729,44 @@ def add_network_connectivity_tab(net, connectivity_out, connectivity_textfields.append( _get_connectivity_widgets(receptor_related_conn)) + # Style the contents of the Connectivity Tab + # ------------------------------------------------------------------------- + + # define custom Vbox layout + # no_padding_layout = Layout(padding="0", margin="0") # unused + + # Initialize sections within the Accordion + connectivity_boxes = [VBox(slider) for slider in connectivity_textfields] + + # Add class to child Vboxes for targeted CSS + for box in connectivity_boxes: + box.add_class("connectivity-contents") + + # Initialize the Accordion section + cell_connectivity = Accordion(children=connectivity_boxes) + + # Add class to Accordion section for targeted CSS + cell_connectivity.add_class("connectivity-section") + for idx, connectivity_name in enumerate(connectivity_names): cell_connectivity.set_title(idx, connectivity_name) + # Style the
automatically creted around connectivity boxes + connectivity_out_style = HTML(""" + + """) + + # Display the Accordion with styling with connectivity_out: - display(cell_connectivity) + display(connectivity_out_style, cell_connectivity) return net From c181d875b49edadf2b910502762bebf6fcaf41f6 Mon Sep 17 00:00:00 2001 From: dylansdaniels-berkeley Date: Mon, 25 Nov 2024 14:17:49 -0500 Subject: [PATCH 2/8] update visuals for adding a drive --- hnn_core/gui/gui.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index 96aa4f292..f546ec4c9 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -397,16 +397,20 @@ def __init__(self, theme_color="#802989", description='', layout={'width': '15%'}) # Drive selection - self.widget_drive_type_selection = RadioButtons( + self.widget_drive_type_selection = Dropdown( options=['Evoked', 'Poisson', 'Rhythmic', 'Tonic'], value='Evoked', - description='Drive:', + description='Drive type:', disabled=False, - layout=self.layout['drive_widget']) - self.widget_location_selection = RadioButtons( - options=['proximal', 'distal'], value='proximal', - description='Location', disabled=False, - layout=self.layout['drive_widget']) + layout=self.layout['drive_widget'], + style={'description_width': '100px'} + ) + self.widget_location_selection = Dropdown( + options=['Proximal', 'Distal'], value='Proximal', + description='Drive location:', disabled=False, + layout=self.layout['drive_widget'], + style={'description_width': '100px'}, + ) self.add_drive_button = create_expanded_button( 'Add drive', 'primary', layout=self.layout['btn'], button_color=self.layout['theme_color']) @@ -428,7 +432,7 @@ def __init__(self, theme_color="#802989", button_style='success') self.delete_drive_button = create_expanded_button( - 'Delete drives', 'success', layout=self.layout['btn'], + 'Delete all drives', 'success', layout=self.layout['btn'], button_color=self.layout['theme_color']) self.cell_type_radio_buttons = RadioButtons( @@ -566,9 +570,10 @@ def _handle_backend_change(backend_type): self.widget_n_jobs) def _add_drive_button_clicked(b): + location = self.widget_location_selection.value.lower() return self.add_drive_widget( self.widget_drive_type_selection.value, - self.widget_location_selection.value, + location, ) def _delete_drives_clicked(b): From 8d2fcca848b46768af582873426689dbcf1b0574 Mon Sep 17 00:00:00 2001 From: dylansdaniels-berkeley Date: Mon, 25 Nov 2024 17:16:13 -0500 Subject: [PATCH 3/8] make min/max spectral widgets functional --- hnn_core/gui/_viz_manager.py | 14 ++++++++------ hnn_core/gui/gui.py | 24 +++++++++++++++--------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/hnn_core/gui/_viz_manager.py b/hnn_core/gui/_viz_manager.py index bd0d71792..2c741f18d 100644 --- a/hnn_core/gui/_viz_manager.py +++ b/hnn_core/gui/_viz_manager.py @@ -555,6 +555,8 @@ def _get_ax_control(widgets, data, fig_default_params, fig_idx, fig, ax): simulation_names = tuple(data['simulations'].keys()) sim_index = 0 default_smoothing = fig_default_params['default_smoothing'] + default_min_frequency = fig_default_params['default_min_frequency'] + default_max_frequency = fig_default_params['default_max_frequency'] if not simulation_names: simulation_names = ("None",) else: @@ -639,7 +641,7 @@ def _get_ax_control(widgets, data, fig_default_params, fig_idx, fig, ax): style=analysis_style) min_spectral_frequency = BoundedFloatText( - value=10, + value=default_min_frequency, min=0.1, max=1000, description='Min Spectral Frequency (Hz):', @@ -648,7 +650,7 @@ def _get_ax_control(widgets, data, fig_default_params, fig_idx, fig, ax): style=analysis_style) max_spectral_frequency = BoundedFloatText( - value=100, + value=default_max_frequency, min=0.1, max=1000, description='Max Spectral Frequency (Hz):', @@ -762,12 +764,12 @@ def _close_figure(b, widgets, data, fig_idx): display(Label(_fig_placeholder)) -def _add_axes_controls(widgets, data, fig_default_smoothing, fig, axd): +def _add_axes_controls(widgets, data, fig_default_params, fig, axd): fig_idx = data['fig_idx']['idx'] controls = Tab() children = [ - _get_ax_control(widgets, data, fig_default_smoothing, fig_idx=fig_idx, + _get_ax_control(widgets, data, fig_default_params, fig_idx=fig_idx, fig=fig, ax=ax) for ax_key, ax in axd.items() ] @@ -788,7 +790,7 @@ def _add_axes_controls(widgets, data, fig_default_smoothing, fig, axd): widgets['axes_config_tabs'].set_title(n_tabs, _idx2figname(fig_idx)) -def _add_figure(b, widgets, data, fig_default_smoothing, +def _add_figure(b, widgets, data, fig_default_params, template_type, scale=0.95, dpi=96): fig_idx = data['fig_idx']['idx'] viz_output_layout = data['visualization_output'] @@ -821,7 +823,7 @@ def _add_figure(b, widgets, data, fig_default_smoothing, else: display(fig.canvas) - _add_axes_controls(widgets, data, fig_default_smoothing, fig=fig, axd=axd) + _add_axes_controls(widgets, data, fig_default_params, fig=fig, axd=axd) data['figs'][fig_idx] = fig widgets['figs_tabs'].selected_index = n_tabs diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index f546ec4c9..af7ae727a 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -333,7 +333,7 @@ def __init__(self, theme_color="#802989", layout=layout, style=analysis_style, ) - self.min_spectral_frequency = BoundedFloatText( + self.widget_min_frequency = BoundedFloatText( value=10, min=0.1, max=1000, @@ -342,7 +342,7 @@ def __init__(self, theme_color="#802989", layout=layout, style=analysis_style) - self.max_spectral_frequency = BoundedFloatText( + self.widget_max_frequency = BoundedFloatText( value=100, min=0.1, max=1000, @@ -352,7 +352,9 @@ def __init__(self, theme_color="#802989", style=analysis_style) self.fig_default_params = { - 'default_smoothing': self.widget_default_smoothing.value + 'default_smoothing': self.widget_default_smoothing.value, + 'default_min_frequency': self.widget_min_frequency.value, + 'default_max_frequency': self.widget_max_frequency.value, } # Simulation parameters @@ -604,6 +606,7 @@ def _run_button_clicked(b): self.widget_simulation_name, self._log_out, self.drive_widgets, self.data, self.widget_dt, self.widget_tstop, self.fig_default_params, self.widget_default_smoothing, + self.widget_min_frequency, self.widget_max_frequency, self.widget_ntrials, self.widget_backend_selection, self.widget_mpi_cmd, self.widget_n_jobs, self.params, self._simulation_status_bar, self._simulation_status_contents, @@ -727,8 +730,8 @@ def compose(self, return_layout=True): ), VBox([ self.widget_default_smoothing, - self.min_spectral_frequency, - self.max_spectral_frequency, + self.widget_min_frequency, + self.widget_max_frequency, ]) ], layout=self.layout['config_box']) @@ -2016,6 +2019,7 @@ def _init_network_from_widgets(params, dt, tstop, single_simulation_data, def run_button_clicked(widget_simulation_name, log_out, drive_widgets, all_data, dt, tstop, fig_default_params, widget_default_smoothing, + widget_min_frequency, widget_max_frequency, ntrials, backend_selection, mpi_cmd, n_jobs, params, simulation_status_bar, simulation_status_contents, connectivity_textfields, @@ -2067,12 +2071,14 @@ def run_button_clicked(widget_simulation_name, log_out, drive_widgets, viz_manager.reset_fig_config_tabs() - # update default_smoothing in gui based on widget + # update default visualization params in gui based on widget fig_default_params['default_smoothing'] = widget_default_smoothing.value + fig_default_params['default_min_frequency'] = widget_min_frequency.value + fig_default_params['default_max_frequency'] = widget_max_frequency.value - # change default smoothing in viz_manager to mirror gui - new_default_smoothing = fig_default_params['default_smoothing'] - viz_manager.fig_default_params['default_smoothing'] = new_default_smoothing + # change default visualization params in viz_manager to mirror gui + for widget, value in fig_default_params.items(): + viz_manager.fig_default_params[widget] = value viz_manager.add_figure() fig_name = _idx2figname(viz_manager.data['fig_idx']['idx'] - 1) From ba5d2a3a04091231f99a94b25555cae8be41523b Mon Sep 17 00:00:00 2001 From: dylansdaniels-berkeley Date: Mon, 25 Nov 2024 17:17:06 -0500 Subject: [PATCH 4/8] fix type in comment --- hnn_core/gui/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index af7ae727a..857979ef4 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -1761,7 +1761,7 @@ def add_network_connectivity_tab(net, connectivity_out, for idx, connectivity_name in enumerate(connectivity_names): cell_connectivity.set_title(idx, connectivity_name) - # Style the
automatically creted around connectivity boxes + # Style the
automatically created around connectivity boxes connectivity_out_style = HTML("""