Skip to content

Commit cd68268

Browse files
committed
feat: Make data probe display table of values
1 parent 4ce4301 commit cd68268

2 files changed

Lines changed: 143 additions & 44 deletions

File tree

‎src/e3sm_compareview/view_manager.py‎

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22
import math
33

4+
from e3sm_compareview.comparison import MULTI_SIM_COMPARISON_LABELS
45
from paraview.modules.vtkPVVTKExtensionsInteractionStyle import (
56
vtkPVInteractorStyle,
67
vtkPVTrackballZoom,
@@ -99,7 +100,7 @@ def __init__(self, server, source):
99100
colormaps.initialize(self.server)
100101

101102
self.state.hover_info = None
102-
self.state.hover_tooltip = None
103+
self.state.probe_table = None
103104

104105
self.ctrl.on_server_ready.add(self._post_init)
105106

@@ -114,18 +115,18 @@ def _post_init(self, *_, **__):
114115
def _on_hover(self, *_):
115116
with self.state:
116117
if not self.state.hover_info:
117-
self.state.hover_tooltip = None
118+
self.state.probe_table = None
118119
return
119120

120121
view = self._var2view.get(self.state.hover_info)
121122
if view is None:
122-
self.state.hover_tooltip = None
123+
self.state.probe_table = None
123124
return
124125

125126
x, y = self._render_window_interactor.GetEventPosition()
126127
self._picker.Pick(x, y, 0, view.renderer)
127128
if self._picker.cell_id < 0:
128-
self.state.hover_tooltip = None
129+
self.state.probe_table = None
129130
return
130131

131132
cell_id = self._picker.cell_id
@@ -137,7 +138,77 @@ def _on_hover(self, *_):
137138
array = cell_data.GetArray(index)
138139
data_info[array.name] = array.GetTuple(cell_id)
139140

140-
self.state.hover_tooltip = data_info
141+
def picked_value(array_name):
142+
values = data_info.get(array_name)
143+
if not values:
144+
return None
145+
value = values[0]
146+
try:
147+
value = float(value)
148+
except (TypeError, ValueError):
149+
return str(value)
150+
if math.isnan(value):
151+
return None
152+
return f"{value:.6g}"
153+
154+
active_variable = view.base_variable
155+
view_specs = self.get_view_specs(active_variable)
156+
if not view_specs:
157+
self.state.probe_table = None
158+
return
159+
160+
source_specs_by_path = {}
161+
if self.state.comparison_mode == "multi-sim":
162+
source_specs_by_path = {
163+
spec.get("path"): spec
164+
for spec in self.source.data_reader.get_view_specs(
165+
active_variable,
166+
"multi-sim",
167+
"source",
168+
)
169+
}
170+
171+
rows = []
172+
for row_spec in view_specs:
173+
row_label = row_spec.get("label", row_spec["array_name"])
174+
if self.state.comparison_mode == "multi-sim":
175+
row_label = row_label.rsplit(" (", 1)[0]
176+
if row_spec.get("role") == "control":
177+
row_label = f"{row_label} (ctrl)"
178+
179+
row = {
180+
"key": row_spec["array_name"],
181+
"label": row_label,
182+
"active": row_spec["array_name"] == self.state.hover_info,
183+
"display": picked_value(row_spec["array_name"]),
184+
"source_label": None,
185+
"source_display": None,
186+
}
187+
188+
if (
189+
self.state.comparison_mode == "multi-sim"
190+
and self.state.comparison_type != "source"
191+
and row_spec.get("role") != "control"
192+
):
193+
source_spec = source_specs_by_path.get(row_spec.get("path"))
194+
if source_spec is not None:
195+
row["source_label"] = source_spec.get("label")
196+
row["source_display"] = picked_value(
197+
source_spec["array_name"]
198+
)
199+
200+
rows.append(row)
201+
202+
self.state.probe_table = {
203+
"lat": data_info.get("lat", [None])[0],
204+
"lon": data_info.get("lon", [None])[0],
205+
"column_label": (
206+
f"{active_variable} ({MULTI_SIM_COMPARISON_LABELS.get(view.comparison_type, view.comparison_type)})"
207+
if self.state.comparison_mode == "multi-sim"
208+
else active_variable
209+
),
210+
"rows": rows,
211+
}
141212

142213
def _active_views(self):
143214
if self._active_configs:

‎src/e3sm_compareview/view_panel.py‎

Lines changed: 67 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -379,21 +379,6 @@ def _get_default_range(self):
379379
return self._get_multi_sim_default_range()
380380

381381
def _build_ui(self):
382-
probe_value_spec = None
383-
if (
384-
self.comparison_mode == "multi-sim"
385-
and self.comparison_type != "source"
386-
and self.role != "control"
387-
):
388-
for source_spec in self.source.data_reader.get_view_specs(
389-
self.base_variable,
390-
"multi-sim",
391-
"source",
392-
):
393-
if source_spec.get("path") == self.view_spec.get("path"):
394-
probe_value_spec = source_spec
395-
break
396-
397382
with DivLayout(
398383
self.server, template_name=self.name, connect_parent=False, classes="h-100"
399384
) as self.ui:
@@ -406,7 +391,7 @@ def _build_ui(self):
406391
tile=("active_layout !== 'auto_layout'",),
407392
raw_attrs=[f'data-field-name="{self.array_name}"'],
408393
v_on_mouseenter=f"hover_info = '{self.array_name}'",
409-
v_on_mouseleave="hover_info = null; hover_tooltip = null",
394+
v_on_mouseleave="hover_info = null; probe_table = null",
410395
):
411396
with v3.VRow(
412397
dense=True,
@@ -511,7 +496,7 @@ def _build_ui(self):
511496
),
512497
open_on_hover=False,
513498
model_value=(
514-
f"picking_mode && !!hover_tooltip && hover_info === '{self.array_name}'",
499+
f"picking_mode && !!probe_table && hover_info === '{self.array_name}'",
515500
),
516501
):
517502
with v3.Template(v_slot_activator="{props}"):
@@ -539,34 +524,77 @@ def _build_ui(self):
539524
v_on_wheel="window.scrollBy(0, $event.deltaY)",
540525
)
541526

542-
with v3.VTable(density="compact", theme="dark", striped="even"):
543-
with html.Tbody(v_if="picking_mode && !!hover_tooltip"):
544-
with html.Tr():
545-
with html.Td():
546-
v3.VIcon("mdi-target")
547-
with html.Td(
548-
classes="d-flex justify-space-between align-center",
549-
):
550-
html.Div(
551-
"lat: {{hover_tooltip?.lat?.[0]?.toFixed(4)}}",
552-
classes="pr-4",
553-
)
554-
html.Div(
555-
"lon: {{hover_tooltip?.lon?.[0]?.toFixed(4)}}",
556-
)
527+
with html.Div(
528+
v_if="picking_mode && !!probe_table",
529+
classes="px-3 pt-2 pb-1 d-flex align-center text-caption",
530+
):
531+
v3.VIcon("mdi-target", size="small", classes="mr-2")
532+
html.Div(
533+
"lat: {{ probe_table.lat == null ? 'N/A' : probe_table.lat.toFixed(4) }}",
534+
classes="pr-4",
535+
)
536+
html.Div(
537+
"lon: {{ probe_table.lon == null ? 'N/A' : probe_table.lon.toFixed(4) }}",
538+
)
557539

558-
with self.config.provide_as("config"):
540+
with html.Div(
541+
v_if="picking_mode && !!probe_table",
542+
style="max-width: 72vw; overflow-x: auto;",
543+
):
544+
with v3.VTable(
545+
density="compact",
546+
theme="dark",
547+
classes="text-no-wrap",
548+
):
549+
with html.Tbody():
559550
with html.Tr():
560-
html.Td("{{ config.label }}")
551+
html.Td("Simulation", classes="font-weight-bold")
561552
html.Td(
562-
f"{{{{hover_tooltip?.['{self.array_name}']?.[0]}}}}"
553+
"{{ probe_table.column_label }}",
554+
classes="font-weight-bold text-blue-lighten-3",
563555
)
564-
if probe_value_spec is not None:
565-
with html.Tr():
566-
html.Td(probe_value_spec["label"])
556+
557+
with html.Tr(
558+
v_for="row in probe_table.rows",
559+
key="row.key",
560+
):
567561
html.Td(
568-
f"{{{{hover_tooltip?.['{probe_value_spec['array_name']}']?.[0]}}}}"
562+
"{{ row.label }}",
563+
style=(
564+
"""
565+
{
566+
fontWeight: row.active ? 700 : null,
567+
color: row.active ? '#90caf9' : null,
568+
}
569+
""",
570+
),
569571
)
572+
with html.Td(
573+
style=(
574+
"""
575+
{
576+
backgroundColor: row.active ? '#1e3a8a' : null,
577+
border: row.active ? '1px solid #90caf9' : null,
578+
}
579+
""",
580+
),
581+
):
582+
html.Div(
583+
"{{ row.display || 'N/A' }}",
584+
style=(
585+
"""
586+
{
587+
fontWeight: row.active ? 700 : null,
588+
color: row.active ? '#ffffff' : null,
589+
}
590+
""",
591+
),
592+
)
593+
html.Div(
594+
"{{ row.source_label }}: {{ row.source_display || 'N/A' }}",
595+
v_if="row.source_label",
596+
classes="text-caption text-medium-emphasis",
597+
)
570598

571599
with self.colormap.provide_as(self.name):
572600
colormaps.HorizontalScalarBar(self.name, popup_location="top")

0 commit comments

Comments
 (0)