batplot 1.8.0__py3-none-any.whl → 1.8.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of batplot might be problematic. Click here for more details.
- batplot/__init__.py +1 -1
- batplot/args.py +5 -3
- batplot/batplot.py +44 -4
- batplot/cpc_interactive.py +96 -3
- batplot/electrochem_interactive.py +28 -0
- batplot/interactive.py +18 -2
- batplot/modes.py +12 -12
- batplot/operando.py +2 -0
- batplot/operando_ec_interactive.py +112 -11
- batplot/session.py +35 -1
- batplot/utils.py +40 -0
- batplot/version_check.py +85 -6
- {batplot-1.8.0.dist-info → batplot-1.8.2.dist-info}/METADATA +1 -1
- batplot-1.8.2.dist-info/RECORD +75 -0
- {batplot-1.8.0.dist-info → batplot-1.8.2.dist-info}/top_level.txt +1 -0
- batplot_backup_20251221_101150/__init__.py +5 -0
- batplot_backup_20251221_101150/args.py +625 -0
- batplot_backup_20251221_101150/batch.py +1176 -0
- batplot_backup_20251221_101150/batplot.py +3589 -0
- batplot_backup_20251221_101150/cif.py +823 -0
- batplot_backup_20251221_101150/cli.py +149 -0
- batplot_backup_20251221_101150/color_utils.py +547 -0
- batplot_backup_20251221_101150/config.py +198 -0
- batplot_backup_20251221_101150/converters.py +204 -0
- batplot_backup_20251221_101150/cpc_interactive.py +4409 -0
- batplot_backup_20251221_101150/electrochem_interactive.py +4520 -0
- batplot_backup_20251221_101150/interactive.py +3894 -0
- batplot_backup_20251221_101150/manual.py +323 -0
- batplot_backup_20251221_101150/modes.py +799 -0
- batplot_backup_20251221_101150/operando.py +603 -0
- batplot_backup_20251221_101150/operando_ec_interactive.py +5487 -0
- batplot_backup_20251221_101150/plotting.py +228 -0
- batplot_backup_20251221_101150/readers.py +2607 -0
- batplot_backup_20251221_101150/session.py +2951 -0
- batplot_backup_20251221_101150/style.py +1441 -0
- batplot_backup_20251221_101150/ui.py +790 -0
- batplot_backup_20251221_101150/utils.py +1046 -0
- batplot_backup_20251221_101150/version_check.py +253 -0
- batplot-1.8.0.dist-info/RECORD +0 -52
- {batplot-1.8.0.dist-info → batplot-1.8.2.dist-info}/WHEEL +0 -0
- {batplot-1.8.0.dist-info → batplot-1.8.2.dist-info}/entry_points.txt +0 -0
- {batplot-1.8.0.dist-info → batplot-1.8.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""Plotting helpers for batplot.
|
|
2
|
+
|
|
3
|
+
This module contains utility functions for positioning and styling plot labels.
|
|
4
|
+
Labels are the text annotations that identify each curve (e.g., file names).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import List
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def update_labels(ax, y_data_list: List, label_text_objects: List, stack_mode: bool, stack_label_at_bottom: bool = False):
|
|
14
|
+
"""
|
|
15
|
+
Update positions and colors of curve labels on a plot.
|
|
16
|
+
|
|
17
|
+
HOW LABEL POSITIONING WORKS:
|
|
18
|
+
---------------------------
|
|
19
|
+
Labels identify which curve is which (e.g., "file1.xy", "file2.xy").
|
|
20
|
+
This function positions them intelligently based on the plot mode:
|
|
21
|
+
|
|
22
|
+
STACK MODE (stack_mode=True):
|
|
23
|
+
-----------------------------
|
|
24
|
+
When curves are stacked vertically (--stack flag), each label is placed
|
|
25
|
+
next to its corresponding curve at the right edge of the plot.
|
|
26
|
+
|
|
27
|
+
Positioning:
|
|
28
|
+
- X position: Right edge of plot (x_max in data coordinates)
|
|
29
|
+
- Y position:
|
|
30
|
+
* If stack_label_at_bottom=False: At curve maximum (top of curve)
|
|
31
|
+
* If stack_label_at_bottom=True: At curve minimum + 10% offset (bottom of curve)
|
|
32
|
+
|
|
33
|
+
Coordinate system: Data coordinates (matches curve data)
|
|
34
|
+
Example: If curve goes from y=0 to y=100, label is at (x_max, 100) or (x_max, 10)
|
|
35
|
+
|
|
36
|
+
NORMAL MODE (stack_mode=False):
|
|
37
|
+
--------------------------------
|
|
38
|
+
When curves are overlaid (not stacked), labels form a vertical list
|
|
39
|
+
at the right edge of the plot.
|
|
40
|
+
|
|
41
|
+
Positioning:
|
|
42
|
+
- X position: Right edge (1.0 in axes coordinates = 100% of plot width)
|
|
43
|
+
- Y position: Evenly spaced vertical list
|
|
44
|
+
* If stack_label_at_bottom=False: Top-right, spacing downward
|
|
45
|
+
* If stack_label_at_bottom=True: Bottom-right, spacing upward
|
|
46
|
+
|
|
47
|
+
Coordinate system: Axes coordinates (0.0 to 1.0, independent of data range)
|
|
48
|
+
Example: First label at (1.0, 0.98), second at (1.0, 0.90), etc.
|
|
49
|
+
|
|
50
|
+
COLOR MATCHING:
|
|
51
|
+
--------------
|
|
52
|
+
Each label's text color is set to match its corresponding curve color.
|
|
53
|
+
This makes it easy to identify which label belongs to which curve.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
ax: Matplotlib axes object containing the plot
|
|
57
|
+
y_data_list: List of y-data arrays, one per curve
|
|
58
|
+
Used to find curve min/max for stack mode positioning
|
|
59
|
+
label_text_objects: List of matplotlib Text objects (the labels)
|
|
60
|
+
stack_mode: True if curves are stacked, False if overlaid
|
|
61
|
+
stack_label_at_bottom: If True, place labels at bottom (for stack mode)
|
|
62
|
+
or bottom-right (for normal mode)
|
|
63
|
+
"""
|
|
64
|
+
# Early return if no labels to update
|
|
65
|
+
if not label_text_objects:
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
# ====================================================================
|
|
69
|
+
# STACK MODE: Labels positioned next to each curve
|
|
70
|
+
# ====================================================================
|
|
71
|
+
# In stack mode, curves are separated vertically. Each label is placed
|
|
72
|
+
# at the right edge of the plot, aligned with its corresponding curve.
|
|
73
|
+
fig = getattr(ax, 'figure', None)
|
|
74
|
+
label_anchor_left = bool(getattr(fig, '_label_anchor_left', False)) if fig is not None else False
|
|
75
|
+
|
|
76
|
+
# ====================================================================
|
|
77
|
+
if stack_mode:
|
|
78
|
+
# Get plot edges in data coordinates
|
|
79
|
+
x_min, x_max = ax.get_xlim()
|
|
80
|
+
x_pos = x_min if label_anchor_left else x_max
|
|
81
|
+
ha = 'left' if label_anchor_left else 'right'
|
|
82
|
+
|
|
83
|
+
# Position each label next to its curve
|
|
84
|
+
for i, txt in enumerate(label_text_objects):
|
|
85
|
+
# Check if we have data for this curve
|
|
86
|
+
if i < len(y_data_list) and len(y_data_list[i]) > 0:
|
|
87
|
+
# We have actual data, use curve min/max
|
|
88
|
+
if stack_label_at_bottom:
|
|
89
|
+
# Place label at bottom of curve with small upward offset
|
|
90
|
+
# This prevents label from being hidden at the very bottom
|
|
91
|
+
y_min = float(np.min(y_data_list[i])) # Bottom of curve
|
|
92
|
+
y_max = float(np.max(y_data_list[i])) # Top of curve
|
|
93
|
+
y_range = y_max - y_min
|
|
94
|
+
# Position 10% above minimum (small offset for visibility)
|
|
95
|
+
y_pos_curve = y_min + (0.1 * y_range)
|
|
96
|
+
else:
|
|
97
|
+
# Place label at top of curve (default)
|
|
98
|
+
y_pos_curve = float(np.max(y_data_list[i]))
|
|
99
|
+
else:
|
|
100
|
+
# No data available, use plot limits as fallback
|
|
101
|
+
if stack_label_at_bottom:
|
|
102
|
+
y_lim_min = ax.get_ylim()[0] # Bottom of plot
|
|
103
|
+
y_lim_max = ax.get_ylim()[1] # Top of plot
|
|
104
|
+
y_lim_range = y_lim_max - y_lim_min
|
|
105
|
+
# Position 10% above bottom of plot
|
|
106
|
+
y_pos_curve = y_lim_min + (0.1 * y_lim_range)
|
|
107
|
+
else:
|
|
108
|
+
y_pos_curve = ax.get_ylim()[1] # Top of plot
|
|
109
|
+
|
|
110
|
+
# Set coordinate system to data coordinates (matches curve positions)
|
|
111
|
+
# transData means positions are in the same units as the plot data
|
|
112
|
+
txt.set_transform(ax.transData)
|
|
113
|
+
|
|
114
|
+
# Set label position (right edge, aligned with curve)
|
|
115
|
+
txt.set_position((x_pos, y_pos_curve))
|
|
116
|
+
txt.set_ha(ha)
|
|
117
|
+
txt.set_va('bottom' if stack_label_at_bottom else 'top')
|
|
118
|
+
|
|
119
|
+
# Set label color to match curve color (makes identification easier)
|
|
120
|
+
try:
|
|
121
|
+
if i < len(ax.lines):
|
|
122
|
+
# Get color from corresponding line object
|
|
123
|
+
txt.set_color(ax.lines[i].get_color())
|
|
124
|
+
except Exception:
|
|
125
|
+
# If color matching fails, keep default color
|
|
126
|
+
pass
|
|
127
|
+
# ====================================================================
|
|
128
|
+
# NORMAL MODE: Labels form vertical list at right edge
|
|
129
|
+
# ====================================================================
|
|
130
|
+
# In normal mode (overlaid curves), labels are stacked vertically
|
|
131
|
+
# at the right edge of the plot. They use axes coordinates (0.0 to 1.0)
|
|
132
|
+
# so they stay in the same position even if data range changes.
|
|
133
|
+
# ====================================================================
|
|
134
|
+
else:
|
|
135
|
+
n = len(label_text_objects)
|
|
136
|
+
|
|
137
|
+
# Padding from edges (in axes coordinates, where 0.0 = bottom, 1.0 = top)
|
|
138
|
+
top_pad = 0.02 # 2% padding from top
|
|
139
|
+
bottom_pad = 0.05 # 5% padding from bottom (more to avoid x-axis labels)
|
|
140
|
+
|
|
141
|
+
# Calculate spacing between labels
|
|
142
|
+
# Formula: Distribute 90% of vertical space evenly among labels
|
|
143
|
+
# Clamp between 0.025 (minimum) and 0.08 (maximum) for readability
|
|
144
|
+
spacing = min(0.08, max(0.025, 0.90 / max(n, 1)))
|
|
145
|
+
|
|
146
|
+
if stack_label_at_bottom:
|
|
147
|
+
# ============================================================
|
|
148
|
+
# BOTTOM-RIGHT POSITIONING
|
|
149
|
+
# ============================================================
|
|
150
|
+
# Labels start at bottom and stack upward.
|
|
151
|
+
# Useful when top of plot is crowded or you want labels near data.
|
|
152
|
+
# ============================================================
|
|
153
|
+
|
|
154
|
+
# Calculate available vertical space
|
|
155
|
+
available_space = 1.0 - bottom_pad - top_pad
|
|
156
|
+
|
|
157
|
+
# Calculate total height needed for all labels
|
|
158
|
+
total_height = (n - 1) * spacing if n > 1 else 0
|
|
159
|
+
|
|
160
|
+
# If labels would extend beyond top, compress spacing
|
|
161
|
+
# This ensures all labels fit even with many curves
|
|
162
|
+
if total_height > available_space:
|
|
163
|
+
spacing = available_space / max(n - 1, 1)
|
|
164
|
+
|
|
165
|
+
# Start from bottom and stack upward
|
|
166
|
+
start_y = bottom_pad
|
|
167
|
+
for i, txt in enumerate(label_text_objects):
|
|
168
|
+
y_pos = start_y + i * spacing # Each label higher than previous
|
|
169
|
+
|
|
170
|
+
# Ensure we stay within bounds (safety check)
|
|
171
|
+
if y_pos > 1.0 - top_pad:
|
|
172
|
+
y_pos = 1.0 - top_pad
|
|
173
|
+
|
|
174
|
+
# Use axes coordinates (0.0 to 1.0, independent of data)
|
|
175
|
+
# This keeps labels in same position even if data range changes
|
|
176
|
+
txt.set_transform(ax.transAxes)
|
|
177
|
+
|
|
178
|
+
# Position at right edge (1.0 = 100% of plot width)
|
|
179
|
+
txt.set_position((0.0 if label_anchor_left else 1.0, y_pos))
|
|
180
|
+
txt.set_ha('left' if label_anchor_left else 'right')
|
|
181
|
+
txt.set_va('bottom')
|
|
182
|
+
|
|
183
|
+
# Match label color to curve color
|
|
184
|
+
try:
|
|
185
|
+
if i < len(ax.lines):
|
|
186
|
+
txt.set_color(ax.lines[i].get_color())
|
|
187
|
+
except Exception:
|
|
188
|
+
pass
|
|
189
|
+
else:
|
|
190
|
+
# ============================================================
|
|
191
|
+
# TOP-RIGHT POSITIONING (DEFAULT)
|
|
192
|
+
# ============================================================
|
|
193
|
+
# Labels start at top and stack downward.
|
|
194
|
+
# This is the default behavior and works well for most plots.
|
|
195
|
+
# ============================================================
|
|
196
|
+
|
|
197
|
+
# Start from top and stack downward
|
|
198
|
+
start_y = 1.0 - top_pad # Start just below top edge
|
|
199
|
+
for i, txt in enumerate(label_text_objects):
|
|
200
|
+
y_pos = start_y - i * spacing # Each label lower than previous
|
|
201
|
+
|
|
202
|
+
# Ensure we stay within bounds (safety check)
|
|
203
|
+
if y_pos < top_pad:
|
|
204
|
+
y_pos = top_pad
|
|
205
|
+
|
|
206
|
+
# Use axes coordinates (0.0 to 1.0, independent of data)
|
|
207
|
+
txt.set_transform(ax.transAxes)
|
|
208
|
+
|
|
209
|
+
# Position at right edge (1.0 = 100% of plot width)
|
|
210
|
+
txt.set_position((0.0 if label_anchor_left else 1.0, y_pos))
|
|
211
|
+
txt.set_ha('left' if label_anchor_left else 'right')
|
|
212
|
+
txt.set_va('top')
|
|
213
|
+
|
|
214
|
+
# Match label color to curve color
|
|
215
|
+
try:
|
|
216
|
+
if i < len(ax.lines):
|
|
217
|
+
txt.set_color(ax.lines[i].get_color())
|
|
218
|
+
except Exception:
|
|
219
|
+
pass
|
|
220
|
+
|
|
221
|
+
# ====================================================================
|
|
222
|
+
# REDRAW PLOT
|
|
223
|
+
# ====================================================================
|
|
224
|
+
# After updating label positions, tell matplotlib to redraw the plot.
|
|
225
|
+
# draw_idle() schedules a redraw when the GUI is ready (more efficient
|
|
226
|
+
# than immediate draw()).
|
|
227
|
+
# ====================================================================
|
|
228
|
+
ax.figure.canvas.draw_idle()
|