From 8d3b919ef68206bc4a123d986407f798e1f4e990 Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Fri, 12 Sep 2025 23:28:23 -0700 Subject: [PATCH] jotpluggler: better defaults for zooming/fitting (#36149) better defaults for zooming/fitting --- tools/jotpluggler/pluggle.py | 11 +++++++++-- tools/jotpluggler/views.py | 32 ++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/tools/jotpluggler/pluggle.py b/tools/jotpluggler/pluggle.py index 61b0a09718..b831a2f154 100755 --- a/tools/jotpluggler/pluggle.py +++ b/tools/jotpluggler/pluggle.py @@ -66,6 +66,7 @@ class PlaybackManager: self.is_playing = False self.current_time_s = 0.0 self.duration_s = 0.0 + self.num_segments = 0 self.x_axis_bounds = (0.0, 0.0) # (min_time, max_time) self.x_axis_observers = [] # callbacks for x-axis changes @@ -131,6 +132,7 @@ class MainController: self.data_tree = DataTree(self.data_manager, self.playback_manager) self.layout_manager = LayoutManager(self.data_manager, self.playback_manager, self.worker_manager, scale=self.scale) self.data_manager.add_observer(self.on_data_loaded) + self._total_segments = 0 def _create_global_themes(self): with dpg.theme(tag="line_theme"): @@ -158,10 +160,15 @@ class MainController: duration = data.get('duration', 0.0) self.playback_manager.set_route_duration(duration) - if data.get('reset'): + if data.get('metadata_loaded'): + self.playback_manager.num_segments = data.get('total_segments', 0) + self._total_segments = data.get('total_segments', 0) + dpg.set_value("load_status", f"Loading... 0/{self._total_segments} segments processed") + elif data.get('reset'): self.playback_manager.current_time_s = 0.0 self.playback_manager.duration_s = 0.0 self.playback_manager.is_playing = False + self._total_segments = 0 dpg.set_value("load_status", "Loading...") dpg.set_value("timeline_slider", 0.0) dpg.configure_item("timeline_slider", max_value=0.0) @@ -173,7 +180,7 @@ class MainController: dpg.configure_item("load_button", enabled=True) elif data.get('segment_added'): segment_count = data.get('segment_count', 0) - dpg.set_value("load_status", f"Loading... {segment_count} segments processed") + dpg.set_value("load_status", f"Loading... {segment_count}/{self._total_segments} segments processed") dpg.configure_item("timeline_slider", max_value=duration) diff --git a/tools/jotpluggler/views.py b/tools/jotpluggler/views.py index 09bba74930..1c4d9a8f3c 100644 --- a/tools/jotpluggler/views.py +++ b/tools/jotpluggler/views.py @@ -63,6 +63,7 @@ class TimeSeriesPanel(ViewPanel): self._last_x_limits = (0.0, 0.0) self._queued_x_sync: tuple | None = None self._queued_reallow_x_zoom = False + self._total_segments = self.playback_manager.num_segments def to_dict(self) -> dict: return { @@ -89,6 +90,7 @@ class TimeSeriesPanel(ViewPanel): dpg.bind_item_theme(timeline_series_tag, "timeline_theme") self._new_data = True + self._queued_x_sync = self.playback_manager.x_axis_bounds self._ui_created = True def update(self): @@ -107,7 +109,19 @@ class TimeSeriesPanel(ViewPanel): if self._queued_reallow_x_zoom: self._queued_reallow_x_zoom = False - dpg.set_axis_limits_auto(self.x_axis_tag) + if tuple(dpg.get_axis_limits(self.x_axis_tag)) == self._last_x_limits: + dpg.set_axis_limits_auto(self.x_axis_tag) + else: + self._queued_x_sync = self._last_x_limits # retry, likely too early + return + + if self._new_data: # handle new data in main thread + self._new_data = False + if self._total_segments > 0: + dpg.set_axis_limits_constraints(self.x_axis_tag, -10, self._total_segments * 60 + 10) + self._fit_y_axis(*dpg.get_axis_limits(self.x_axis_tag)) + for series_path in list(self._series_data.keys()): + self.add_series(series_path, update=True) current_limits = dpg.get_axis_limits(self.x_axis_tag) # downsample if plot zoom changed significantly @@ -120,12 +134,6 @@ class TimeSeriesPanel(ViewPanel): self._last_x_limits = current_limits self._fit_y_axis(current_limits[0], current_limits[1]) - if self._new_data: # handle new data in main thread - self._new_data = False - dpg.set_axis_limits_constraints(self.x_axis_tag, -10, (self.playback_manager.duration_s + 10)) - for series_path in list(self._series_data.keys()): - self.add_series(series_path, update=True) - while self._results_deque: # handle downsampled results in main thread results = self._results_deque.popleft() for series_path, downsampled_time, downsampled_values in results: @@ -223,8 +231,7 @@ class TimeSeriesPanel(ViewPanel): else: line_series_tag = dpg.add_line_series(x=time_array, y=value_array.astype(float), label=series_path, parent=self.y_axis_tag, tag=series_tag) dpg.bind_item_theme(line_series_tag, "line_theme") - dpg.fit_axis_data(self.x_axis_tag) - dpg.fit_axis_data(self.y_axis_tag) + self._fit_y_axis(*dpg.get_axis_limits(self.x_axis_tag)) plot_duration = dpg.get_axis_limits(self.x_axis_tag)[1] - dpg.get_axis_limits(self.x_axis_tag)[0] self._downsample_all_series(plot_duration) @@ -252,7 +259,12 @@ class TimeSeriesPanel(ViewPanel): del self._series_data[series_path] def on_data_loaded(self, data: dict): - self._new_data = True + with self._update_lock: + self._new_data = True + if data.get('metadata_loaded'): + self._total_segments = data.get('total_segments', 0) + limits = (-10, self._total_segments * 60 + 10) + self._queued_x_sync = limits def _on_series_drop(self, sender, app_data, user_data): self.add_series(app_data)