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 ba7c882d1..c791af081 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,12 +324,37 @@ 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.widget_min_frequency = BoundedFloatText( + value=10, + min=0.1, + max=1000, + description='Min Spectral Frequency (Hz):', + disabled=False, + layout=layout, + style=analysis_style) + + self.widget_max_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 + '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 @@ -374,16 +399,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']) @@ -405,7 +434,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( @@ -543,9 +572,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): @@ -576,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, @@ -677,11 +708,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"
- Receptor: {conn_data[receptor_name]['receptor']}
"""), - w_text_input, HTML(value="{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 +1737,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