Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.1.1
current_version = 0.1.2
commit = True
tag = True

Expand Down
9 changes: 0 additions & 9 deletions .github/workflows/package-and-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,3 @@ jobs:
releaseDraft: true
prerelease: false
args: ${{ matrix.args }}

- name: Create git tag
if:
matrix.platform == 'macos-latest' && matrix.args == '--target
aarch64-apple-darwin'
run: |
git tag v${{ env.VERSION }}
git push origin v${{ env.VERSION }}
shell: bash -l {0}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "quickview"
version = "0.1.1"
version = "0.1.2"
description = "An application to explore/analyze data for atmosphere component for E3SM"
authors = [
{name = "Kitware Inc."},
Expand Down
2 changes: 1 addition & 1 deletion quickview/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""QuickView: Visual Analysis for E3SM Atmosphere Data."""

__version__ = "0.1.1"
__version__ = "0.1.2"
__author__ = "Kitware Inc."
__license__ = "Apache-2.0"
88 changes: 84 additions & 4 deletions quickview/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing import Union

from trame.app import get_server
from trame.decorators import TrameApp, life_cycle
from trame.decorators import TrameApp, life_cycle, trigger
from trame.ui.vuetify import SinglePageWithDrawerLayout

from trame.widgets import vuetify as v2, html, client
Expand Down Expand Up @@ -84,6 +84,8 @@
"use_cvd_colors",
"use_standard_colors",
"show_color_bar",
# Grid layout
"layout",
]


Expand Down Expand Up @@ -117,6 +119,7 @@ def __init__(
ctrl = server.controller

self._ui = None
self._cached_layout = {} # Cache for layout positions by variable name

self.workdir = workdir
self.server = server
Expand Down Expand Up @@ -269,16 +272,73 @@ def update_state_from_config(self, initstate):
self.viewmanager.registry = build_color_information(initstate)
self.load_variables()

@trigger("layout_changed")
def on_layout_changed_trigger(self, layout, **kwargs):
"""Cache layout changes to ensure they are properly saved"""
# Cache the layout data with variable names as keys for easier lookup
self._cached_layout = {}
if layout and hasattr(self.state, "variables"):
for item in layout:
if isinstance(item, dict) and "i" in item:
idx = item["i"]
if idx < len(self.state.variables):
var_name = self.state.variables[idx]
self._cached_layout[var_name] = {
"x": item.get("x", 0),
"y": item.get("y", 0),
"w": item.get("w", 4),
"h": item.get("h", 3),
}

def generate_state(self):
# Force state synchronization
self.state.flush()

all = self.state.to_dict()
to_export = {k: all[k] for k in save_state_keys}
# with open(os.path.join(self.workdir, "state.json"), "w") as outfile:

# Convert cached layout back to array format for saving
if self._cached_layout and hasattr(self.state, "variables"):
layout_array = []
for idx, var_name in enumerate(self.state.variables):
if var_name in self._cached_layout:
pos = self._cached_layout[var_name]
layout_array.append(
{
"x": pos["x"],
"y": pos["y"],
"w": pos["w"],
"h": pos["h"],
"i": idx,
}
)
if layout_array:
to_export["layout"] = layout_array

return to_export

def load_state(self, state_file):
from_state = json.loads(Path(state_file).read_text())
data_file = from_state["data_file"]
conn_file = from_state["conn_file"]
# Convert loaded layout to variable-name-based cache
self._cached_layout = {}
if (
"layout" in from_state
and from_state["layout"]
and "variables" in from_state
):
for item in from_state["layout"]:
if isinstance(item, dict) and "i" in item:
idx = item["i"]
if idx < len(from_state["variables"]):
var_name = from_state["variables"][idx]
self._cached_layout[var_name] = {
"x": item.get("x", 0),
"y": item.get("y", 0),
"w": item.get("w", 4),
"h": item.get("h", 3),
}
self.source.Update(
data_file=data_file,
conn_file=conn_file,
Expand Down Expand Up @@ -336,7 +396,21 @@ def load_variables(self):
state.varmax = [np.nan] * len(vars)
state.override_range = [False] * len(vars)

self.viewmanager.rebuild_visualization_layout()
self.viewmanager.rebuild_visualization_layout(self._cached_layout)
# Update cached layout after rebuild
if state.layout and state.variables:
self._cached_layout = {}
for item in state.layout:
if isinstance(item, dict) and "i" in item:
idx = item["i"]
if idx < len(state.variables):
var_name = state.variables[idx]
self._cached_layout[var_name] = {
"x": item.get("x", 0),
"y": item.get("y", 0),
"w": item.get("w", 4),
"h": item.get("h", 3),
}

def update_view_color_settings(self, index, type, value):
with self.state as state:
Expand Down Expand Up @@ -594,7 +668,13 @@ def ui(self) -> SinglePageWithDrawerLayout:

with layout.content:
with grid.GridLayout(
layout=("layout", []),
layout=("layout"),
col_num=12,
row_height=100,
is_draggable=True,
is_resizable=True,
vertical_compact=True,
layout_updated="layout = $event; trigger('layout_changed', [$event])",
):
with grid.GridItem(
v_for="vref, idx in views",
Expand Down
12 changes: 9 additions & 3 deletions quickview/ui/toolbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ async def select_connectivity_file(self):

@task
async def export_state(self):
# Small delay to ensure client state is synchronized
import asyncio

await asyncio.sleep(0.1)

if self._generate_state is not None:
config = self._generate_state()
with self.state:
Expand Down Expand Up @@ -109,6 +114,7 @@ def __init__(

with layout_toolbar as toolbar:
toolbar.density = "compact"
toolbar.style = "overflow-x: auto; overflow-y: hidden;"
v2.VDivider(vertical=True, classes="mx-2")
v2.VBtn(
"Load Variables",
Expand All @@ -126,7 +132,7 @@ def __init__(
with v2.VCard(
flat=True,
classes="d-flex align-center px-2 py-1 mx-1",
style="background-color: #f5f5f5; border-radius: 4px;",
style="background-color: #f5f5f5; border-radius: 4px; flex-shrink: 0;",
):
with v2.VTooltip(bottom=True):
with html.Template(v_slot_activator="{ on, attrs }"):
Expand Down Expand Up @@ -175,7 +181,7 @@ def __init__(
with v2.VCard(
flat=True,
classes="d-flex align-center px-2 py-1 mx-1",
style="background-color: #f5f5f5; border-radius: 4px; min-width: 35%;",
style="background-color: #f5f5f5; border-radius: 4px; min-width: 35%; flex-shrink: 1;",
):
with v2.VTooltip(bottom=True):
with html.Template(v_slot_activator="{ on, attrs }"):
Expand Down Expand Up @@ -268,7 +274,7 @@ def __init__(
with v2.VCard(
flat=True,
classes="d-flex align-center px-2 py-1 mx-1",
style="background-color: #f5f5f5; border-radius: 4px;",
style="background-color: #f5f5f5; border-radius: 4px; flex-shrink: 0;",
):
with v2.VTooltip(bottom=True):
with html.Template(v_slot_activator="{ on, attrs }"):
Expand Down
47 changes: 44 additions & 3 deletions quickview/view_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ def build_color_information(state: map):
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):
Expand All @@ -215,6 +217,11 @@ def build_color_information(state: map):
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


Expand Down Expand Up @@ -408,7 +415,7 @@ def compute_range(self, var, vtkdata=None):
vardata = vtkdata.GetCellData().GetArray(var)
return vardata.GetRange()

def rebuild_visualization_layout(self):
def rebuild_visualization_layout(self, cached_layout=None):
self.widgets.clear()
state = self.state
source = self.source
Expand All @@ -434,6 +441,26 @@ def rebuild_visualization_layout(self):
data = self.source.views["atmosphere_data"]
vtkdata = sm.Fetch(data)

# Use cached layout if provided, or fall back to saved layout in registry
layout_map = cached_layout if cached_layout else {}

# If no cached layout, check if we have saved layout in registry
if not layout_map and hasattr(self.registry, "_saved_layout"):
# Convert saved layout array to variable-name-based map
temp_map = {}
for item in self.registry._saved_layout:
if isinstance(item, dict) and "i" in item:
idx = item["i"]
if hasattr(state, "variables") and idx < len(state.variables):
var_name = state.variables[idx]
temp_map[var_name] = {
"x": item.get("x", 0),
"y": item.get("y", 0),
"w": item.get("w", 4),
"h": item.get("h", 3),
}
layout_map = temp_map

del self.state.views[:]
del self.state.layout[:]
del self.widgets[:]
Expand All @@ -444,8 +471,20 @@ def rebuild_visualization_layout(self):

view0 = None
for index, var in enumerate(to_render):
x = int(index % 3) * wdt
y = int(index / 3) * hgt
# Check if we have saved position for this variable
if var in layout_map:
# Use saved position
pos = layout_map[var]
x = pos["x"]
y = pos["y"]
wdt = pos["w"]
hgt = pos["h"]
else:
# 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)
Expand Down Expand Up @@ -510,6 +549,7 @@ def rebuild_visualization_layout(self):
)
self.widgets.append(widget)
sWidgets.append(widget.ref_name)
# Use index as identifier to maintain compatibility with grid expectations
layout.append({"x": x, "y": y, "w": wdt, "h": hgt, "i": index})

for var in to_delete:
Expand All @@ -518,6 +558,7 @@ def rebuild_visualization_layout(self):
self.state.views = sWidgets
self.state.layout = layout
self.state.dirty("views")
self.state.dirty("layout")
# from trame.app import asynchronous
# asynchronous.create_task(self.flushViews())

Expand Down
2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "app"
version = "0.1.1"
version = "0.1.2"
description = "QuickView: Visual Analyis for E3SM Atmosphere Data"
authors = ["Kitware"]
license = ""
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
"package": {
"productName": "QuickView",
"version": "0.1.1"
"version": "0.1.2"
},
"tauri": {
"allowlist": {
Expand Down
Loading