From 47160967ed1df932d577dcdc94e5e71f618b1c49 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 29 Jul 2026 08:33:37 -0400 Subject: [PATCH 01/12] plot_info: +get_sublabels --- src/lib/plotting/plot_info.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/plotting/plot_info.py b/src/lib/plotting/plot_info.py index 4b76348..4b363b2 100644 --- a/src/lib/plotting/plot_info.py +++ b/src/lib/plotting/plot_info.py @@ -50,8 +50,11 @@ def get_coord_label(self, dim: DimKey) -> Latex: maybe_space = "\\ " if unit else "" return Latex(f"{display} = {coord_val:.3f}{maybe_space}{unit}") + def get_sublabels(self) -> list[str]: + return [f"${self.get_coord_label(dim)}$" for dim in self.scalar_coord_values] + def get_title(self) -> str: - coord_labels_str = ", ".join(f"${self.get_coord_label(dim)}$" for dim in self.scalar_coord_values) + coord_labels_str = ", ".join(self.get_sublabels()) if self.subject and coord_labels_str: return f"{self.subject} ({coord_labels_str})" From fe0b00c79496703875094d96f31bb9a1d0070b33 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 29 Jul 2026 15:03:12 -0400 Subject: [PATCH 02/12] plot_info: +listeners --- src/lib/plotting/plot_info.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/plotting/plot_info.py b/src/lib/plotting/plot_info.py index 4b363b2..fdac3fe 100644 --- a/src/lib/plotting/plot_info.py +++ b/src/lib/plotting/plot_info.py @@ -1,5 +1,5 @@ from dataclasses import KW_ONLY, dataclass, field -from typing import Any, Callable, Literal +from typing import Any, Callable, Literal, Self import numpy as np from matplotlib.typing import LineStyleType @@ -28,6 +28,7 @@ class PlotInfo: projection: Projection = field(default="rectilinear", init=False) _setter_callbacks: dict[AttrKey | tuple[AttrKey, DimKey], Callable[[Any], None]] = field(default_factory=dict, init=False) + _listeners: list[Callable[[Self], None]] = field(default_factory=list, init=False) def set(self, key: AttrKey | tuple[AttrKey, DimKey], value: Any): if isinstance(key, str): @@ -71,6 +72,13 @@ def get_dim_label(self, dim: DimKey) -> str: return dim_label + def add_listener(self, listener: Callable[[Self], None]): + self._listeners.append(listener) + + def trigger_listeners(self): + for listener in self._listeners: + listener(self) + @dataclass class PlotInfo2D(PlotInfo): From a5bbf560236f83b45f7e4afc9f489bee203ebab8 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 29 Jul 2026 15:06:33 -0400 Subject: [PATCH 03/12] animated_plot: trigger listeners after update --- src/lib/plotting/animated_plot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/plotting/animated_plot.py b/src/lib/plotting/animated_plot.py index 2e80696..0d95735 100644 --- a/src/lib/plotting/animated_plot.py +++ b/src/lib/plotting/animated_plot.py @@ -32,6 +32,7 @@ def _initialize(self): def _next_frame(self, frame: int): for renderer in self.renderers: renderer.update_plot_info(frame) + renderer.plot_info.trigger_listeners() self.post_update_fig(DrawMessage(plot_info=self.renderers[0].plot_info, axes=self.fig.axes[0], frame_data=self.renderers[0]._get_data_at_frame(frame))) print_progress(frame, self.n_frames) From ae47aab1d4d8537cb20a24ddb4d3267f4dc87ff4 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 29 Jul 2026 15:43:03 -0400 Subject: [PATCH 04/12] +labeler: +TreeLabeler --- src/lib/plotting/labeler.py | 85 +++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/lib/plotting/labeler.py diff --git a/src/lib/plotting/labeler.py b/src/lib/plotting/labeler.py new file mode 100644 index 0000000..01f4f2c --- /dev/null +++ b/src/lib/plotting/labeler.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Callable + +from lib.plotting.plot_info import PlotInfo + + +@dataclass +class TreeLabeler: + set_text: Callable[[str], None] + source: PlotInfo | None = None + + children: list[TreeLabeler] = field(default_factory=list, init=False) + parent: TreeLabeler | None = field(default=None, init=False) + + _subject: str | None = field(default=None, init=False) + _sublabels: list[str] = field(default_factory=list, init=False) + + def add_child(self, child: TreeLabeler): + assert child.parent is None + child.parent = self + self.children.append(child) + + def update(self): + """Propagate updates up to the root labeler, which makes sure that everyone rebuilds and then everyone updates text.""" + if self.parent: + return self.parent.update() + else: + self._rebuild() + self._update_text() + + def _rebuild(self): + for child in self.children: + child._rebuild() + + child_subjects = {child._subject for child in self.children} + all_child_sublabels = {sublabel: None for child in self.children for sublabel in child._sublabels} # use dict to preserve insertion order + common_child_sublabels = {sublabel: None for sublabel in all_child_sublabels if all(sublabel in child._sublabels for child in self.children)} # use dict to preserve insertion order + + if self.source: + self._subject = self.source.subject + self._sublabels = self.source.get_sublabels() + + # only eliminate child subjects + sublabels if every child shares the root subject and all its sublabels + has_common_subject = {self._subject} == child_subjects + has_common_sublabels = set(self._sublabels) <= set(common_child_sublabels.keys()) + + if has_common_subject and has_common_sublabels: + self._eliminate_subject() + self._eliminate_common_sublabels() + + else: + # no source -> lift all common subject and/or sublabels independently + + if len(child_subjects) == 1: + self._subject = child_subjects.pop() + self._eliminate_subject() + else: + self._subject = None + + self._sublabels = list(common_child_sublabels.keys()) + self._eliminate_common_sublabels() + + def _update_text(self): + # set_text intelligently checks if the text actually changes or not + self.set_text(self._get_label()) + for child in self.children: + child._update_text() + + def _get_label(self) -> str: + sublabels = ", ".join(self._sublabels) + + if self._subject and sublabels: + return f"{self._subject} ({sublabels})" + return self._subject or sublabels + + def _eliminate_subject(self): + for child in self.children: + child._subject = None + + def _eliminate_common_sublabels(self): + for child in self.children: + for sublabel in self._sublabels: + child._sublabels.remove(sublabel) From 86f693f855625a15a9dd5911690e5cf8e3844580 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 29 Jul 2026 16:14:53 -0400 Subject: [PATCH 05/12] setup_fig: use TreeLabeler --- src/lib/plotting/setup_fig.py | 153 ++++++---------------------------- 1 file changed, 25 insertions(+), 128 deletions(-) diff --git a/src/lib/plotting/setup_fig.py b/src/lib/plotting/setup_fig.py index 73c7f6e..e600607 100644 --- a/src/lib/plotting/setup_fig.py +++ b/src/lib/plotting/setup_fig.py @@ -11,7 +11,8 @@ from matplotlib.text import Text from lib.plotting import plt_util -from lib.plotting.plot_info import DimKey, ImageInfo, LineInfo, PlotInfo, PlotInfo2D, PolarMeshInfo, ScatterInfo +from lib.plotting.labeler import TreeLabeler +from lib.plotting.plot_info import ImageInfo, LineInfo, PlotInfo, PlotInfo2D, PolarMeshInfo, ScatterInfo type AxesIdx = tuple[int, int] @@ -118,12 +119,9 @@ def __init__(self, ax: A, info: PI): self.info = info def setup_title(self): - update_title = UpdateText(self.ax.title, self.info) - self.info._setter_callbacks["subject"] = update_title - self.info._setter_callbacks["dim_displays"] = update_title - self.info._setter_callbacks["dim_units"] = update_title - self.info._setter_callbacks["scalar_coord_values"] = update_title - update_title() + self.labeler = TreeLabeler(self.ax.title.set_text, self.info) + self.labeler.update() + self.info.add_listener(lambda _: self.labeler.update()) class AxesManagerSingle2D[PI2D: PlotInfo2D](AxesManagerSingle[Axes, PI2D]): @@ -235,76 +233,23 @@ class AxesManagerMultiLine(AxesManager): def __init__(self, ax: Axes, infos: list[LineInfo]): self.ax = ax self.infos = infos - - self.common_coord_dims: set[DimKey] = set() - self.unique_coord_dimss: list[set[DimKey]] = [set() for _ in self.infos] - self._update_common_scalar_coordinates() - - self.common_subject: str | None = None - self._update_common_subject() - self.lines: list[Line2D] = [] # populated later - def _update_common_scalar_coordinates(self): - self.common_coord_dims.clear() - for unique_dims in self.unique_coord_dimss: - unique_dims.clear() - - all_dims: set[DimKey] = {dim for info in self.infos for dim in info.scalar_coord_values} - - for dim in all_dims: - if all(dim in info.scalar_coord_values for info in self.infos) and len({info.get_coord_label(dim) for info in self.infos}) == 1: - self.common_coord_dims.add(dim) - else: - for info, unique_dims in zip(self.infos, self.unique_coord_dimss): - if dim in info.scalar_coord_values: - unique_dims.add(dim) - - def _update_common_subject(self): - self.common_subject = _one_or_none(info.subject for info in self.infos) - - def _get_title(self) -> str: - # by definition, it shouldn't matter which info we use to construct this string - coord_labels_str = ", ".join(self.infos[0].get_coord_label(dim).maybe_with_dollars() for dim in self.common_coord_dims) - - if self.common_subject and coord_labels_str: - return f"{self.common_subject} ({coord_labels_str})" - return self.common_subject or coord_labels_str - - def _get_legend_labels(self) -> list[str]: - legend_labels: list[str] = [] - - for info, unique_dims in zip(self.infos, self.unique_coord_dimss): - coord_labels_str = ", ".join(info.get_coord_label(dim).maybe_with_dollars() for dim in unique_dims) - - if self.common_subject: - legend_labels.append(coord_labels_str) - elif info.subject and coord_labels_str: - legend_labels.append(f"{info.subject} ({coord_labels_str})") - else: - legend_labels.append(info.subject or coord_labels_str) - - return legend_labels - - def _update_title_and_legend(self, *_): - self._update_common_scalar_coordinates() - self._update_common_subject() - self.ax.set_title(self._get_title()) - for line, label in zip(self.lines, self._get_legend_labels()): - line.set_label(label) - def setup(self): - self.setup_title() self.setup_labels() self.setup_data() + self.setup_title() # after data, to make sure lines is populated self.setup_scales() self.setup_bounds() - for info in self.infos: - info._setter_callbacks["scalar_coord_values"] = self._update_title_and_legend - def setup_title(self): - self.ax.set_title(self._get_title()) + self.labeler = TreeLabeler(self.ax.title.set_text) + for info, line in zip(self.infos, self.lines): + line_labeler = TreeLabeler(line.set_label, info) + self.labeler.add_child(line_labeler) + info.add_listener(lambda _: self.labeler.update()) + self.labeler.update() + self.ax.legend() def setup_labels(self): x_labels = [info.get_dim_label(info.x_dim) for info in self.infos] @@ -340,15 +285,13 @@ def setup_bounds(self): self.ax.set_ybound(*find_widest_bounds(info.dim_bounds[info.y_dim] for info in self.infos)) def setup_data(self): - for info, label in zip(self.infos, self._get_legend_labels()): - [line] = self.ax.plot(info.x_data, info.y_data, linestyle=info.line_style, scalex=False, scaley=False, label=label) + for info in self.infos: + [line] = self.ax.plot(info.x_data, info.y_data, linestyle=info.line_style, scalex=False, scaley=False) info._setter_callbacks["x_data"] = line.set_xdata info._setter_callbacks["y_data"] = line.set_ydata info._setter_callbacks["line_style"] = line.set_linestyle self.lines.append(line) - self.ax.legend() - class AxesManagerImageAndLines(AxesManager): def __init__(self, ax: Axes, image_info: ImageInfo, line_infos: list[LineInfo]): @@ -357,67 +300,23 @@ def __init__(self, ax: Axes, image_info: ImageInfo, line_infos: list[LineInfo]): self.image_info = image_info self.line_infos = line_infos self.infos: list[PlotInfo2D] = [image_info, *line_infos] - - self.common_coord_dims: set[DimKey] = set() - self.unique_coord_dimss: list[set[DimKey]] = [set() for _ in self.line_infos] - self._update_common_scalar_coordinates() - self.lines: list[Line2D] = [] # populated later - def _update_common_scalar_coordinates(self): - self.common_coord_dims.clear() - for unique_dims in self.unique_coord_dimss: - unique_dims.clear() - - all_dims: set[DimKey] = {dim for info in self.infos for dim in info.scalar_coord_values} - - for dim in all_dims: - if all(dim in info.scalar_coord_values for info in self.infos) and len({info.get_coord_label(dim) for info in self.infos}) == 1: - self.common_coord_dims.add(dim) - else: - for info, unique_dims in zip(self.infos, self.unique_coord_dimss): - if dim in info.scalar_coord_values: - unique_dims.add(dim) - - def _get_title(self) -> str: - # by definition, it shouldn't matter which info we use to construct this string - coord_labels_str = ", ".join(self.line_infos[0].get_coord_label(dim).maybe_with_dollars() for dim in self.common_coord_dims) - - if self.image_info.subject and coord_labels_str: - return f"{self.image_info.subject} ({coord_labels_str})" - return self.image_info.subject or coord_labels_str - - def _get_legend_labels(self) -> list[str]: - legend_labels: list[str] = [] - - for info, unique_dims in zip(self.line_infos, self.unique_coord_dimss): - coord_labels_str = ", ".join(info.get_coord_label(dim).maybe_with_dollars() for dim in unique_dims) - - if info.subject and coord_labels_str: - legend_labels.append(f"{info.subject} ({coord_labels_str})") - else: - legend_labels.append(info.subject or coord_labels_str) - - return legend_labels - - def _update_title_and_legend(self, *_): - self._update_common_scalar_coordinates() - self.image_ax.set_title(self._get_title()) - for line, label in zip(self.lines, self._get_legend_labels()): - line.set_label(label) - def setup(self): - self.setup_title() self.setup_labels() self.setup_data() + self.setup_title() # after data to get line info self.setup_scales() self.setup_bounds() - for info in self.infos: - info._setter_callbacks["scalar_coord_values"] = self._update_title_and_legend - def setup_title(self): - self.image_ax.set_title(self._get_title()) + self.labeler = TreeLabeler(self.image_ax.title.set_text, self.image_info) + for info, line in zip(self.line_infos, self.lines): + line_labeler = TreeLabeler(line.set_label, info) + self.labeler.add_child(line_labeler) + info.add_listener(lambda _: self.labeler.update()) + self.labeler.update() + self.line_ax.legend() def setup_labels(self): x_labels = [info.get_dim_label(info.x_dim) for info in self.infos] @@ -472,15 +371,13 @@ def setup_data(self): data_lower, data_upper = self.image_info.dim_bounds[self.image_info.color_dim] plt_util.update_cbar(image, data_min_override=data_lower, data_max_override=data_upper) - for info, label in zip(self.line_infos, self._get_legend_labels()): - [line] = self.line_ax.plot(info.x_data, info.y_data, linestyle=info.line_style, scalex=False, scaley=False, label=label) + for info in self.line_infos: + [line] = self.line_ax.plot(info.x_data, info.y_data, linestyle=info.line_style, scalex=False, scaley=False) info._setter_callbacks["x_data"] = line.set_xdata info._setter_callbacks["y_data"] = line.set_ydata info._setter_callbacks["line_style"] = line.set_linestyle self.lines.append(line) - self.line_ax.legend() - def setup_fig(plot_infos: list[PlotInfo]) -> Figure: figure = plt.figure(layout="constrained") From 5f3cec9fed84dc38a41ef84f802fb7455c6487f0 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 29 Jul 2026 16:23:37 -0400 Subject: [PATCH 06/12] test_plots: +test_image_and_cuts --- tests/baseline/test_image_and_cuts.png | Bin 0 -> 37822 bytes tests/test_plots.py | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 tests/baseline/test_image_and_cuts.png diff --git a/tests/baseline/test_image_and_cuts.png b/tests/baseline/test_image_and_cuts.png new file mode 100644 index 0000000000000000000000000000000000000000..3bc3672475889c8ff7d5adadd7c21915928e9f20 GIT binary patch literal 37822 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 z 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 Date: Wed, 29 Jul 2026 16:29:33 -0400 Subject: [PATCH 07/12] setup_fig: set cbar label --- src/lib/plotting/setup_fig.py | 13 ++++++++----- tests/baseline/test_image_and_line.png | Bin 25019 -> 24872 bytes 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/lib/plotting/setup_fig.py b/src/lib/plotting/setup_fig.py index e600607..63b1854 100644 --- a/src/lib/plotting/setup_fig.py +++ b/src/lib/plotting/setup_fig.py @@ -305,15 +305,18 @@ def __init__(self, ax: Axes, image_info: ImageInfo, line_infos: list[LineInfo]): def setup(self): self.setup_labels() self.setup_data() - self.setup_title() # after data to get line info + self.setup_title() # after data to get line info and cbar self.setup_scales() self.setup_bounds() def setup_title(self): - self.labeler = TreeLabeler(self.image_ax.title.set_text, self.image_info) + self.labeler = TreeLabeler(self.image_ax.title.set_text) + + self.labeler.add_child(TreeLabeler(self.cbar.set_label, self.image_info)) + self.image_info.add_listener(lambda _: self.labeler.update()) + for info, line in zip(self.line_infos, self.lines): - line_labeler = TreeLabeler(line.set_label, info) - self.labeler.add_child(line_labeler) + self.labeler.add_child(TreeLabeler(line.set_label, info)) info.add_listener(lambda _: self.labeler.update()) self.labeler.update() self.line_ax.legend() @@ -367,7 +370,7 @@ def setup_data(self): ) self.image_info._setter_callbacks["data"] = image.set_data - self.image_ax.figure.colorbar(image) + self.cbar = self.image_ax.figure.colorbar(image) data_lower, data_upper = self.image_info.dim_bounds[self.image_info.color_dim] plt_util.update_cbar(image, data_min_override=data_lower, data_max_override=data_upper) diff --git a/tests/baseline/test_image_and_line.png b/tests/baseline/test_image_and_line.png index 7c5bb9189345426cb8c8dd8556454920cda6c582..fbb971c72d546ab771348e2973554e250de975d6 100644 GIT binary patch literal 24872 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 azH{E1%yu*8lRb@>6rEoTj^=4PT>U?=C0*SB literal 25019 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 Sz61x4qW+8a>6DWuKK}*m6pNbx From 00df4e8b110b48efc32bfa4b39e6ffeef660b4b5 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 29 Jul 2026 16:37:58 -0400 Subject: [PATCH 08/12] labeler: +comment --- src/lib/plotting/labeler.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/plotting/labeler.py b/src/lib/plotting/labeler.py index 01f4f2c..9cec722 100644 --- a/src/lib/plotting/labeler.py +++ b/src/lib/plotting/labeler.py @@ -8,6 +8,12 @@ @dataclass class TreeLabeler: + """Manages the labels associated with one or more datasets within a figure. A label comprises an optional subject + (variable name) and any number of sublabels (e.g. scalar coordinates). When multiple datasets are plotted within + the same figure, common label components can be "factored out" to a higher label location, e.g. from a legend to + an axis title. Label locations are well-described by a tree structure, where common label components propagate + from the leaves to the root.""" + set_text: Callable[[str], None] source: PlotInfo | None = None From 202a9061f8ddb11ef1246fa908c23c04a22a128b Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 29 Jul 2026 16:54:52 -0400 Subject: [PATCH 09/12] +renderer2 --- src/lib/plotting/renderer2.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/lib/plotting/renderer2.py diff --git a/src/lib/plotting/renderer2.py b/src/lib/plotting/renderer2.py new file mode 100644 index 0000000..3e386f8 --- /dev/null +++ b/src/lib/plotting/renderer2.py @@ -0,0 +1,6 @@ +from abc import ABC, abstractmethod + + +class Renderer2(ABC): + @abstractmethod + def update(self): ... From 78b372b6cd718417db06d4ff53534d2ac54eed12 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 29 Jul 2026 16:55:01 -0400 Subject: [PATCH 10/12] labeler: is Renderer2 --- src/lib/plotting/labeler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/plotting/labeler.py b/src/lib/plotting/labeler.py index 9cec722..eb1c98c 100644 --- a/src/lib/plotting/labeler.py +++ b/src/lib/plotting/labeler.py @@ -4,10 +4,11 @@ from typing import Callable from lib.plotting.plot_info import PlotInfo +from lib.plotting.renderer2 import Renderer2 @dataclass -class TreeLabeler: +class TreeLabeler(Renderer2): """Manages the labels associated with one or more datasets within a figure. A label comprises an optional subject (variable name) and any number of sublabels (e.g. scalar coordinates). When multiple datasets are plotted within the same figure, common label components can be "factored out" to a higher label location, e.g. from a legend to From 8f2f85058da5a8214ea5a65134292bdccdee46e6 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 29 Jul 2026 16:55:43 -0400 Subject: [PATCH 11/12] setup_fig; *: pass renderer2s --- src/lib/plotting/animated_plot.py | 3 ++- src/lib/plotting/plot.py | 2 +- src/lib/plotting/setup_fig.py | 19 +++++++++++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/lib/plotting/animated_plot.py b/src/lib/plotting/animated_plot.py index 0d95735..acd596f 100644 --- a/src/lib/plotting/animated_plot.py +++ b/src/lib/plotting/animated_plot.py @@ -32,7 +32,8 @@ def _initialize(self): def _next_frame(self, frame: int): for renderer in self.renderers: renderer.update_plot_info(frame) - renderer.plot_info.trigger_listeners() + for renderer in self.renderer2s: + renderer.update() self.post_update_fig(DrawMessage(plot_info=self.renderers[0].plot_info, axes=self.fig.axes[0], frame_data=self.renderers[0]._get_data_at_frame(frame))) print_progress(frame, self.n_frames) diff --git a/src/lib/plotting/plot.py b/src/lib/plotting/plot.py index f5d9ae9..28d35f7 100644 --- a/src/lib/plotting/plot.py +++ b/src/lib/plotting/plot.py @@ -28,7 +28,7 @@ def _initialize(self): return self._initialized = True - self.fig = setup_fig([r.plot_info for r in self.renderers]) + self.fig, self.renderer2s = setup_fig([r.plot_info for r in self.renderers]) # TODO hooks should be per-renderer; for now, just apply them to the 1st one self.post_init_fig(DrawMessage(plot_info=self.renderers[0].plot_info, axes=self.fig.axes[0], frame_data=self.renderers[0]._get_data_at_frame(0))) diff --git a/src/lib/plotting/setup_fig.py b/src/lib/plotting/setup_fig.py index 63b1854..e71e5c8 100644 --- a/src/lib/plotting/setup_fig.py +++ b/src/lib/plotting/setup_fig.py @@ -13,6 +13,7 @@ from lib.plotting import plt_util from lib.plotting.labeler import TreeLabeler from lib.plotting.plot_info import ImageInfo, LineInfo, PlotInfo, PlotInfo2D, PolarMeshInfo, ScatterInfo +from lib.plotting.renderer2 import Renderer2 type AxesIdx = tuple[int, int] @@ -94,6 +95,9 @@ def __call__(self, *_): class AxesManager(ABC): + @abstractmethod + def get_renderers(self) -> list[Renderer2]: ... + @abstractmethod def setup(self): ... @@ -123,6 +127,9 @@ def setup_title(self): self.labeler.update() self.info.add_listener(lambda _: self.labeler.update()) + def get_renderers(self): + return [self.labeler] + class AxesManagerSingle2D[PI2D: PlotInfo2D](AxesManagerSingle[Axes, PI2D]): def setup(self): @@ -235,6 +242,9 @@ def __init__(self, ax: Axes, infos: list[LineInfo]): self.infos = infos self.lines: list[Line2D] = [] # populated later + def get_renderers(self): + return [self.labeler] + def setup(self): self.setup_labels() self.setup_data() @@ -302,6 +312,9 @@ def __init__(self, ax: Axes, image_info: ImageInfo, line_infos: list[LineInfo]): self.infos: list[PlotInfo2D] = [image_info, *line_infos] self.lines: list[Line2D] = [] # populated later + def get_renderers(self): + return [self.labeler] + def setup(self): self.setup_labels() self.setup_data() @@ -382,8 +395,9 @@ def setup_data(self): self.lines.append(line) -def setup_fig(plot_infos: list[PlotInfo]) -> Figure: +def setup_fig(plot_infos: list[PlotInfo]) -> tuple[Figure, list[Renderer2]]: figure = plt.figure(layout="constrained") + renderers = [] for ax, infos in _setup_axes(figure, plot_infos).values(): manager: AxesManager @@ -410,5 +424,6 @@ def setup_fig(plot_infos: list[PlotInfo]) -> Figure: raise NotImplementedError("don't yet support multiple non-line plots per axes") manager.setup() + renderers += manager.get_renderers() - return figure + return figure, renderers From 09d2380a12ff565f1cb011dddce6bc499007f7be Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 29 Jul 2026 16:58:20 -0400 Subject: [PATCH 12/12] plot_info; *: -_listeners --- src/lib/plotting/plot_info.py | 10 +--------- src/lib/plotting/setup_fig.py | 6 +----- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/lib/plotting/plot_info.py b/src/lib/plotting/plot_info.py index fdac3fe..4b363b2 100644 --- a/src/lib/plotting/plot_info.py +++ b/src/lib/plotting/plot_info.py @@ -1,5 +1,5 @@ from dataclasses import KW_ONLY, dataclass, field -from typing import Any, Callable, Literal, Self +from typing import Any, Callable, Literal import numpy as np from matplotlib.typing import LineStyleType @@ -28,7 +28,6 @@ class PlotInfo: projection: Projection = field(default="rectilinear", init=False) _setter_callbacks: dict[AttrKey | tuple[AttrKey, DimKey], Callable[[Any], None]] = field(default_factory=dict, init=False) - _listeners: list[Callable[[Self], None]] = field(default_factory=list, init=False) def set(self, key: AttrKey | tuple[AttrKey, DimKey], value: Any): if isinstance(key, str): @@ -72,13 +71,6 @@ def get_dim_label(self, dim: DimKey) -> str: return dim_label - def add_listener(self, listener: Callable[[Self], None]): - self._listeners.append(listener) - - def trigger_listeners(self): - for listener in self._listeners: - listener(self) - @dataclass class PlotInfo2D(PlotInfo): diff --git a/src/lib/plotting/setup_fig.py b/src/lib/plotting/setup_fig.py index e71e5c8..4b01b08 100644 --- a/src/lib/plotting/setup_fig.py +++ b/src/lib/plotting/setup_fig.py @@ -125,7 +125,6 @@ def __init__(self, ax: A, info: PI): def setup_title(self): self.labeler = TreeLabeler(self.ax.title.set_text, self.info) self.labeler.update() - self.info.add_listener(lambda _: self.labeler.update()) def get_renderers(self): return [self.labeler] @@ -257,7 +256,6 @@ def setup_title(self): for info, line in zip(self.infos, self.lines): line_labeler = TreeLabeler(line.set_label, info) self.labeler.add_child(line_labeler) - info.add_listener(lambda _: self.labeler.update()) self.labeler.update() self.ax.legend() @@ -326,11 +324,9 @@ def setup_title(self): self.labeler = TreeLabeler(self.image_ax.title.set_text) self.labeler.add_child(TreeLabeler(self.cbar.set_label, self.image_info)) - self.image_info.add_listener(lambda _: self.labeler.update()) - for info, line in zip(self.line_infos, self.lines): self.labeler.add_child(TreeLabeler(line.set_label, info)) - info.add_listener(lambda _: self.labeler.update()) + self.labeler.update() self.line_ax.legend()