Skip to content

3D, views, recent files, background, axis limits, copy and paste shortcuts, fixes#4

Merged
SimonHH merged 22 commits into
devfrom
claude/add-axis-limits-fHfBp
May 7, 2026
Merged

3D, views, recent files, background, axis limits, copy and paste shortcuts, fixes#4
SimonHH merged 22 commits into
devfrom
claude/add-axis-limits-fHfBp

Conversation

@SimonHH
Copy link
Copy Markdown
Owner

@SimonHH SimonHH commented May 4, 2026

Summary

This PR adds a full 3D view, a Z/color variable selector, named Views with portable .pdvview files, a unified Recent Files menu, background images behind plots, manual axis/z limits, and standard Ctrl+C / Ctrl+V shortcuts. It also bundles GUI layout fixes, miscellaneous bug fixes, expanded documentation, and a post-review fix pass.

Base: dev ← Head: claude/add-axis-limits-fHfBp — 16 files changed, +3,933 / −284 (22 commits).


Features

3D view & Z/color variable

  • Adds a third (Z) variable to the selection panel, used both as a color scale on 2D scatter and as the third axis in 3D mode.
  • Curve-type combo gains Scatter and Scatter+Line when a Z column is selected without 3D enabled (2D+Z mode).
  • Full 3D view with Surface and Scatter sub-types (Scatter is the default).
  • Dedicated Rotate toggle button in the toolbar; left-click pan in 3D matches 2D behaviour.
  • Home button and orthographic plane reset (XY/XZ/YZ).
  • x/y/z axis constraints, logZ, flip-z, and a single shared colorbar per axis (no more duplicated colorbars on redraw).
  • 3D camera state is preserved across redraws.

Named Views (.pdvview)

  • Save and restore named views capturing column selection (X/Y/Z), plot settings, sub-panel state, pipeline actions (Filter/Resample/Bin/Mask), 3D view state (logZ, flipZ, cb3D), and loader options.
  • Export views to portable .pdvview JSON files with relative data-file paths, importable via drag-and-drop or the Views menu.
  • Apply view to current table resolves saved column names against whichever table is selected.
  • Missing tables/columns produce warnings instead of crashes.

Recent Files

  • Single Recent Files submenu (cap 30, newest first) covering: opened data files, imported .pdvview files, exported view files, exported tables, and pasted files.

Background image

  • New BG toolbar button with Load / Paste / Clear menu.
  • Two modes: Fixed (fills the plot area) and Moving with axes (glued to data coordinates).

Manual axis & z limits

  • xmin/xmax/ymin/ymax text fields added to the Esthetics panel.
  • zmin/zmax fields appear in 2D+Z and 3D modes.
  • Z-limit fields drive the color scale in 2D+Z scatter; sync correctly when AutoScale toggles.

Copy & paste shortcuts

  • Ctrl+V accepts file paths (data, .pdvview, images) or raw bitmaps from the clipboard. Shift+Ctrl+V appends to existing tables instead of replacing.
  • Ctrl+C is context-aware: columns list → TSV; tables list → TSV; plot canvas → PNG bitmap; stats panel → clipboard.
  • Implemented via EVT_CHAR_HOOK so native Ctrl+C/V in TextCtrl / SearchCtrl / ComboBox keep working.

GUI layout fixes

  • Long column names no longer blow out the selection-panel width: ListBox/ComboBox subclassed to clamp DoGetBestSize.
  • SP_LIVE_UPDATE enabled on both splitters — no more XOR tracker line on click-and-hold.
  • Persistent per-panel absolute widths: mode switches restore saved widths; widths are a UI preference and are intentionally not coupled to named views (restoring a view leaves the current panel layout untouched).
  • Window resize keeps panels at their stored widths; only the plot area absorbs slack.

Miscellaneous bug fixes

  • Pipeline save/delete and table __repr__ typos.
  • saveOptions typo'd parameter name.
  • Loader options (dayfirst, naming) now persisted on shutdown.
  • DC bin dropped when converting FFT to Period.
  • Mask plugin IndexError on empty dataframes — switched to dtype-based timestamp detection.
  • Column filter TypeError against np.array column indices.

Post-review fixes (pre-merge)

  • W1 main.py — removed dead onRestoreViewFromCombo method (referenced a non-existent comboViews widget).
  • W2 GUIPlotPanel'1.75' was missing from LWChoices in restoreViewData; restoring a view saved with LineWidth=1.75 previously raised ValueError silently.
  • W3 GUIPlotPanelcaptureViewData now reads plot3D_type from the live cbCurveType widget (not the always-hidden cbPlot3D), so 3D type is correctly saved.
  • M1 GUISelectionPanel — off-by-one iFilt<=leniFilt<len in setGUIColumns; passing len(columnsY) to SetSelection is out-of-bounds.
  • M2 main.py — removed dead self.restore_formulas (real mechanism uses formulas_backup).
  • M3 GUIToolBox — removed unreachable docstring after return in GetKeyString.
  • M4 GUIToolBox — removed debug print('MPL VERSION:') that fired on every toolbar instantiation.
  • Tests expanded from 52 → 61 cases (TestPlotPanelViewData: LWChoices consistency, curveType save/restore, axis-limits dict structure, JSON round-trip).

3D interaction fixes (post-review)

  • 3D panning — left-drag in pan mode now correctly shifts all three axis limits using camera-oriented screen vectors (derived from ax.azim/ax.elev), instead of incorrectly setting button_pressed=2 which triggered zoom in modern matplotlib.
  • 3D right-click zoom — right-drag in pan or rotate mode now zooms exponentially about the axis centre, consistent with the 2D convention.
  • Pan/Rotate mutual exclusivity — activating one toggle now forces the other off (with defensive ToggleTool calls to keep visuals in sync); toggling Rotate off returns to zoom mode rather than auto-activating pan.
  • Tooltips updated: Pan shows When-on/When-off sections; Rotate shows "left rotates, right zooms".

View save/restore fixes (post-review)

  • Unique table keys — when two files share the same basename (e.g. /dir1/data.csv and /dir2/data.csv), captureViewState now uses a unique key (full tab.name) instead of the short name, preventing the second table's selection from silently overwriting the first.
  • Panel widths decoupled from views — sash widths are no longer captured in captureViewState / restored in restoreViewState; old .pdvview files that already contain a sashWidths key are unaffected (key is ignored on load).
  • Tests expanded to 71 cases (unique-key round-trip, duplicate-basename disambiguation).

claude added 16 commits March 27, 2026 11:31
Adds a third variable (Z) to the selection panel with colour scale, enabling
coloured 2-D scatter plots and a full 3-D view.  Polishes the Z/colour UI and
3-D mouse interaction, and fixes Ctrl+rotate using button_pressed reset.
…vview files

Introduces the Views feature: named views (column selection + plot settings)
can be saved and restored at any time.  Views can be exported to portable
.pdvview JSON files with relative data-file paths, and re-imported via
drag-and-drop or the Views menu.  Missing tables/columns on restore produce
warnings rather than crashes.

Extends to cover: Z/color column selection, 3D view state (log-z, flip-z,
cb3D), pipeline actions (Filter/Resample/Bin/Mask), all sub-panel settings
and loader options.  Adds "Apply view to current table" which resolves saved
column names on whichever table is selected.  Fixes crashes, list-deselect
TypeError, compare-with-1-series, multi-y column drop, formula restore, and
drag-drop/Ctrl+drag parity.

https://claude.ai/code/session_01RK7XoUAyBGq81RqNdX7htm
…olorbar

Replaces CTRL-rotate with a dedicated Rotate toggle button in the toolbar.
Adds orthographic plane-reset, Home button, and 3D left-click pan mode
matching 2D behaviour.  Adds x/y/z axis constraint support.  Includes 6 UI
improvements: view menu submenus, surf/scatter choice, single shared colorbar
per axis, plane reset, and Scatter as default 3D type.  Improves control-
panel layout and error robustness throughout.

https://claude.ai/code/session_01RK7XoUAyBGq81RqNdX7htm
…ombo

Cancels debounce timer in empty(), guards against np.array column indices
(TypeError fix).  Adds Scatter/Scatter+Line choices to the curve-type combo
when a Z column is selected but 3-D view is off.
…ries)

The Recent Files submenu now tracks: opened data files, imported .pdvview
files, exported view files, and exported tables — all in a single list,
newest first, capped at 30.  Clicking a .pdvview entry calls
load_view_file(); all others call load_files().

https://claude.ai/code/session_01RK7XoUAyBGq81RqNdX7htm
Each panel now stores its own absolute pixel width.  Window resize only
affects the last panel (absorbs slack); other panels keep their width.
Dragging a sash updates only the panel to its left via GetSashIdx().
Mode switches (1/2/3 columns) restore saved widths instead of resetting to
equal.  Widths are persisted in view save/restore via stable panel names.

https://claude.ai/code/session_01RK7XoUAyBGq81RqNdX7htm
Covers when to use the tool, installation, all supported file formats,
GUI layout, selection/plot/pipeline/view features, export options,
keyboard shortcuts, and common-task quick reference.

https://claude.ai/code/session_01RK7XoUAyBGq81RqNdX7htm
Previously the last panel was never stored in _panelWidths so dragging
the sash to its left had no persistent effect — the panel snapped back
to "remainder" on the next resize.

Changes:
- _savePanelWidths now records ALL panels including the last
- _restorePanelWidths scales all panels proportionally when the window
  is resized (instead of letting only the last panel absorb slack)
- onSashChange also saves the last panel's width after every drag

Result: every panel has a stored width; resizing the window maintains
proportions; no panel is treated as a passive remainder.

https://claude.ai/code/session_01RK7XoUAyBGq81RqNdX7htm
- Set vSplitter sash gravity to 0 so the left selection panel keeps
  its width when the window is resized
- Stop MultiSplit from scaling sub-panels proportionally on EVT_SIZE
- Remove automatic sash repositioning from the resize/idle handler;
  layout updates still happen when switching column modes

https://claude.ai/code/session_01TEDdcedUgmSE1yVF33MFYE
- Fix pipeline save/delete and table repr typos (GUIPipelinePanel,
  pipeline, Tables)
- Fix saveOptions typo'd parameter name (Tables)
- Persist loader options (dayfirst, naming) on shutdown (appdata)
- Drop DC bin when converting FFT to Period (plotdata)
- Fix mask plugin IndexError on empty dataframes by using dtype-based
  timestamp detection instead of df.iloc[0,i]; surface the underlying
  exception in applyMaskString for better debugging (data_mask, Tables)

https://claude.ai/code/session_01RusnXtXqo9DDz4dX2rUhXB
- Fix selection-panel width blowing out with long column names by
  subclassing ListBox/ComboBox to clamp DoGetBestSize (GUISelectionPanel)
- Restore MultiSplit onParentChangeSize to call _restorePanelWidths
  and Skip the event so base class handles resize (GUIMultiSplit)
- Set MinimumPaneSize and SashGravity before SplitVertically so the
  initial sash position is not silently overridden (GUIFields1D)
- Enable SP_LIVE_UPDATE on both outer vSplitter and tSplitter to
  eliminate the XOR tracker line on click-and-hold (GUIFields1D)
- Don't clear the filter search box text on Ctrl+C (GUISelectionPanel)

https://claude.ai/code/session_01RusnXtXqo9DDz4dX2rUhXB
Axis limits:
- Add xmin/xmax/ymin/ymax text fields to EstheticsPanel
- Add zmin/zmax fields for 2D+Z and 3D modes
- Flatten EstheticsPanel to a single WrapSizer; hide optional panels
  reliably on first open via plotsizer.Hide()

Background image:
- Add BG toolbar button with Load/Paste/Clear menu
- Two modes: 'Fixed' (default, fills plot area via imshow +
  xlim/ylim-changed callbacks) and 'Moving with axes' (glued to data
  coords captured on switch)
- IMAGE_EXTS constant shared with main.py for recent-files routing

Z-axis / color-scale:
- Use z-axis limit fields for color scale in 2D+Z scatter mode
- Fix color-scale / z-limit sync when AutoScale toggles
- Exclude datetime Z columns from color-scale path
- Preserve 3D camera state across redraws; enable logZ; dedupe
  colorbars

https://claude.ai/code/session_01RusnXtXqo9DDz4dX2rUhXB
Ctrl+V (paste): accepts file paths (data, .pdvview, images) or raw
bitmaps from the clipboard. File paths are routed through a shared
_routeFilenames helper (also used by drag-and-drop). Shift+Ctrl+V
adds to existing tables instead of replacing. All pasted files
appear in the Recent Files menu.

Ctrl+C (copy): dispatches based on last-focused pane:
- Columns list → TSV of selected X/Y/Z columns across tables
- Tables list → TSV of all columns for selected tables
- Plot canvas → PNG bitmap of the current figure
- Stats panel → reuses existing CopyToClipBoard method

Implementation uses EVT_CHAR_HOOK (not AcceleratorTable) so native
Ctrl+C/V in TextCtrl/SearchCtrl/ComboBox is preserved. Visible
File menu entries added for discoverability. Status bar feedback
on every successful paste/copy.

Removes the auto-copy-on-select binding in GUIInfoPanel that
silently overwrote the clipboard on every stats row click.

https://claude.ai/code/session_01RusnXtXqo9DDz4dX2rUhXB
…tation

PR #2 (now in dev) contained an earlier, simpler Z/color and 3D implementation.
PR #4's 13 commits are the polished, final version that supersedes PR #2 entirely.

Conflicts resolved in 3 files (13 conflict blocks total) by keeping HEAD:
- GUIPlotPanel.py: _patch_3d_ctrl_rotate (3-layer toolbar-aware approach),
  ColorCtrlPanel (btFree + cbPlot3D + camera state), plotSignals (shared
  z_norm, logZ/flipZ, single colorbar per axis), _store/_restore_limits
  (3D camera + zlim preservation).
- GUISelectionPanel.py: _ClampedComboBox for comboZ, zSel parameter handling.
- plotdata.py: auto-merged cleanly (no manual resolution needed).

https://claude.ai/code/session_01TRfZkFW9YczDyobkh3cndo
…s, 3D controls

- Append 3D navigation note (Rotate, plane views, Home) to Z/color bullet
- Add 5 new Workflow bullets: BG image, manual axis limits, Ctrl+V paste,
  context-aware Ctrl+C copy, Recent Files
- Add 4 bullets to Features list covering same new capabilities
- Remove duplicate Z/color and 3D view entries from Plot options section

https://claude.ai/code/session_01TRfZkFW9YczDyobkh3cndo
W2 — GUIPlotPanel.py: add missing '1.75' to LWChoices in restoreViewData;
     restoring a view saved with LineWidth=1.75 previously raised ValueError.

W3 — GUIPlotPanel.py: captureViewData now reads plot3D_type from the live
     cbCurveType widget (not the always-hidden cbPlot3D), so 3D type is
     correctly captured when saving a view in 3D mode.

W1 — main.py: remove dead onRestoreViewFromCombo method; it referenced a
     comboViews widget that was never created (views are restored via menu
     lambdas calling onRestoreView directly).

M1 — GUISelectionPanel.py: fix off-by-one iFilt<=len → iFilt<len in
     setGUIColumns; passing len(columnsY) to SetSelection is out-of-bounds.

M2 — main.py: remove dead self.restore_formulas (real mechanism uses
     formulas_backup).

M3 — GUIToolBox.py: remove unreachable docstring after return in GetKeyString.

M4 — GUIToolBox.py: remove debug print('MPL VERSION:') that fired on every
     toolbar instantiation.

tests: add TestPlotPanelViewData (9 headless tests) covering W2 LWChoices
consistency, W3 curveType-over-plot3D_type restore path, axis limits dict
structure, and axis limits JSON round-trip. Total: 52 → 61 tests.

https://claude.ai/code/session_01TRfZkFW9YczDyobkh3cndo
@SimonHH SimonHH closed this May 5, 2026
@SimonHH SimonHH deleted the claude/add-axis-limits-fHfBp branch May 5, 2026 14:22
@SimonHH SimonHH restored the claude/add-axis-limits-fHfBp branch May 5, 2026 14:25
@SimonHH SimonHH reopened this May 5, 2026
claude and others added 6 commits May 5, 2026 21:19
Two bugs fixed:

1. _toggle_rotate (GUIToolBox.py): when the Rotate toggle was turned OFF
   it activated zoom mode, so left-drag box-zoomed rather than panned.
   Now it activates pan mode instead, making the tooltip text ("When off:
   left-drag pans, right-drag zooms") accurate.

2. _patch_3d_ctrl_rotate (GUIPlotPanel.py): the pan implementation set
   ax.button_pressed=2 hoping Axes3D would pan, but button 2 triggers
   zoom in modern matplotlib.  Replaced with a proper manual pan: on
   left-press in pan mode the start position and axis limits are stored;
   on each mouse-move the screen delta is projected onto the camera's
   screen-right / screen-up vectors (derived from ax.azim / ax.elev)
   and applied as a shift to xlim3d / ylim3d / zlim3d.  x/y/z key
   constraints are preserved.  A button-release handler clears the pan
   state.  A fallback motion handler is added for old matplotlib builds
   where _on_move is not found in canvas callbacks.

https://claude.ai/code/session_01GNbfFRd1uPxiEHSHYZkE6t
When two files with the same basename were loaded from different
directories (e.g. /dir1/data.csv and /dir2/data.csv), _tab_shortname
returned 'data' for both.  captureViewState keyed tabSelectionsFull by
shortname so the second table's entry silently overwrote the first,
causing wrong y-axis and z-axis selections after view restore.

Add _unique_tab_keys(tab_list) which uses the portable shortname when it
is unique across all loaded tables, and falls back to the full tab.name
when two or more tables share the same shortname.  Use this helper
consistently for tabSelectedNames, tabSelectionsFull, and formulas_state
in captureViewState.  The restore path (_find_tab_by_key, t.name
fallback in table-selection rebuild) already handles full-name keys
correctly, so no restore-side changes are needed.

https://claude.ai/code/session_01JQiNFHSjaNDv2VpPE2DzLV
- Right-drag in 3D pan or rotate mode now zooms (exponential scale about
  axis centre, matching matplotlib 2D convention) instead of doing nothing
- Pan and Rotate buttons are now fully mutually exclusive: activating one
  forces the other off, with defensive ToggleTool calls to keep visuals in sync
- Rotate-off no longer auto-activates pan; returns to default zoom mode
- Tooltips updated: Pan shows When-on/When-off sections; Rotate shows
  "Rotation for 3D: left rotates, right zooms"

https://claude.ai/code/session_01GNbfFRd1uPxiEHSHYZkE6t
Panel (sash) widths are a UI/workspace preference and should not be
coupled to named views.  Restoring a view was overriding the user's
current panel layout, which is unexpected.

Remove the sashWidths capture block from captureViewState and the
corresponding restore block from restoreViewState.  Old view files that
already contain a sashWidths key are unaffected — the key is simply
never read.

https://claude.ai/code/session_01JQiNFHSjaNDv2VpPE2DzLV
fix 3d panning
fix view restore duplicates
@SimonHH SimonHH merged commit c68c1b4 into dev May 7, 2026
@SimonHH SimonHH deleted the claude/add-axis-limits-fHfBp branch May 7, 2026 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants