diff --git a/src/e3sm_compareview/app.py b/src/e3sm_compareview/app.py index 85a370c..d587103 100644 --- a/src/e3sm_compareview/app.py +++ b/src/e3sm_compareview/app.py @@ -181,6 +181,9 @@ def _tauri_show(self, **_): # ------------------------------------------------------------------------- def _build_ui(self, **_): + if self.server.hot_reload: + toolbars.reload(toolbars) + with VAppLayout(self.server, fill_height=True) as self.ui: # Keyboard shortcut with tw.MouseTrap( @@ -672,8 +675,11 @@ async def _import_state(self, state_content): array_name = view_state.get("array_name", var_name) view_spec = next( - (c for c in self.view_manager.get_view_specs(var_name) - if c["array_name"] == array_name), + ( + c + for c in self.view_manager.get_view_specs(var_name) + if c["array_name"] == array_name + ), None, ) view = self.view_manager.get_view(view_spec or array_name, view_type) @@ -694,9 +700,7 @@ async def _import_state(self, state_content): self.state.aspect_ratio = layout["aspect-ratio"] self.state.active_layout = layout["active"] self.state.active_tools = [ - tool - for tool in layout["tools"] - if tool != "comparison-controls" + tool for tool in layout["tools"] if tool != "comparison-controls" ] self.state.compact_drawer = not layout["help"] diff --git a/src/e3sm_compareview/components/doc.py b/src/e3sm_compareview/components/doc.py index f9a1274..3b67f59 100644 --- a/src/e3sm_compareview/components/doc.py +++ b/src/e3sm_compareview/components/doc.py @@ -38,13 +38,13 @@ def __init__(self): QuickCompare contrasts two or more simulations by displaying the physical quantities as well as their differences between simulations. A detailed {Bold("User's Guide")} can be found through - {Link("this link","https://kitware.github.io/QuickView/guides/quickcompare/")}. + {Link("this link", "https://kitware.github.io/QuickView/guides/quickcompare/")}. {Bold("Bug reports and feature requests")} can be submitted through - {Link("GitHub","https://github.com/Kitware/E3SMQuickCompare/issues")}. + {Link("GitHub", "https://github.com/Kitware/E3SMQuickCompare/issues")}. """ ) -# v3.VImg(classes="rounded-lg", src=ASSETS.banner) + # v3.VImg(classes="rounded-lg", src=ASSETS.banner) Title("Toolbar Icons") @@ -187,16 +187,22 @@ def __init__(self): {Link("Pacific Northwest National Laboratory", "https://www.pnnl.gov/")} using funding from the U.S. Department of Energy's SciDAC program through a partnership between - the {Link("Advanced Scientific Computing Reaserch (ASCR)", - "https://www.energy.gov/science/ascr/advanced-scientific-computing-research")} program and - the {Link("Biological and Environmental Research (BER)", - "https://www.energy.gov/science/ber/biological-and-environmental-research")} program. - """ - ) + the { + Link( + "Advanced Scientific Computing Reaserch (ASCR)", + "https://www.energy.gov/science/ascr/advanced-scientific-computing-research", + ) + } program and + the { + Link( + "Biological and Environmental Research (BER)", + "https://www.energy.gov/science/ber/biological-and-environmental-research", + ) + } program. + """) Paragraph(f""" The development of QuickView used resources of the - {Link("National Energy Research Scientific Computing Center (NERSC)","https://www.nersc.gov/")}, + {Link("National Energy Research Scientific Computing Center (NERSC)", "https://www.nersc.gov/")}, a U.S. Department of Energy User Facility. - """ - ) + """) diff --git a/src/e3sm_compareview/components/toolbars.py b/src/e3sm_compareview/components/toolbars.py index 4d3904f..53adcd6 100644 --- a/src/e3sm_compareview/components/toolbars.py +++ b/src/e3sm_compareview/components/toolbars.py @@ -27,6 +27,7 @@ "classes": "border-b-thin", } + def to_kwargs(value): return { "v_show": js.is_active(value), @@ -35,6 +36,11 @@ def to_kwargs(value): } +def reload(m=None): + if m: + m.__loader__.exec_module(m) + + class Layout(v3.VToolbar): def __init__(self, apply_size=None, zoom=None, pan=None, reset_camera=None): super().__init__(**to_kwargs("adjust-layout")) @@ -394,8 +400,8 @@ def __init__(self): super().__init__(**to_kwargs("simulation-controls")) with self: - v3.VIcon("mdi-database-cog-outline", classes="pl-6 opacity-50") - with v3.VBtnGroup(classes="mx-3", density="compact"): + v3.VIcon("mdi-database-cog-outline", classes="px-6 opacity-50") + with v3.VBtnGroup(density="compact"): v3.VBtn( "Two Sim", variant="outlined", @@ -446,21 +452,18 @@ def __init__(self): ), item_title="title", item_value="value", - label="Comparison type", - chips=True, density="compact", - variant="solo", + variant="flat", hide_details=True, - classes="mx-1", - style="min-width: 14rem; max-width: 18rem;", + style="width: 16rem;max-width: 16rem;", ): with v3.Template(v_slot_selection="{ item }"): - with html.Div(classes="d-flex align-center py-1"): + with html.Div("Comparison:", classes="ml-n3"): v3.VChip( "{{ item.raw.title }}", size="small", - color="primary", variant="outlined", + color="primary", ) with v3.VSelect( v_else=True, @@ -479,46 +482,41 @@ def __init__(self): label="Comparison columns", multiple=True, density="compact", - variant="solo", + variant="plain", hide_details=True, - classes="mx-1", - style="min-width: 0; max-width: 19rem;", + style="width: 21rem;max-width: 21rem;", ): with v3.Template(v_slot_selection="{ item, index }"): - with html.Div( - v_if="index === 0", - classes="d-flex align-center flex-nowrap w-100 overflow-hidden pt-1", - style="max-width: 100%;", - ): - html.Div( - "{{ selected_columns.length === 1 ? item.raw.title : `${selected_columns.length} selected` }}", - classes="text-body-2 text-truncate", - ) - - v3.VSpacer() + v3.VChip( + "{{ item.raw.title }}", + size="small", + variant="outlined", + color="primary", + classes="", + ) + v3.VDivider(vertical=True, classes="mx-2") with v3.VSelect( v_model=("control_simulation_file", ""), items=("simulation_configs", []), item_title="label", item_value="path", - label="Choose ctrl", - chips=True, density="compact", - variant="solo", + variant="flat", hide_details=True, disabled=("simulation_configs.length === 0",), - classes="mx-1", - style="min-width: 14rem; max-width: 18rem;", ): with v3.Template(v_slot_selection="{ item }"): - with html.Div(classes="d-flex align-center py-1"): + with html.Div("Ctrl:", classes="ml-n3"): v3.VChip( "{{ item.raw.label || item.raw.path.split('/').pop() }}", size="small", - color="primary", variant="outlined", + color="primary", ) + v3.VDivider( + vertical=True, classes="mx-2", v_if="comparison_mode === 'two-sim'" + ) with v3.VSelect( v_if="comparison_mode === 'two-sim'", v_model=("two_sim_test_simulation_file", ""), @@ -527,26 +525,21 @@ def __init__(self): ), item_title="label", item_value="path", - label="Choose test", - chips=True, density="compact", - variant="solo", + variant="flat", hide_details=True, disabled=( "simulation_configs.filter(sim => sim.path !== control_simulation_file).length === 0", ), - classes="mx-1", - style="min-width: 14rem; max-width: 18rem;", ): with v3.Template(v_slot_selection="{ item }"): - with html.Div(classes="d-flex align-center py-1"): + with html.Div("Test:", classes="ml-n3"): v3.VChip( "{{ item.raw.label || item.raw.path.split('/').pop() }}", size="small", color="primary", variant="outlined", ) - v3.VBtn( "Organize simulation collection", v_if="comparison_mode === 'multi-sim'", @@ -570,7 +563,9 @@ def __init__(self): classes="border-b-thin", ): v3.VIcon("mdi-database-cog-outline", classes="ml-4 mr-2") - v3.VLabel("Organize simulation collection", classes="text-subtitle-2") + v3.VLabel( + "Organize simulation collection", classes="text-subtitle-2" + ) v3.VSpacer() with v3.VTooltip(): with v3.Template(v_slot_activator="{ props }"): @@ -609,97 +604,77 @@ def __init__(self): classes="simulation-entry-row pb-2 d-flex align-center", ): with html.Div( - classes="d-flex flex-column align-center justify-center mr-2 ga-1", + classes="d-flex flex-column align-center justify-space-between mr-2 ga-6", ): - with v3.VTooltip(text="Move up"): - with v3.Template(v_slot_activator="{ props }"): - v3.VBtn( - v_bind="props", - icon="mdi-chevron-up", - size="small", - variant="outlined", - density="comfortable", - color="primary", - style="min-width: 34px; width: 34px; height: 34px;", - disabled=("idx === 0",), - click=(self._move_simulation, "[idx, -1]"), - ) - with v3.VTooltip(text="Move down"): - with v3.Template(v_slot_activator="{ props }"): - v3.VBtn( - v_bind="props", - icon="mdi-chevron-down", - size="small", - variant="outlined", - density="comfortable", - color="primary", - style="min-width: 34px; width: 34px; height: 34px;", - disabled=("idx >= simulation_configs.length - 1",), - click=(self._move_simulation, "[idx, 1]"), - ) + v3.VBtn( + v_tooltip="'Move up'", + icon="mdi-chevron-up", + size="small", + variant="outlined", + density="comfortable", + classes="rounded", + disabled=("idx === 0",), + click=(self._move_simulation, "[idx, -1]"), + ) + v3.VBtn( + v_tooltip="'Move down'", + v_bind="props", + icon="mdi-chevron-down", + size="small", + variant="outlined", + density="comfortable", + classes="rounded", + disabled=("idx >= simulation_configs.length - 1",), + click=(self._move_simulation, "[idx, 1]"), + ) with v3.VCard( variant="outlined", classes="flex-grow-1", ): with v3.VCardText(classes="pa-3"): - with v3.VRow(dense=True, classes="align-center"): - with v3.VCol(cols=12, md=6): - v3.VTextField( - model_value=("entry.label",), - update_modelValue="utils.quickcompare.setSimulationLabel(entry.path, $event)", - label="Label", - density="compact", - variant="outlined", - hide_details=True, - ) - with v3.VCol(cols=6, md=3): - with v3.VTooltip( - text=( - "control_simulation_file === entry.path ? 'Current control simulation' : 'Set this simulation as control'", - ), - ): - with v3.Template( - v_slot_activator="{ props }" - ): - v3.VBtn( - v_bind="props", - text=( - "control_simulation_file === entry.path ? 'Control' : 'Set control'", - ), - variant="outlined", - color=( - "control_simulation_file === entry.path ? 'primary' : 'default'", - ), - classes=( - "`text-none w-100 ${control_simulation_file === entry.path ? '' : 'text-medium-emphasis'}`", - ), - style="min-width: 112px;", - size="small", - click=( - self._on_control_selected, - "[entry.path]", - ), - ) - with v3.VCol(cols=6, md=3): - with v3.VTooltip( - text="Toggle simulation inclusion", - ): - with v3.Template( - v_slot_activator="{ props }" - ): - v3.VCheckbox( - v_bind="props", - model_value=( - "control_simulation_file === entry.path ? true : entry.include", - ), - update_modelValue="utils.quickcompare.setSimulationInclude(entry.path, $event)", - label="Include", - density="compact", - hide_details=True, - disabled=( - "control_simulation_file === entry.path", - ), - ) + with html.Div(classes="d-flex align-center ga-2"): + v3.VTextField( + model_value=("entry.label",), + update_modelValue="utils.quickcompare.setSimulationLabel(entry.path, $event)", + label="Label", + density="compact", + variant="outlined", + hide_details=True, + ) + v3.VBtn( + v_tooltip_right="control_simulation_file === entry.path ? 'Current control simulation' : 'Set this simulation as control'", + text=( + "control_simulation_file === entry.path ? 'Control' : 'Set control'", + ), + variant="outlined", + color=( + "control_simulation_file === entry.path ? 'primary' : 'default'", + ), + classes=( + "`text-none ${control_simulation_file === entry.path ? '' : 'text-medium-emphasis'}`", + ), + # style="min-width: 112px;", + height="40px", + width="120px", + click=( + self._on_control_selected, + "[entry.path]", + ), + ) + v3.VSwitch( + v_tooltip_right="'Toggle simulation inclusion'", + model_value=( + "control_simulation_file === entry.path ? true : entry.include", + ), + update_modelValue="utils.quickcompare.setSimulationInclude(entry.path, $event)", + disabled=( + "control_simulation_file === entry.path", + ), + hide_details=True, + inset=True, + false_icon="mdi-close", + true_icon="mdi-check", + ) html.Div( "{{ entry.path }}", classes="text-caption text-medium-emphasis mt-2", diff --git a/src/e3sm_compareview/pipeline.py b/src/e3sm_compareview/pipeline.py index 1814316..300de2a 100644 --- a/src/e3sm_compareview/pipeline.py +++ b/src/e3sm_compareview/pipeline.py @@ -198,9 +198,7 @@ def __init__(self, projection="Robinson"): self.perimeter_extract.SetInputConnection(geometry.output_port) self.perimeter_mapper = vtkDataSetMapper() self.perimeter_mapper.SetScalarVisibility(0) - self.perimeter_mapper.SetInputConnection( - self.perimeter_extract.GetOutputPort() - ) + self.perimeter_mapper.SetInputConnection(self.perimeter_extract.GetOutputPort()) self.perimeter_actor = vtkActor() self.perimeter_actor.SetMapper(self.perimeter_mapper) perimeter_prop = self.perimeter_actor.GetProperty() @@ -308,7 +306,6 @@ def __init__(self, projection="Robinson"): self.atmos_extract = None self.atmos_proj = None self.atmos_surface = None - self._atmos_extract_mode = "range" self.extents = [-180.0, 180.0, -90.0, 90.0] self.moveextents = [-180.0, 180.0, -90.0, 90.0] self.clip_longitude = [-180.0, 180.0] @@ -345,12 +342,8 @@ def crop(self, longitude_min_max, latitude_min_max): self._apply_clip_to_extract() def _apply_clip_to_extract(self): - if self._atmos_extract_mode == "trim": - self.atmos_extract.TrimLongitude = range_to_trim(self.clip_longitude, 180) - self.atmos_extract.TrimLatitude = range_to_trim(self.clip_latitude, 90) - else: - self.atmos_extract.LongitudeRange = self.clip_longitude - self.atmos_extract.LatitudeRange = self.clip_latitude + self.atmos_extract.LongitudeRange = self.clip_longitude + self.atmos_extract.LatitudeRange = self.clip_latitude def update(self, time=0.0): if not self.valid or self.atmos_proj is None: @@ -662,14 +655,12 @@ def _build_atmosphere_pipeline(self): registrationName="AtmosExtract", Input=self.atmos_center, ) - self._atmos_extract_mode = "trim" elif has_range_extract: self.atmos_center = None self.atmos_extract = EAMTransformAndExtract( # noqa: F821 registrationName="AtmosExtract", Input=self.prog_filter, ) - self._atmos_extract_mode = "range" else: raise RuntimeError( "No compatible atmospheric extract filter is available " diff --git a/src/e3sm_compareview/view_manager.py b/src/e3sm_compareview/view_manager.py index c9fc035..9d8cce0 100644 --- a/src/e3sm_compareview/view_manager.py +++ b/src/e3sm_compareview/view_manager.py @@ -202,9 +202,7 @@ def picked_value(array_name): source_spec = source_specs_by_path.get(row_spec.get("path")) if source_spec is not None: row["has_source"] = True - row["source_display"] = picked_value( - source_spec["array_name"] - ) + row["source_display"] = picked_value(source_spec["array_name"]) rows.append(row) @@ -672,10 +670,16 @@ def build_auto_layout(self, variables=None): view_specs = self.get_view_specs(var_name) if not view_specs: continue - flat_vars.append((var_type, var_name, view_specs, self._group_orders.get(var_name))) + flat_vars.append( + (var_type, var_name, view_specs, self._group_orders.get(var_name)) + ) - current_group_order = {var_name: saved for _, var_name, _, saved in flat_vars if saved is not None} - next_group_order_idx = (max(current_group_order.values()) + 1) if current_group_order else 1 + current_group_order = { + var_name: saved for _, var_name, _, saved in flat_vars if saved is not None + } + next_group_order_idx = ( + (max(current_group_order.values()) + 1) if current_group_order else 1 + ) for _, var_name, _, saved in flat_vars: if saved is None: current_group_order[var_name] = next_group_order_idx @@ -714,8 +718,7 @@ def build_auto_layout(self, variables=None): with html.Div( var_name, classes=( - "text-subtitle-2 " - "font-weight-medium d-inline-block" + "text-subtitle-2 font-weight-medium d-inline-block" ), style="user-select: none; cursor: pointer;", ): @@ -743,14 +746,10 @@ def build_auto_layout(self, variables=None): style="transform: scale(0.75);", ) with v3.VRow(dense=True): - use_config_size = ( - self.state.comparison_mode == "multi-sim" - ) + use_config_size = self.state.comparison_mode == "multi-sim" if not use_config_size: views_per_row = max(1, len(view_specs)) - group_cols = max( - 1, math.floor(12 / views_per_row) - ) + group_cols = max(1, math.floor(12 / views_per_row)) panel_options = [ { "name": vs["array_name"], diff --git a/src/e3sm_compareview/view_panel.py b/src/e3sm_compareview/view_panel.py index c35bdbc..7bf6449 100644 --- a/src/e3sm_compareview/view_panel.py +++ b/src/e3sm_compareview/view_panel.py @@ -311,7 +311,10 @@ def _get_multi_sim_default_range(self): if range_min is not None and range_max is not None: return (range_min, range_max) - if self.comparison_type in ("diff", "comp1", "comp2") and self.role != "control": + if ( + self.comparison_type in ("diff", "comp1", "comp2") + and self.role != "control" + ): comparison_ranges = [] for view_spec in view_specs: if view_spec["role"] == "control": @@ -491,9 +494,7 @@ def _build_ui(self): with v3.VTooltip( classes="tooltip-no-padding", - disabled=( - f"!picking_mode || hover_info !== '{self.array_name}'", - ), + disabled=(f"!picking_mode || hover_info !== '{self.array_name}'",), open_on_hover=False, model_value=( f"picking_mode && !!probe_table && hover_info === '{self.array_name}'",