From 25a9c3025f107623b2b5a28f6e139c790bd919a0 Mon Sep 17 00:00:00 2001 From: Abhishek Yenpure Date: Fri, 22 Aug 2025 09:53:05 -0700 Subject: [PATCH 1/7] fix: usability fixes - Clear varaiable selection clears search list as well. - Lat/Long only operates with Range Sliders --- quickview/interface.py | 27 ++++++++++++++------- quickview/ui/slice_selection.py | 38 +++++++++++------------------- quickview/ui/variable_selection.py | 16 +++++++++++-- 3 files changed, 46 insertions(+), 35 deletions(-) diff --git a/quickview/interface.py b/quickview/interface.py index ea66553..60e1c08 100644 --- a/quickview/interface.py +++ b/quickview/interface.py @@ -713,19 +713,28 @@ def search_interface_vars(self, search: str): ].tolist() self.state.dirty("interface_vars_state") - def clear_surface_vars(self): - self.state.surface_vars_state = [False] * len(self.state.surface_vars_state) - self.surface_vars_state = np.array([False] * len(self.surface_vars_state)) + def clear_surface_vars(self, clear_var_name): + self.state[clear_var_name] = "" + self.ind_surface = None + self.state.surface_vars = self.source.surface_vars + self.state.surface_vars_state = [False] * len(self.source.surface_vars) + self.surface_vars_state = np.array([False] * len(self.source.surface_vars)) self.state.dirty("surface_vars_state") - def clear_midpoint_vars(self): - self.state.midpoint_vars_state = [False] * len(self.state.midpoint_vars_state) - self.midpoint_vars_state = np.array([False] * len(self.midpoint_vars_state)) + def clear_midpoint_vars(self, clear_var_name): + self.state[clear_var_name] = "" + self.ind_midpoint = None + self.state.midpoint_vars = self.source.midpoint_vars + self.state.midpoint_vars_state = [False] * len(self.source.midpoint_vars) + self.midpoint_vars_state = np.array([False] * len(self.source.midpoint_vars)) self.state.dirty("midpoint_vars_state") - def clear_interface_vars(self): - self.state.interface_vars_state = [False] * len(self.state.interface_vars_state) - self.interface_vars_state = np.array([False] * len(self.interface_vars_state)) + def clear_interface_vars(self, clear_var_name): + self.state[clear_var_name] = "" + self.ind_interface = None + self.state.interface_vars = self.source.interface_vars + self.state.interface_vars_state = [False] * len(self.source.interface_vars) + self.interface_vars_state = np.array([False] * len(self.source.interface_vars)) self.state.dirty("interface_vars_state") def start(self, **kwargs): diff --git a/quickview/ui/slice_selection.py b/quickview/ui/slice_selection.py index 9127d40..0f4b6b0 100644 --- a/quickview/ui/slice_selection.py +++ b/quickview/ui/slice_selection.py @@ -169,27 +169,22 @@ def __init__(self, source: EAMVisSource, view_manager: ViewManager): with v2.VRow(classes="text-center align-center text-subtitle-1 pt-2 pa-2"): with v2.VCol(cols=3, classes="py-0"): - v2.VTextField( - v_model=("cliplong[0]",), - label="Min", - suffix="°", - classes="py-0", - **style, + html.Div( + "{{ cliplong[0].toFixed(1) }}°", + classes="font-weight-medium text-center", ) with v2.VCol(cols=6, classes="py-0"): html.Div("Longitude") with v2.VCol(cols=3, classes="py-0"): - v2.VTextField( - v_model=("cliplong[1]",), - label="Max", - suffix="°", - classes="py-0", - **style, + html.Div( + "{{ cliplong[1].toFixed(1) }}°", + classes="font-weight-medium text-center", ) v2.VRangeSlider( v_model=("cliplong", [self.source.extents[0], self.source.extents[1]]), min=-180, max=180, + step=0.5, color="blue-grey", **style, flat=True, @@ -200,27 +195,22 @@ def __init__(self, source: EAMVisSource, view_manager: ViewManager): with v2.VRow(classes="text-center align-center text-subtitle-1 pt-4 px-2"): with v2.VCol(cols=3, classes="py-0"): - v2.VTextField( - v_model=("cliplat[0]",), - label="Min", - suffix="°", - classes="py-0", - **style, + html.Div( + "{{ cliplat[0].toFixed(1) }}°", + classes="font-weight-medium text-center", ) with v2.VCol(cols=6, classes="py-0"): html.Div("Latitude") with v2.VCol(cols=3, classes="py-0"): - v2.VTextField( - v_model=("cliplat[1]",), - label="Max", - suffix="°", - classes="py-0", - **style, + html.Div( + "{{ cliplat[1].toFixed(1) }}°", + classes="font-weight-medium text-center", ) v2.VRangeSlider( v_model=("cliplat", [self.source.extents[2], self.source.extents[3]]), min=-90, max=90, + step=0.5, color="blue-grey", **style, flat=True, diff --git a/quickview/ui/variable_selection.py b/quickview/ui/variable_selection.py index 5801adc..c78db3d 100644 --- a/quickview/ui/variable_selection.py +++ b/quickview/ui/variable_selection.py @@ -37,6 +37,14 @@ def __init__(self, variables, state, update=None): @TrameApp() class VariableSelection(CollapsableSection): + _next_id = 0 + + @classmethod + def next_id(cls): + """Get the next unique ID for the scalar bar.""" + cls._next_id += 1 + return f"var_select_{cls._next_id}" + def __init__( self, title=None, @@ -48,13 +56,17 @@ def __init__( on_update=None, ): super().__init__(title=title, var_name=panel_name) + + ns = self.next_id() + self.__search_var = f"{ns}_search" + with self.content: # Search and controls section with v2.VCard(flat=True, elevation=0, classes="pa-2 mb-1"): with v2.VRow(classes="align-center", no_gutters=True): with v2.VCol(cols=9, classes="pr-1"): v2.VTextField( - v_model=("variableSearchQuery", ""), + v_model=(self.__search_var, ""), prepend_inner_icon="mdi-magnify", label="Search variables", placeholder="Type to filter...", @@ -69,7 +81,7 @@ def __init__( with v2.VTooltip(bottom=True): with html.Template(v_slot_activator="{ on, attrs }"): with v2.VBtn( - click=(on_clear), + click=(on_clear, f"['{self.__search_var}']"), depressed=True, small=True, v_bind="attrs", From e53172c31513bd461601b04bd3a6336da6e51631 Mon Sep 17 00:00:00 2001 From: Abhishek Yenpure Date: Fri, 22 Aug 2025 12:06:57 -0700 Subject: [PATCH 2/7] rollback intermediate -- view clear works --- quickview/interface.py | 92 +--- quickview/ui/projection_selection.py | 2 +- quickview/utils/state.py | 106 ----- quickview/view_manager.py | 616 ++++++++++++--------------- 4 files changed, 287 insertions(+), 529 deletions(-) delete mode 100644 quickview/utils/state.py diff --git a/quickview/interface.py b/quickview/interface.py index 60e1c08..952926e 100644 --- a/quickview/interface.py +++ b/quickview/interface.py @@ -26,9 +26,9 @@ from quickview.ui.view_settings import ViewProperties, ViewControls from quickview.ui.toolbar import Toolbar -# Import view management components +# Build color cache here +from quickview.view_manager import build_color_information from quickview.view_manager import ViewManager -from quickview.utils.state import ViewContext, build_color_information from paraview.simple import ImportPresets, GetLookupTableNames @@ -320,52 +320,7 @@ def update_state_from_config(self, initstate): self.midpoint_vars_state = np.array(selection_midpoint) self.interface_vars_state = np.array(selection_interface) - # Build registry and populate with saved configuration self.viewmanager.registry = build_color_information(initstate) - - # Sync loaded configuration to contexts - if "variables" in initstate: - for i, var in enumerate(initstate["variables"]): - context = self.viewmanager.registry.get_view(var) - if not context: - context = ViewContext(var, i) - self.viewmanager.registry.register_view(var, context) - - # Populate context from loaded state - context.colormap = ( - initstate.get("varcolor", [])[i] - if i < len(initstate.get("varcolor", [])) - else None - ) - context.use_log_scale = ( - initstate.get("uselogscale", [])[i] - if i < len(initstate.get("uselogscale", [])) - else False - ) - context.invert_colors = ( - initstate.get("invert", [])[i] - if i < len(initstate.get("invert", [])) - else False - ) - context.min_value = ( - initstate.get("varmin", [])[i] - if i < len(initstate.get("varmin", [])) - else None - ) - context.max_value = ( - initstate.get("varmax", [])[i] - if i < len(initstate.get("varmax", [])) - else None - ) - context.override_range = ( - initstate.get("override_range", [])[i] - if i < len(initstate.get("override_range", [])) - else False - ) - context.has_been_configured = ( - True # Mark as configured since we're loading saved state - ) - self.load_variables(use_cached_layout=True) @trigger("layout_changed") @@ -514,39 +469,16 @@ def load_variables(self, use_cached_layout=False): vars = surf + mid + intf # Tracking variables to control camera and color properties - state = self.state - state.variables = vars - - # Initialize arrays with proper size - state.varcolor = [""] * len(vars) - state.uselogscale = [False] * len(vars) - state.invert = [False] * len(vars) - state.varmin = [0] * len(vars) - state.varmax = [1] * len(vars) - state.override_range = [False] * len(vars) - state.colorbar_images = [""] * len(vars) # Initialize empty images - state.varaverage = [0] * len(vars) - - # Check if variables already have contexts (still selected, just updating) - # Preserve configuration for variables that remain selected - for i, var in enumerate(vars): - context = self.viewmanager.registry.get_view(var) - if context and context.has_been_configured: - # Variable is still selected, preserve its configuration - state.varcolor[i] = context.colormap or self.get_default_colormap() - state.uselogscale[i] = context.use_log_scale - state.invert[i] = context.invert_colors - state.varmin[i] = ( - context.min_value if context.min_value is not None else 0 - ) - state.varmax[i] = ( - context.max_value if context.max_value is not None else 1 - ) - state.override_range[i] = context.override_range - else: - # New variable or was deselected, use defaults - state.varcolor[i] = self.get_default_colormap() - # Other values remain as initialized defaults + with self.state as state: + state.variables = vars + state.varcolor = self.get_default_colormap() + state.uselogscale = [False] * len(vars) + state.invert = [False] * len(vars) + state.varmin = [np.nan] * len(vars) + state.varmax = [np.nan] * len(vars) + state.override_range = [False] * len(vars) + state.colorbar_images = [""] * len(vars) # Initialize empty images + state.varaverage = [np.nan] * len(vars) # Only use cached layout when explicitly requested (i.e., when loading state) layout_to_use = self._cached_layout if use_cached_layout else None diff --git a/quickview/ui/projection_selection.py b/quickview/ui/projection_selection.py index 690bc37..793f0e8 100644 --- a/quickview/ui/projection_selection.py +++ b/quickview/ui/projection_selection.py @@ -48,6 +48,6 @@ def update_pipeline_interactive(self, **kwargs): time = 0.0 if len(self.state.timesteps) == 0 else self.state.timesteps[tstamp] self.source.UpdatePipeline(time) # For projection changes, we need to fit viewports to new bounds - self.views.update_views_for_timestep(fit_viewport=True) + self.views.update_views_for_timestep() # Render once after all updates self.views.render_all_views() diff --git a/quickview/utils/state.py b/quickview/utils/state.py deleted file mode 100644 index c52232a..0000000 --- a/quickview/utils/state.py +++ /dev/null @@ -1,106 +0,0 @@ -""" -View state management classes for QuickView. - -This module contains the ViewContext and ViewRegistry classes that manage -the persistent state and configuration of visualization views across -variable selection cycles. -""" - -from typing import Dict, List, Optional - - -class ViewRegistry: - """Central registry for managing views - tracks only currently selected variables.""" - - def __init__(self): - self._contexts: Dict[str, "ViewContext"] = {} - self._view_order: List[str] = [] - - def register_view(self, variable: str, context: "ViewContext"): - """Register a new view or update existing one.""" - self._contexts[variable] = context - if variable not in self._view_order: - self._view_order.append(variable) - - def get_view(self, variable: str) -> Optional["ViewContext"]: - """Get view context for a variable.""" - return self._contexts.get(variable) - - def remove_view(self, variable: str): - """Remove a view from the registry.""" - if variable in self._contexts: - del self._contexts[variable] - self._view_order.remove(variable) - - def get_ordered_views(self) -> List["ViewContext"]: - """Get all views in order they were added.""" - return [ - self._contexts[var] for var in self._view_order if var in self._contexts - ] - - def get_all_variables(self) -> List[str]: - """Get all registered variable names.""" - return list(self._contexts.keys()) - - def items(self): - """Iterate over variable-context pairs.""" - return self._contexts.items() - - def clear(self): - """Clear all registered views.""" - self._contexts.clear() - self._view_order.clear() - - def __len__(self): - """Get number of registered views.""" - return len(self._contexts) - - def __contains__(self, variable: str): - """Check if a variable is registered.""" - return variable in self._contexts - - -class ViewContext: - """Context storing ParaView objects and persistent configuration. - - This class is critical for maintaining user configuration across - variable selection/deselection cycles. It stores both the ParaView - rendering objects and the user's chosen visualization settings. - """ - - def __init__(self, variable: str, index: int): - self.variable = variable - self.index = index # Current position in state arrays - self.view_proxy = None # ParaView render view - self.data_representation = None # ParaView data representation - - # Persistent configuration that survives variable selection changes - self.colormap = None # Will persist colormap choice - self.use_log_scale = False - self.invert_colors = False - self.min_value = None # Computed or manual - self.max_value = None # Computed or manual - self.override_range = False # Track if manually set - self.has_been_configured = False # Track if user has modified settings - - -def build_color_information(state: Dict) -> ViewRegistry: - """Build a ViewRegistry from saved state information. - - This function is used for backward compatibility with saved states. - It creates an empty registry and preserves layout information if present. - - Args: - state: Dictionary containing saved state with optional 'layout' key - - Returns: - ViewRegistry: A new registry instance with saved layout if available - """ - registry = ViewRegistry() - - # Store layout if provided (for backward compatibility) - layout = state.get("layout", None) - if layout: - registry._saved_layout = [item.copy() for item in layout] - - return registry diff --git a/quickview/view_manager.py b/quickview/view_manager.py index 13c0443..2204aeb 100644 --- a/quickview/view_manager.py +++ b/quickview/view_manager.py @@ -11,13 +11,15 @@ GetColorTransferFunction, AddCameraLink, Render, + GetActiveView, ) from quickview.pipeline import EAMVisSource +from typing import Dict, List, Optional + +from quickview.utils.math import calculate_weighted_average from quickview.utils.color import get_cached_colorbar_image from quickview.utils.geometry import generate_annotations as generate_map_annotations -from quickview.utils.math import calculate_weighted_average -from quickview.utils.state import ViewContext, ViewRegistry # Constants for camera and display LABEL_OFFSET_FACTOR = 0.075 # Factor for offsetting labels from map edge @@ -33,11 +35,109 @@ DEFAULT_GRID_HEIGHT = 3 # Default height of grid items -# ViewRegistry and ViewContext classes have been moved to view_state.py +class ViewRegistry: + """Central registry for managing views""" + + def __init__(self): + self._contexts: Dict[str, "ViewContext"] = {} + self._view_order: List[str] = [] + + def register_view(self, variable: str, context: "ViewContext"): + """Register a new view or update existing one""" + self._contexts[variable] = context + if variable not in self._view_order: + self._view_order.append(variable) + + def get_view(self, variable: str) -> Optional["ViewContext"]: + """Get view context for a variable""" + return self._contexts.get(variable) + + def remove_view(self, variable: str): + """Remove a view from the registry""" + if variable in self._contexts: + del self._contexts[variable] + self._view_order.remove(variable) + + def get_ordered_views(self) -> List["ViewContext"]: + """Get all views in order they were added""" + return [ + self._contexts[var] for var in self._view_order if var in self._contexts + ] + + def get_all_variables(self) -> List[str]: + """Get all registered variable names""" + return list(self._contexts.keys()) + + def items(self): + """Iterate over variable-context pairs""" + return self._contexts.items() + + def clear(self): + """Clear all registered views""" + self._contexts.clear() + self._view_order.clear() + + def __len__(self): + """Get number of registered views""" + return len(self._contexts) + + def __contains__(self, variable: str): + """Check if a variable is registered""" + return variable in self._contexts + + +class ViewConfiguration: + """Mutable configuration for a view - what the user can control""" + + def __init__( + self, + variable: str, + colormap: str, + use_log_scale: bool = False, + invert_colors: bool = False, + min_value: float = None, + max_value: float = None, + override_range: bool = False, + ): + self.variable = variable + self.colormap = colormap + self.use_log_scale = use_log_scale + self.invert_colors = invert_colors + self.min_value = min_value + self.max_value = max_value + self.override_range = override_range # True when user manually sets min/max + + +class ViewState: + """Runtime state for a view - ParaView objects""" + + def __init__( + self, + view_proxy=None, + data_representation=None, + ): + self.view_proxy = view_proxy + self.data_representation = data_representation + + +class ViewContext: + """Complete context for a rendered view combining configuration and state""" + + def __init__(self, config: ViewConfiguration, state: ViewState, index: int): + self.config = config + self.state = state + self.index = index + + +def apply_projection(projection, point): + if projection is None: + return point + else: + new = projection.transform(point[0] - 180, point[1]) + return [new[0], new[1], 1.0] def generate_annotations(long, lat, projection, center): - """Generate map annotations using geo_utils.""" return generate_map_annotations( long, lat, @@ -48,7 +148,45 @@ def generate_annotations(long, lat, projection, center): ) -# build_color_information has been moved to view_state.py +def build_color_information(state: map): + vars = state["variables"] + colors = state["varcolor"] + logscl = state["uselogscale"] + invert = state["invert"] + varmin = state["varmin"] + varmax = state["varmax"] + # Get override_range from state if available + override_range = state.get("override_range", None) + # Store layout from state if available for backward compatibility + layout = state.get("layout", None) + + registry = ViewRegistry() + for index, var in enumerate(vars): + # Use provided override_range if available + if override_range is not None and index < len(override_range): + override = override_range[index] + else: + # Legacy behavior for older saved states without override_range + override = True + + config = ViewConfiguration( + variable=var, + colormap=colors[index], + use_log_scale=logscl[index], + invert_colors=invert[index], + min_value=varmin[index], + max_value=varmax[index], + override_range=override, + ) + view_state = ViewState() + context = ViewContext(config, view_state, index) + registry.register_view(var, context) + + # Store layout info in registry for later use + if layout: + registry._saved_layout = [item.copy() for item in layout] + + return registry @TrameApp() @@ -60,146 +198,68 @@ def __init__(self, source: EAMVisSource, server, state): self.widgets = [] self.registry = ViewRegistry() # Central registry for view management self.to_delete = [] - self.rep_change = False - - # Register state change listener for pipeline_valid - self.state.change("pipeline_valid")(self._on_pipeline_valid_change) - - def _on_pipeline_valid_change(self, pipeline_valid, **kwargs): - """Clear view registry when pipeline becomes invalid.""" - if not pipeline_valid: - # Clear all views and variables from registry - self.registry.clear() - # Clear widgets and colors tracking - del self.state.views[:] - del self.state.layout[:] - self.state.dirty("views") - self.state.dirty("layout") - - def get_color_config(self, index): - """Get all color configuration for a variable from state arrays""" - state = self.state - return { - "colormap": state.varcolor[index] - if index < len(state.varcolor) - else "Cool to Warm", - "use_log_scale": state.uselogscale[index] - if index < len(state.uselogscale) - else False, - "invert_colors": state.invert[index] - if index < len(state.invert) - else False, - "min_value": state.varmin[index] if index < len(state.varmin) else None, - "max_value": state.varmax[index] if index < len(state.varmax) else None, - "override_range": state.override_range[index] - if index < len(state.override_range) - else False, - } - - def should_use_manual_range(self, index): - """Check if manual range should be used for a variable""" - return ( - hasattr(self.state, "override_range") - and index < len(self.state.override_range) - and self.state.override_range[index] - ) - - def get_color_range(self, var, index): - """Get the appropriate color range (manual or computed) for a variable""" - if self.should_use_manual_range(index): - return (self.state.varmin[index], self.state.varmax[index]) - else: - return self.compute_range(var) - - def update_views_for_timestep(self, fit_viewport=True): - """Update views for timestep changes. - Args: - fit_viewport: Whether to fit viewport after update (default True). - Set to False to avoid redundant fits when caller will do it. - """ + def update_views_for_timestep(self): if len(self.registry) == 0: return data = sm.Fetch(self.source.views["atmosphere_data"]) - first_view = None - for var, context in self.registry.items(): varavg = self.compute_average(var, vtkdata=data) # Directly set average in trame state self.state.varaverage[context.index] = varavg self.state.dirty("varaverage") - - if not context.override_range: - context.data_representation.RescaleTransferFunctionToDataRange( + if not context.config.override_range: + context.state.data_representation.RescaleTransferFunctionToDataRange( False, True ) range = self.compute_range(var=var) - # Update both context and state - context.min_value = range[0] - context.max_value = range[1] - self.state.varmin[context.index] = range[0] - self.state.varmax[context.index] = range[1] - self.state.dirty("varmin") - self.state.dirty("varmax") - + context.config.min_value = range[0] + context.config.max_value = range[1] + self.sync_color_config_to_state(context.index, context) self.generate_colorbar_image(context.index) - # Track the first view for camera fitting - if first_view is None and context.view_proxy: - first_view = context.view_proxy - - # Only fit the first view since cameras are linked - if fit_viewport and first_view: - self.fit_to_viewport(first_view) + view = GetActiveView() + view.ResetCamera(True, 0.9) def refresh_view_display(self, context: ViewContext): - if not self.should_use_manual_range(context.index): - context.data_representation.RescaleTransferFunctionToDataRange(False, True) + if not context.config.override_range: + context.state.data_representation.RescaleTransferFunctionToDataRange( + False, True + ) + rview = context.state.view_proxy - if context.view_proxy: - Render(context.view_proxy) - # ResetCamera(rview) + Render(rview) + # ResetCamera(rview) def configure_new_view(self, var, context: ViewContext, sources): - rview = context.view_proxy + rview = context.state.view_proxy # Update unique sources to all render views data = sources["atmosphere_data"] rep = Show(data, rview) - context.data_representation = rep + context.state.data_representation = rep ColorBy(rep, ("CELLS", var)) - - # Use context configuration if available, fallback to state - colormap = ( - context.colormap - if context.colormap - else ( - self.state.varcolor[context.index] - if context.index < len(self.state.varcolor) - else "Cool to Warm" - ) - ) - coltrfunc = GetColorTransferFunction(var) - coltrfunc.ApplyPreset(colormap, True) + coltrfunc.ApplyPreset(context.config.colormap, True) coltrfunc.NanOpacity = 0.0 # Apply log scale if configured - if context.use_log_scale: + if context.config.use_log_scale: coltrfunc.MapControlPointsToLogSpace() coltrfunc.UseLogScale = 1 # Apply inversion if configured - if context.invert_colors: + if context.config.invert_colors: coltrfunc.InvertTransferFunction() # Ensure the color transfer function is scaled to the data range - if not context.override_range: + if not context.config.override_range: rep.RescaleTransferFunctionToDataRange(False, True) else: - if context.min_value is not None and context.max_value is not None: - coltrfunc.RescaleTransferFunction(context.min_value, context.max_value) + coltrfunc.RescaleTransferFunction( + context.config.min_value, context.config.max_value + ) # ParaView scalar bar is always hidden - using custom HTML colorbar instead @@ -225,10 +285,24 @@ def configure_new_view(self, var, context: ViewContext, sources): rep.SetScalarBarVisibility(rview, False) rview.CameraParallelProjection = 1 - # Skip individual fit - will be handled by view0 after all views are created Render(rview) - - # This function is no longer needed - we work directly with state arrays + # ResetCamera(rview) + + def sync_color_config_to_state(self, index, context: ViewContext): + # Update state arrays directly without context manager to avoid recursive flush + self.state.varcolor[index] = context.config.colormap + self.state.varmin[index] = context.config.min_value + self.state.varmax[index] = context.config.max_value + self.state.uselogscale[index] = context.config.use_log_scale + self.state.override_range[index] = context.config.override_range + self.state.invert[index] = context.config.invert_colors + # Mark arrays as dirty to ensure UI updates + self.state.dirty("varcolor") + self.state.dirty("varmin") + self.state.dirty("varmax") + self.state.dirty("uselogscale") + self.state.dirty("override_range") + self.state.dirty("invert") def generate_colorbar_image(self, index): """Generate colorbar image for a variable at given index. @@ -244,54 +318,23 @@ def generate_colorbar_image(self, index): if context is None: return - # Use context configuration - colormap = ( - context.colormap - if context.colormap - else ( - self.state.varcolor[index] - if index < len(self.state.varcolor) - else "Cool to Warm" - ) - ) - invert = context.invert_colors - # Get cached colorbar image based on colormap and invert status try: - image_data = get_cached_colorbar_image(colormap, invert) + image_data = get_cached_colorbar_image( + context.config.colormap, context.config.invert_colors + ) # Update state with the cached image self.state.colorbar_images[index] = image_data self.state.dirty("colorbar_images") except Exception as e: print(f"Error getting cached colorbar image for {var}: {e}") - def fit_to_viewport(self, view, margin=DEFAULT_MARGIN, use_largest_viewport=False): - """ - Reset camera to fit objects in viewport. - - Args: - view: The render view to fit - margin: Not used (kept for compatibility) - use_largest_viewport: Not used (kept for compatibility) - """ - from paraview.simple import SetActiveView - - try: - # Set this view as active and reset camera - SetActiveView(view) - view.ResetCamera(True, 0.9) - - except Exception as e: - print(f"Error in fit_to_viewport: {e}") - def reset_camera(self, **kwargs): - """Reset camera for all views to optimally fit objects.""" - # Only reset the first view since cameras are linked if len(self.widgets) > 0 and len(self.state.variables) > 0: var = self.state.variables[0] context = self.registry.get_view(var) - if context and context.view_proxy: - self.fit_to_viewport(context.view_proxy) + if context and context.state.view_proxy: + context.state.view_proxy.ResetCamera(True, 0.9) self.render_all_views() def render_all_views(self, **kwargs): @@ -318,10 +361,8 @@ def compute_average(self, var, vtkdata=None): vtkdata = sm.Fetch(data) vardata = vtkdata.GetCellData().GetArray(var) - # Check if area variable exists for weighted averaging + # Check if area variable exists area_array = vtkdata.GetCellData().GetArray("area") - - # Use utility function for calculation return calculate_weighted_average(vardata, area_array) def compute_range(self, var, vtkdata=None): @@ -337,14 +378,11 @@ def rebuild_visualization_layout(self, cached_layout=None): source = self.source long = state.cliplong lat = state.cliplat - tstamp = state.tstamp - time = 0.0 if len(self.state.timesteps) == 0 else self.state.timesteps[tstamp] - source.UpdateLev(self.state.midpoint, self.state.interface) source.ApplyClipping(long, lat) source.UpdateCenter(self.state.center) source.UpdateProjection(self.state.projection) - source.UpdatePipeline(time) + source.UpdatePipeline() surface_vars = source.vars.get("surface", []) midpoint_vars = source.vars.get("midpoint", []) interface_vars = source.vars.get("interface", []) @@ -352,11 +390,9 @@ def rebuild_visualization_layout(self, cached_layout=None): rendered = self.registry.get_all_variables() to_delete = set(rendered) - set(to_render) # Move old variables so they their proxies can be deleted - self.to_delete.extend([self.registry.get_view(x).view_proxy for x in to_delete]) - - # Remove deselected variables from registry to free memory - for var in to_delete: - self.registry.remove_view(var) + self.to_delete.extend( + [self.registry.get_view(x).state.view_proxy for x in to_delete] + ) # Get area variable to calculate weighted average data = self.source.views["atmosphere_data"] @@ -401,11 +437,11 @@ def rebuild_visualization_layout(self, cached_layout=None): wdt = pos["w"] hgt = pos["h"] else: - # Default grid position - x = int(index % DEFAULT_GRID_COLUMNS) * DEFAULT_GRID_WIDTH - y = int(index / DEFAULT_GRID_COLUMNS) * DEFAULT_GRID_HEIGHT - wdt = DEFAULT_GRID_WIDTH - hgt = DEFAULT_GRID_HEIGHT + # Default grid position (3 columns) + x = int(index % 3) * 4 + y = int(index / 3) * 3 + wdt = 4 + hgt = 3 varrange = self.compute_range(var, vtkdata=vtkdata) varavg = self.compute_average(var, vtkdata=vtkdata) @@ -413,109 +449,56 @@ def rebuild_visualization_layout(self, cached_layout=None): view = None context: ViewContext = self.registry.get_view(var) if context is not None: - view = context.view_proxy + view = context.state.view_proxy if view is None: view = CreateRenderView() view.UseColorPaletteForBackground = 0 view.BackgroundColorMode = "Gradient" view.GetRenderWindow().SetOffScreenRendering(True) - context.view_proxy = view - - # Update context's index to current position - context.index = index - - # Sync min/max values - if context.override_range: - # Use context's saved values - self.state.varmin[index] = ( - context.min_value - if context.min_value is not None - else varrange[0] - ) - self.state.varmax[index] = ( - context.max_value - if context.max_value is not None - else varrange[1] - ) - else: - # Use computed range and update context - context.min_value = varrange[0] - context.max_value = varrange[1] - self.state.varmin[index] = varrange[0] - self.state.varmax[index] = varrange[1] - + context.state.view_proxy = view + context.config.min_value = varrange[0] + context.config.max_value = varrange[1] self.configure_new_view(var, context, self.source.views) else: self.refresh_view_display(context) - # Skip individual viewport fitting - will be done once for view0 else: view = CreateRenderView() - view.UseColorPaletteForBackground = 0 - view.BackgroundColorMode = "Gradient" - view.GetRenderWindow().SetOffScreenRendering(True) - - # Create new context - context = ViewContext(var, index) - context.view_proxy = view - - # Copy configuration from state arrays (already restored) - context.colormap = ( - state.varcolor[index] if index < len(state.varcolor) else None - ) - context.use_log_scale = ( - state.uselogscale[index] - if index < len(state.uselogscale) + # Preserve override flag if context already exists + existing_context = self.registry.get_view(var) + override = ( + existing_context.config.override_range + if existing_context else False ) - context.invert_colors = ( - state.invert[index] if index < len(state.invert) else False + + config = ViewConfiguration( + variable=var, + colormap=state.varcolor[index] + if index < len(state.varcolor) + else state.varcolor[0], + use_log_scale=state.uselogscale[index] + if index < len(state.uselogscale) + else False, + invert_colors=state.invert[index] + if index < len(state.invert) + else False, + min_value=varrange[0], + max_value=varrange[1], + override_range=override, ) - context.override_range = ( - state.override_range[index] - if index < len(state.override_range) - else False + view_state = ViewState( + view_proxy=view, ) - - # Set min/max based on override flag - if context.override_range: - # Use saved min/max from state - context.min_value = ( - state.varmin[index] - if index < len(state.varmin) - else varrange[0] - ) - context.max_value = ( - state.varmax[index] - if index < len(state.varmax) - else varrange[1] - ) - self.state.varmin[index] = context.min_value - self.state.varmax[index] = context.max_value - else: - # Use computed range - context.min_value = varrange[0] - context.max_value = varrange[1] - self.state.varmin[index] = varrange[0] - self.state.varmax[index] = varrange[1] - - # Mark as configured if it has non-default values - if ( - context.colormap - or context.use_log_scale - or context.invert_colors - or context.override_range - ): - context.has_been_configured = True - - # Register the view + context = ViewContext(config, view_state, index) + view.UseColorPaletteForBackground = 0 + view.BackgroundColorMode = "Gradient" self.registry.register_view(var, context) - self.configure_new_view(var, context, self.source.views) context.index = index # Set the computed average directly in trame state self.state.varaverage[index] = varavg self.state.dirty("varaverage") - # No need to sync - we're already working with state arrays + self.sync_color_config_to_state(index, context) self.generate_colorbar_image(index) if index == 0: @@ -534,72 +517,54 @@ def rebuild_visualization_layout(self, cached_layout=None): # Use index as identifier to maintain compatibility with grid expectations layout.append({"x": x, "y": y, "w": wdt, "h": hgt, "i": index}) - # Only fit view0 since all cameras are linked - if view0 is not None: - self.fit_to_viewport(view0) + for var in to_delete: + self.registry.remove_view(var) self.state.views = sWidgets self.state.layout = layout self.state.dirty("views") self.state.dirty("layout") - - # Single render after all updates - self.render_all_views() + # from trame.app import asynchronous + # asynchronous.create_task(self.flushViews()) """ async def flushViews(self): await self.server.network_completion + print("Flushing views") self.render_all_views() import asyncio await asyncio.sleep(1) + print("Resetting views after sleep") self.render_all_views() """ def update_colormap(self, index, value): """Update the colormap for a variable.""" var = self.state.variables[index] - context = self.registry.get_view(var) - if not context: - return - coltrfunc = GetColorTransferFunction(var) + context: ViewContext = self.registry.get_view(var) - # Persist to context - context.colormap = value - context.has_been_configured = True - - # Update state for UI - self.state.varcolor[index] = value - self.state.dirty("varcolor") - + context.config.colormap = value # Apply the preset - coltrfunc.ApplyPreset(value, True) + coltrfunc.ApplyPreset(context.config.colormap, True) # Reapply inversion if it was enabled - if context.invert_colors: + if context.config.invert_colors: coltrfunc.InvertTransferFunction() # Generate new colorbar image with updated colormap self.generate_colorbar_image(index) + # Sync all color configuration changes back to state + self.sync_color_config_to_state(index, context) self.render_view_by_index(index) def update_log_scale(self, index, value): """Update the log scale setting for a variable.""" var = self.state.variables[index] - context = self.registry.get_view(var) - if not context: - return - coltrfunc = GetColorTransferFunction(var) + context: ViewContext = self.registry.get_view(var) - # Persist to context - context.use_log_scale = value - context.has_been_configured = True - - # Update state for UI - self.state.uselogscale[index] = value - self.state.dirty("uselogscale") - - if value: + context.config.use_log_scale = value + if context.config.use_log_scale: coltrfunc.MapControlPointsToLogSpace() coltrfunc.UseLogScale = 1 else: @@ -609,60 +574,41 @@ def update_log_scale(self, index, value): # itself doesn't change with log scale - only the data mapping changes. # The colorbar always shows a linear color progression. + # Sync all color configuration changes back to state + self.sync_color_config_to_state(index, context) self.render_view_by_index(index) def update_invert_colors(self, index, value): """Update the color inversion setting for a variable.""" var = self.state.variables[index] - context = self.registry.get_view(var) - if not context: - return - coltrfunc = GetColorTransferFunction(var) + context: ViewContext = self.registry.get_view(var) - # Persist to context - context.invert_colors = value - context.has_been_configured = True - - # Update state for UI - self.state.invert[index] = value - self.state.dirty("invert") - + context.config.invert_colors = value coltrfunc.InvertTransferFunction() # Generate new colorbar image when colors are inverted self.generate_colorbar_image(index) + # Sync all color configuration changes back to state + self.sync_color_config_to_state(index, context) self.render_view_by_index(index) def update_scalar_bars(self, event=None): # Always hide ParaView scalar bars - using custom HTML colorbar # The HTML colorbar is always visible, no toggle needed for _, context in self.registry.items(): - view = context.view_proxy - if context.data_representation: - context.data_representation.SetScalarBarVisibility(view, False) + view = context.state.view_proxy + context.state.data_representation.SetScalarBarVisibility(view, False) self.render_all_views() def set_manual_color_range(self, index, min, max): var = self.state.variables[index] - context = self.registry.get_view(var) - if not context: - return - - # Persist to context - context.override_range = True - context.min_value = float(min) - context.max_value = float(max) - context.has_been_configured = True - - # Update state for UI - self.state.override_range[index] = True - self.state.varmin[index] = float(min) - self.state.varmax[index] = float(max) - self.state.dirty("override_range") - self.state.dirty("varmin") - self.state.dirty("varmax") - + context: ViewContext = self.registry.get_view(var) + context.config.override_range = True + context.config.min_value = float(min) + context.config.max_value = float(max) + # Sync all changes back to state + self.sync_color_config_to_state(index, context) # Update color transfer function coltrfunc = GetColorTransferFunction(var) coltrfunc.RescaleTransferFunction(float(min), float(max)) @@ -674,55 +620,41 @@ def revert_to_auto_color_range(self, index): # Get colors from main file varrange = self.compute_range(var) context: ViewContext = self.registry.get_view(var) - if not context: - return - - # Persist to context - context.override_range = False - context.min_value = varrange[0] - context.max_value = varrange[1] - context.has_been_configured = True - - # Update state for UI - self.state.override_range[index] = False - self.state.varmin[index] = varrange[0] - self.state.varmax[index] = varrange[1] - self.state.dirty("override_range") - self.state.dirty("varmin") - self.state.dirty("varmax") - + context.config.override_range = False + context.config.min_value = varrange[0] + context.config.max_value = varrange[1] + # Sync all changes back to state + self.sync_color_config_to_state(index, context) # Rescale transfer function to data range - if context.data_representation: - context.data_representation.RescaleTransferFunctionToDataRange(False, True) + context.state.data_representation.RescaleTransferFunctionToDataRange( + False, True + ) # Note: colorbar image doesn't change with range, only data mapping changes self.render_all_views() def zoom_in(self, index=0): var = self.state.variables[index] context: ViewContext = self.registry.get_view(var) - if context and context.view_proxy: - context.view_proxy.CameraParallelScale *= ZOOM_IN_FACTOR + rview = context.state.view_proxy + rview.CameraParallelScale *= 0.95 self.render_all_views() def zoom_out(self, index=0): var = self.state.variables[index] context: ViewContext = self.registry.get_view(var) - if context and context.view_proxy: - context.view_proxy.CameraParallelScale *= ZOOM_OUT_FACTOR + rview = context.state.view_proxy + rview.CameraParallelScale *= 1.05 self.render_all_views() def pan_camera(self, dir, factor, index=0): var = self.state.variables[index] context: ViewContext = self.registry.get_view(var) - if not context or not context.view_proxy: - return - - rview = context.view_proxy + rview = context.state.view_proxy extents = self.source.moveextents move = ( - (extents[1] - extents[0]) * PAN_OFFSET_RATIO, - (extents[3] - extents[2]) * PAN_OFFSET_RATIO, - (extents[5] - extents[4]) * PAN_OFFSET_RATIO, + (extents[1] - extents[0]) * 0.05, + (extents[3] - extents[2]) * 0.05, + (extents[5] - extents[4]) * 0.05, ) pos = rview.CameraPosition From ab2cb68a91c2110d7cbdb152166fcee0dc9d1c51 Mon Sep 17 00:00:00 2001 From: Abhishek Yenpure Date: Fri, 22 Aug 2025 13:27:06 -0700 Subject: [PATCH 3/7] fix: color map issue --- quickview/interface.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/quickview/interface.py b/quickview/interface.py index 952926e..f3a2fa0 100644 --- a/quickview/interface.py +++ b/quickview/interface.py @@ -439,7 +439,9 @@ def get_default_colormap(self): # Fallback to first available colormap return ( - self.state.colormaps[0]["value"] if self.state.colormaps else "Cool to Warm" + self.state.colormaps[0]["value"] + if self.state.colormaps + else "Cool to Warm (Extended)" ) def load_variables(self, use_cached_layout=False): @@ -471,7 +473,7 @@ def load_variables(self, use_cached_layout=False): # Tracking variables to control camera and color properties with self.state as state: state.variables = vars - state.varcolor = self.get_default_colormap() + state.varcolor = [self.get_default_colormap()] * len(vars) state.uselogscale = [False] * len(vars) state.invert = [False] * len(vars) state.varmin = [np.nan] * len(vars) From 8a516206e4c4756b87fa2ccca0127070216ce8b8 Mon Sep 17 00:00:00 2001 From: Abhishek Yenpure Date: Fri, 22 Aug 2025 14:08:55 -0700 Subject: [PATCH 4/7] fix: color settings apply to load state --- quickview/interface.py | 48 +++++++++++++++--- quickview/view_manager.py | 100 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 135 insertions(+), 13 deletions(-) diff --git a/quickview/interface.py b/quickview/interface.py index f3a2fa0..7ddfa76 100644 --- a/quickview/interface.py +++ b/quickview/interface.py @@ -473,14 +473,46 @@ def load_variables(self, use_cached_layout=False): # Tracking variables to control camera and color properties with self.state as state: state.variables = vars - state.varcolor = [self.get_default_colormap()] * len(vars) - state.uselogscale = [False] * len(vars) - state.invert = [False] * len(vars) - state.varmin = [np.nan] * len(vars) - state.varmax = [np.nan] * len(vars) - state.override_range = [False] * len(vars) - state.colorbar_images = [""] * len(vars) # Initialize empty images - state.varaverage = [np.nan] * len(vars) + + # When loading from cached state, preserve existing color values + # Otherwise, initialize with defaults + if not use_cached_layout: + state.varcolor = [self.get_default_colormap()] * len(vars) + state.uselogscale = [False] * len(vars) + state.invert = [False] * len(vars) + state.varmin = [np.nan] * len(vars) + state.varmax = [np.nan] * len(vars) + state.override_range = [False] * len(vars) + state.colorbar_images = [""] * len(vars) # Initialize empty images + state.varaverage = [np.nan] * len(vars) + else: + # Preserve loaded values but ensure arrays match variable count + # Extend or trim arrays to match new variable count if needed + current_len = ( + len(state.varcolor) + if hasattr(state, "varcolor") and state.varcolor + else 0 + ) + if current_len != len(vars): + # If array lengths don't match, extend with defaults or trim + default_colormap = self.get_default_colormap() + state.varcolor = (state.varcolor + [default_colormap] * len(vars))[ + : len(vars) + ] + state.uselogscale = (state.uselogscale + [False] * len(vars))[ + : len(vars) + ] + state.invert = (state.invert + [False] * len(vars))[: len(vars)] + state.varmin = (state.varmin + [np.nan] * len(vars))[: len(vars)] + state.varmax = (state.varmax + [np.nan] * len(vars))[: len(vars)] + state.override_range = (state.override_range + [False] * len(vars))[ + : len(vars) + ] + state.varaverage = (state.varaverage + [np.nan] * len(vars))[ + : len(vars) + ] + # Always reset colorbar images as they need to be regenerated + state.colorbar_images = [""] * len(vars) # Only use cached layout when explicitly requested (i.e., when loading state) layout_to_use = self._cached_layout if use_cached_layout else None diff --git a/quickview/view_manager.py b/quickview/view_manager.py index 2204aeb..8e5f432 100644 --- a/quickview/view_manager.py +++ b/quickview/view_manager.py @@ -456,11 +456,74 @@ def rebuild_visualization_layout(self, cached_layout=None): view.BackgroundColorMode = "Gradient" view.GetRenderWindow().SetOffScreenRendering(True) context.state.view_proxy = view - context.config.min_value = varrange[0] - context.config.max_value = varrange[1] + # Use loaded color range if override is enabled and values are valid + if ( + index < len(state.override_range) + and state.override_range[index] + and index < len(state.varmin) + and index < len(state.varmax) + and not ( + state.varmin[index] != state.varmin[index] + ) # Check for NaN + and not (state.varmax[index] != state.varmax[index]) + ): # Check for NaN + context.config.min_value = state.varmin[index] + context.config.max_value = state.varmax[index] + context.config.override_range = True + else: + context.config.min_value = varrange[0] + context.config.max_value = varrange[1] self.configure_new_view(var, context, self.source.views) else: + # Update context with all loaded color settings + # Update colormap if available + if index < len(state.varcolor): + context.config.colormap = state.varcolor[index] + # Update log scale setting if available + if index < len(state.uselogscale): + context.config.use_log_scale = state.uselogscale[index] + # Update invert colors setting if available + if index < len(state.invert): + context.config.invert_colors = state.invert[index] + + # Update color range if override is enabled and values are valid + if ( + index < len(state.override_range) + and state.override_range[index] + and index < len(state.varmin) + and index < len(state.varmax) + and not ( + state.varmin[index] != state.varmin[index] + ) # Check for NaN + and not (state.varmax[index] != state.varmax[index]) + ): # Check for NaN + context.config.min_value = state.varmin[index] + context.config.max_value = state.varmax[index] + context.config.override_range = True + + # Apply all the loaded color settings to the transfer function + coltrfunc = GetColorTransferFunction(var) + # Apply colormap + coltrfunc.ApplyPreset(context.config.colormap, True) + # Apply log scale + if context.config.use_log_scale: + coltrfunc.MapControlPointsToLogSpace() + coltrfunc.UseLogScale = 1 + else: + coltrfunc.UseLogScale = 0 + # Apply invert colors + if context.config.invert_colors: + coltrfunc.InvertTransferFunction() + # Apply color range if overridden + if context.config.override_range: + coltrfunc.RescaleTransferFunction( + context.config.min_value, context.config.max_value + ) + self.refresh_view_display(context) + # Sync the updated color configuration back to state and regenerate colorbar + self.sync_color_config_to_state(index, context) + self.generate_colorbar_image(index) else: view = CreateRenderView() # Preserve override flag if context already exists @@ -468,9 +531,28 @@ def rebuild_visualization_layout(self, cached_layout=None): override = ( existing_context.config.override_range if existing_context - else False + else ( + index < len(state.override_range) + and state.override_range[index] + ) ) + # Use loaded color range if override is enabled and values are valid + if ( + override + and index < len(state.varmin) + and index < len(state.varmax) + and not ( + state.varmin[index] != state.varmin[index] + ) # Check for NaN + and not (state.varmax[index] != state.varmax[index]) + ): # Check for NaN + min_val = state.varmin[index] + max_val = state.varmax[index] + else: + min_val = varrange[0] + max_val = varrange[1] + config = ViewConfiguration( variable=var, colormap=state.varcolor[index] @@ -482,8 +564,8 @@ def rebuild_visualization_layout(self, cached_layout=None): invert_colors=state.invert[index] if index < len(state.invert) else False, - min_value=varrange[0], - max_value=varrange[1], + min_value=min_val, + max_value=max_val, override_range=override, ) view_state = ViewState( @@ -494,6 +576,14 @@ def rebuild_visualization_layout(self, cached_layout=None): view.BackgroundColorMode = "Gradient" self.registry.register_view(var, context) self.configure_new_view(var, context, self.source.views) + + # Apply manual color range if override is enabled + if context.config.override_range: + coltrfunc = GetColorTransferFunction(var) + coltrfunc.RescaleTransferFunction( + context.config.min_value, context.config.max_value + ) + context.index = index # Set the computed average directly in trame state self.state.varaverage[index] = varavg From 7734fce7d825498e161487d5352323598015673a Mon Sep 17 00:00:00 2001 From: Abhishek Yenpure Date: Fri, 22 Aug 2025 14:30:53 -0700 Subject: [PATCH 5/7] Adding load state color properties --- quickview/interface.py | 4 ++-- quickview/view_manager.py | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/quickview/interface.py b/quickview/interface.py index 7ddfa76..d1d0937 100644 --- a/quickview/interface.py +++ b/quickview/interface.py @@ -302,12 +302,12 @@ def update_state_from_source(self): def update_state_from_config(self, initstate): source = self.source + self.state.update(initstate) + self.update_available_color_maps() with self.state as state: state.surface_vars = source.surface_vars state.interface_vars = source.interface_vars state.midpoint_vars = source.midpoint_vars - state.update(initstate) - selection = state.variables selection_surface = np.isin(state.surface_vars, selection).tolist() selection_midpoint = np.isin(state.midpoint_vars, selection).tolist() diff --git a/quickview/view_manager.py b/quickview/view_manager.py index 8e5f432..f81ca23 100644 --- a/quickview/view_manager.py +++ b/quickview/view_manager.py @@ -521,9 +521,6 @@ def rebuild_visualization_layout(self, cached_layout=None): ) self.refresh_view_display(context) - # Sync the updated color configuration back to state and regenerate colorbar - self.sync_color_config_to_state(index, context) - self.generate_colorbar_image(index) else: view = CreateRenderView() # Preserve override flag if context already exists @@ -620,11 +617,9 @@ def rebuild_visualization_layout(self, cached_layout=None): """ async def flushViews(self): await self.server.network_completion - print("Flushing views") self.render_all_views() import asyncio await asyncio.sleep(1) - print("Resetting views after sleep") self.render_all_views() """ From 8eb6b56634f1ebf859d3c6bed60eeb3d0b3c3e13 Mon Sep 17 00:00:00 2001 From: Abhishek Yenpure Date: Fri, 22 Aug 2025 14:40:56 -0700 Subject: [PATCH 6/7] fix: remove print statements --- quickview/pipeline.py | 1 - quickview/plugins/eam_filters.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/quickview/pipeline.py b/quickview/pipeline.py index fee9637..d4cbbd8 100644 --- a/quickview/pipeline.py +++ b/quickview/pipeline.py @@ -242,7 +242,6 @@ def Update(self, data_file, conn_file, midpoint=0, interface=0, force_reload=Fal self.timestamps = ( [timestep_values] if timestep_values is not None else [] ) - print(self.timestamps) # Step 1: Extract and transform atmospheric data atmos_extract = EAMTransformAndExtract( diff --git a/quickview/plugins/eam_filters.py b/quickview/plugins/eam_filters.py index d7b78c3..6674245 100644 --- a/quickview/plugins/eam_filters.py +++ b/quickview/plugins/eam_filters.py @@ -271,7 +271,7 @@ def __init__(self): self.__Dims = -1 def SetZonalAverage(self, zonal): - print("Checked zonal : ", zonal) + pass def RequestData(self, request, inInfo, outInfo): global _has_deps From 1178004fa385ddbab8698b780640315c68f25e07 Mon Sep 17 00:00:00 2001 From: Abhishek Yenpure Date: Fri, 22 Aug 2025 14:41:08 -0700 Subject: [PATCH 7/7] =?UTF-8?q?Bump=20version:=200.1.16=20=E2=86=92=200.1.?= =?UTF-8?q?17?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- pyproject.toml | 2 +- quickview/__init__.py | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 850b29e..a2fecc7 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.1.16 +current_version = 0.1.17 commit = True tag = True diff --git a/pyproject.toml b/pyproject.toml index cdf7854..24e58ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "quickview" -version = "0.1.16" +version = "0.1.17" description = "An application to explore/analyze data for atmosphere component for E3SM" authors = [ {name = "Kitware Inc."}, diff --git a/quickview/__init__.py b/quickview/__init__.py index a1420d2..1bb4762 100644 --- a/quickview/__init__.py +++ b/quickview/__init__.py @@ -1,5 +1,5 @@ """QuickView: Visual Analysis for E3SM Atmosphere Data.""" -__version__ = "0.1.16" +__version__ = "0.1.17" __author__ = "Kitware Inc." __license__ = "Apache-2.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 7a7aa23..4f822e7 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "app" -version = "0.1.16" +version = "0.1.17" description = "QuickView: Visual Analyis for E3SM Atmosphere Data" authors = ["Kitware"] license = "" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 19390ae..ad4b738 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -7,7 +7,7 @@ }, "package": { "productName": "QuickView", - "version": "0.1.16" + "version": "0.1.17" }, "tauri": { "allowlist": {