feat: interactive annotations, vector preview, named data columns#2
Merged
Merged
Conversation
E1: GLE print-based calibration protocol injected into preview temp scripts only; per-axes data/cm/pixel transforms with log-axis support (PreviewGeometry, geometry_ready signal, never blocks rendering). E2: SVG vector preview as default with QtSvg validation, sticky PNG fallback and View-menu toggle; Cairo-safe font injection for SVG mode (GLE default PostScript font silently truncates Cairo output at exit 0); frozen view_mapping contract for the annotation overlay. E3: generated .dat sidecars carry sanitized named headers by default; column_names round-trip through model/writer/recognizer; GLE auto-key from headers neutralized with explicit key clauses (byte-identical rendering verified). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
F1: AnnotationOverlay on the live preview - transparent hit-areas over rendered text, drag with ghost and anti-jump sequencing around the debounced re-render, mid-render drag preservation, inline editing, add-text mode (shortcut T, Esc cancels), Delete removal, log-axis inverse mapping, pan suspension while hovering items. F2: TextsPanel property tab (content, position, color, size, alignment; honest disabled state for fields GLE never renders). MF integration: Texts tab wired with loop-free two-way selection sync between panel and overlay, surviving re-render rebuilds, with cross-axes retargeting. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… fix G1: double-click header renaming for figure-owned sidecars with full propagation (table, combos, series column_names, next save); external referenced files stay read-only - gleplot never rewrites user source data. G2: guide sections for on-canvas annotations, preview rendering modes, and named headers; README/rst sync; example regenerated with three synthetic annotations, verified warning-free parse and real GLE compile. Fix: .gitignore ignored *.gle and data_*.dat repo-wide, so the shipped example figure was never actually committed (docs referenced files absent from clones). Negation patterns added; example files tracked. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Data safety (critical): import-vs-reference classification now trusts only the gleplot metadata block; the filename heuristic is removed, so hand-written .gle files can never cause gleplot to rewrite a user data file (reviewer repro: md5-unchanged guarantee test). Loader: all- missing first rows are data not headers; multi-word whitespace headers fall back to positional names with a warning. Panel ownership resolves by sidecar basename, fixing stale propagation after Save As. Overlay: annotations disarm in read-only GLE-preview mode (drags there mutated the hidden document); undo/redo actions stay authoritative in preview mode; figure_replaced aborts active interactions (no phantom items after undo mid-drag); mapping fingerprints abort drags across PNG/SVG switches (no wrong-space commits); degenerate calibrations (non-positive log bounds, zero ranges) are skipped with warnings, not clamped; Texts panel rejects non-finite coordinates and no longer clobbers focused editors; orphaned-dict commits drop silently. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Text clusters: set hei/color/just are now optional and sticky across clusters (matching GLE interpreter semantics; writer suppresses redundant set lines to keep the fixed point); blank lines between clusters no longer abort recognition; TeX markup in write strings round-trips byte-exact. Column headers: GLE-style indexed comment blocks (! c N = name) are recovered as column names with full-coverage validation and unicode preserved; last-comment-line fallback retained; both display-only so vouched sidecar round-trips stay byte-identical. Texts panel: selection changes during list rebuilds are explicitly guarded (spurious currentRowChanged during QListWidget.clear); remove flow guard aligned; field-isolation audit encoded as tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR extends the gleplot GUI editor with (1) interactive on-canvas text annotations backed by preview calibration geometry, (2) SVG vector preview with a sticky PNG fallback, and (3) named data-column headers for generated .dat sidecars (including safe parsing/round-tripping and UI header renaming), along with related safety hardening in the recognizer and GUI mode handling.
Changes:
- Added a calibration/geometry pipeline to map data↔page↔view coordinates and enable interactive annotation editing/selection sync in the GUI.
- Switched live preview to SVG by default (when available) with validation-driven sticky fallback to PNG and a UI toggle.
- Introduced named sidecar column headers, key-clause neutralization for GLE auto-key behavior, and Data dock header renaming with ownership rules + persistence.
Reviewed changes
Copilot reviewed 37 out of 37 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/test_text.py | Verifies writer sticky-state behavior for set just/set color defaults vs non-defaults. |
| tests/unit/test_column_names.py | Unit coverage for column-name sanitization, writer header emission, and recognizer round-trips. |
| tests/parser/test_recognizer.py | Updates recognizer tests for sticky text semantics and conservative import/reference classification. |
| tests/integration/test_graphics_compilation.py | Real-GLE integration proving header rows don’t change rendered PNG output. |
| tests/gui/test_svg_preview.py | End-to-end GUI tests for SVG preview, validation, and automatic PNG fallback. |
| tests/gui/test_preview.py | Extends GUI preview tests for render-format plumbing and geometry-reset behavior on failures. |
| tests/gui/test_main_window_fixes.py | Regression tests for preview-mode safety hardening (overlay disabled, undo actions stay disabled). |
| tests/gui/test_integration.py | E2E test for dragging an annotation, saving, and verifying persistence after re-parse. |
| tests/gui/test_header_editing.py | GUI tests for Data dock header renaming rules, validation, propagation, and persistence. |
| tests/gui/test_geometry.py | Pure-logic unit tests for calibration math + parser tolerance/validation. |
| tests/gui/test_data_loader.py | Loader regressions for header detection (all-missing row, mismatch handling, comment-header recovery). |
| tests/gui/test_calibration_integration.py | Real-GLE integration tests for calibration injection and geometry emission ordering. |
| tests/gui/test_annotation_sync.py | Integration tests for Texts tab ↔ overlay selection synchronization and rebuild behavior. |
| src/gleplot/writer.py | Implements named sidecar headers, key suppression, and sticky text-state emission behavior. |
| src/gleplot/parser/recognizer.py | Recognizes sticky text clusters, recovers column names from sidecars, and handles key-suppression markers. |
| src/gleplot/gui/panels/init.py | Exposes the new TextsPanel in the GUI panels package API. |
| src/gleplot/gui/main_window.py | Wires SVG toggle, annotation overlay, Texts panel, selection sync, and preview-mode hardening. |
| src/gleplot/gui/geometry.py | New Qt-free geometry/calibration layer and parser for gleplot-cal protocol lines. |
| src/gleplot/gui/data/panel.py | Adds header rename UX, ownership checks, propagation into series column_names, and signals/tooltips. |
| src/gleplot/figure.py | Passes per-series column_names through to writer methods during .gle emission. |
| src/gleplot/axes.py | Introduces column-name sanitization + default column_names schema across series; regen defaults for old projects. |
| README.md | Updates GUI feature list to include annotations, SVG preview, and named columns. |
| examples/gui/README.md | Refreshes example walkthrough to include annotations and named header rows. |
| examples/gui/data_1.dat | Adds a named header row to the example model sidecar. |
| examples/gui/data_0.dat | Adds a named header row to the example measured-data sidecar. |
| examples/gui/damped_oscillation.gle | Updates example .gle to include annotations and updated ranges/series emission. |
| examples/gui/damped_oscillation.csv | Updates example CSV values to match regenerated example assets. |
| docs/source/gui.rst | Documents SVG preview/fallback, annotations, named columns, and exports unaffected by preview substitutions. |
| docs/guides/GUI_EDITOR.md | Adds extensive user documentation for annotations, SVG preview behavior, and header renaming/ownership rules. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+556
to
+563
| if not path.is_absolute(): | ||
| if base is None: | ||
| continue | ||
| path = base / path | ||
| if not path.exists(): | ||
| continue | ||
| if os.path.normcase(str(path.resolve())) == os.path.normcase(key): | ||
| return True |
Comment on lines
512
to
+516
| bar_cmd = f' bar {d_name} fill {bar_color}' | ||
| self.lines_gle.append(bar_cmd) | ||
|
|
||
| if column_names and not label: | ||
| self.lines_gle.append(f' {d_name} key ""') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #1, stacked on
feature/pyside-gui-editor. Three user-facing features plus the safety hardening that surfaced while building them.Interactive annotations on the preview
T) places new ones with a clickprintstatements reporting each axes' exact data→page transform (parsed from the compile's own output — zero extra cost, log-axis aware, multi-subplot aware); calibration lines exist only in the temp preview copy, never in saved filesset hei/just/coloroptional + sticky across clusters), so hand-authored annotation blocks parse fully — including TeX markup like{\it{T}}, preserved byte-exactlyVector (SVG) preview
Named data columns
.datsidecars carry a sanitized header row (x signal errinstead of anonymous columns); GLE's auto-key-from-header behavior neutralized with explicit key clauses (byte-identical rendering verified)! c N = namecomment blocks and plain comment headers (display-only, conservative validation, unicode preserved)Safety hardening (adversarial review gate, 15 confirmed findings fixed)
.gitignorefix: repo-wide*.glepattern had silently excluded the shipped example (also fixed on feat: PySide6 GUI editor with native .gle format #1)Test plan
🤖 Generated with Claude Code