pyredshift 1.13__tar.gz → 1.15__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyredshift
3
- Version: 1.13
3
+ Version: 1.15
4
4
  Summary: Interactive redshifting of 1D astronomical spectra (Python successor of pdlredshift / redshift.f)
5
5
  Home-page: https://github.com/karlglazebrook/pyredshift
6
6
  Author: Karl Glazebrook
@@ -13,7 +13,7 @@ from setuptools import setup
13
13
 
14
14
  setup(
15
15
  name="pyredshift",
16
- version="1.13",
16
+ version="1.15",
17
17
  description="Interactive redshifting of 1D astronomical spectra "
18
18
  "(Python successor of pdlredshift / redshift.f)",
19
19
  long_description=open("README.md").read(),
@@ -1,5 +1,16 @@
1
1
  <h1>pyredshift help <span class="ver">v{version}</span></h1>
2
2
 
3
+ <p class="lineage">Karl's interactive redshift program. Thirty-four years
4
+ of redshifts by eye, now in its third language: <code>redshift.f</code>
5
+ (FIGARO/Fortran, Durham 1992) &rarr; <code>pdlredshift</code>
6
+ (Perl/PDL/PGPLOT 2018) &rarr; <code>pyredshift</code> (Python/matplotlib,
7
+ ported with Claude, 2026) &mdash; same keys, same beautiful crosshair,
8
+ faster carriage.</p>
9
+
10
+ <p class="lineage">Karl Glazebrook, Swinburne University of Technology.
11
+ MIT license.
12
+ <a href="https://github.com/karlglazebrook/pyredshift">github.com/karlglazebrook/pyredshift</a></p>
13
+
3
14
  <input type="radio" name="tabs" id="tab-keys" checked>
4
15
  <input type="radio" name="tabs" id="tab-lines">
5
16
  <input type="radio" name="tabs" id="tab-guide">
@@ -56,6 +56,18 @@ V1.12 - The version number is shown by -h/-help and on the help page.
56
56
  V1.13 - CSV reader: blank flux cells are bad pixels (NaN, plotted as
57
57
  gaps) instead of desynchronising the wavelength and flux arrays;
58
58
  rows with no wavelength are skipped.
59
+ V1.14 - Saving works properly: the toolbar save button no longer
60
+ truncates the output (the crosshair/readout blit machinery ran
61
+ during savefig and corrupted the file render), and the crosshair
62
+ and readout are no longer printed into saved PNGs/PDFs (savefig
63
+ draws animated artists - they now stay invisible outside blit
64
+ passes; the crosshair had been in 'p' output since V1.0).
65
+ - The backend chooser actually imports each backend before
66
+ choosing it, so a python without Qt falls back to MacOSX
67
+ instead of crashing at the first figure.
68
+ - Lineage/credits at the top of the help page.
69
+ V1.15 - Help-page credits link to the GitHub repo; lineage text in a
70
+ readable muted grey in both themes.
59
71
  """
60
72
 
61
73
  import ctypes
@@ -69,10 +81,16 @@ from html.parser import HTMLParser
69
81
  import numpy as np
70
82
  import matplotlib
71
83
 
72
- # Pick a GUI backend unless the user forced one via MPLBACKEND
84
+ # Pick a GUI backend unless the user forced one via MPLBACKEND.
85
+ # matplotlib.use() validates lazily (a missing Qt only explodes later at
86
+ # figure creation), so import the backend module to prove it really works.
73
87
  if "MPLBACKEND" not in os.environ:
74
- for _backend in ("QtAgg", "MacOSX", "TkAgg"):
88
+ import importlib
89
+ for _backend, _mod in (("QtAgg", "backend_qtagg"),
90
+ ("MacOSX", "backend_macosx"),
91
+ ("TkAgg", "backend_tkagg")):
75
92
  try:
93
+ importlib.import_module("matplotlib.backends." + _mod)
76
94
  matplotlib.use(_backend)
77
95
  break
78
96
  except ImportError:
@@ -95,7 +113,7 @@ try:
95
113
  except AttributeError:
96
114
  pass
97
115
 
98
- __version__ = "1.13"
116
+ __version__ = "1.15"
99
117
 
100
118
  C_LIGHT = 2.99792458e8 # m/s
101
119
 
@@ -384,27 +402,38 @@ class StickyCursor(Cursor):
384
402
  self.linev.set_data((xdata, xdata),
385
403
  ((figbb.y0 - axbb.y0) / axbb.height,
386
404
  (figbb.y1 - axbb.y0) / axbb.height))
387
- self.linev.set_visible(self.visible and self.vertOn)
388
405
  self.lineh.set_data(((figbb.x0 - axbb.x0) / axbb.width,
389
406
  (figbb.x1 - axbb.x0) / axbb.width),
390
407
  (ydata, ydata))
391
- self.lineh.set_visible(self.visible and self.horizOn)
392
408
  if not (self.visible and (self.vertOn or self.horizOn)):
393
409
  return
394
- # Redraw
410
+ # Redraw. Animated artists still land in savefig output (bitten:
411
+ # crosshair printed into PDFs/PNGs), so they stay invisible except
412
+ # inside this blit pass - a save sees them switched off.
395
413
  if self.useblit:
396
414
  if self.background is not None:
397
415
  self.canvas.restore_region(self.background)
416
+ self.linev.set_visible(self.vertOn)
417
+ self.lineh.set_visible(self.horizOn)
398
418
  self.ax.draw_artist(self.linev)
399
419
  self.ax.draw_artist(self.lineh)
420
+ self.linev.set_visible(False)
421
+ self.lineh.set_visible(False)
400
422
  if readout_artist is not None:
401
423
  set_readout_text(event) # restore wiped the corner -
424
+ readout_artist.set_visible(True)
402
425
  fig.draw_artist(readout_artist) # redraw with fresh values
426
+ readout_artist.set_visible(False)
403
427
  self.canvas.blit(figbb)
404
428
  else:
429
+ self.linev.set_visible(self.visible and self.vertOn)
430
+ self.lineh.set_visible(self.visible and self.horizOn)
405
431
  self.canvas.draw_idle()
406
432
 
407
433
  def _redraw(self, event):
434
+ if self.canvas.is_saving():
435
+ return # savefig also fires draw_event: never blit the screen
436
+ # background into a file render (bitten: truncated PNGs)
408
437
  if self.active and self._last_event is not None:
409
438
  self.onmove(self._last_event)
410
439
  elif readout_lastev is not None:
@@ -652,8 +681,11 @@ readout_lastev = None # last motion event, to survive full redraws
652
681
 
653
682
  def make_readout():
654
683
  global readout_artist, readout_bg, readout_lastev
684
+ # visible=False except inside blit passes: savefig draws animated
685
+ # artists (mpl 3.9), so this keeps the readout out of saved output
655
686
  readout_artist = fig.text(0.995, 0.012, "", ha="right", va="bottom",
656
687
  family="monospace", fontsize=8, animated=True,
688
+ visible=False,
657
689
  color="white" if dark_mode else "black")
658
690
  readout_bg = None
659
691
  readout_lastev = None
@@ -668,6 +700,8 @@ def snapshot_readout(ev):
668
700
  the cursor takes ITS (full-figure) snapshot; the cursor's _redraw
669
701
  re-renders the readout after every repaint instead."""
670
702
  global readout_bg
703
+ if fig.canvas.is_saving():
704
+ return # don't snapshot a file render as the screen background
671
705
  W, H = fig.bbox.width, fig.bbox.height
672
706
  box = Bbox([[0.40 * W, 0.0], [W, 0.055 * H]])
673
707
  readout_bg = (fig.canvas.copy_from_bbox(box), box)
@@ -696,10 +730,14 @@ def set_readout_text(ev):
696
730
  def update_readout(ev):
697
731
  if readout_artist is None or readout_bg is None or f is None:
698
732
  return
733
+ if fig.canvas.is_saving():
734
+ return # no blitting into a file render
699
735
  set_readout_text(ev)
700
736
  bg, box = readout_bg
701
737
  fig.canvas.restore_region(bg)
738
+ readout_artist.set_visible(True)
702
739
  fig.draw_artist(readout_artist)
740
+ readout_artist.set_visible(False)
703
741
  fig.canvas.blit(box)
704
742
 
705
743
 
@@ -979,6 +1017,8 @@ body { font-family: -apple-system, "Helvetica Neue", sans-serif;
979
1017
  color: %(fg)s; background: %(bg)s; line-height: 1.45; }
980
1018
  h1 { font-size: 1.5em; border-bottom: 2px solid %(rule)s; padding-bottom: 0.2em; }
981
1019
  h1 .ver { font-size: 0.6em; font-weight: normal; color: %(accent)s; }
1020
+ p.lineage { font-size: 0.85em; color: %(muted)s; margin-top: -0.3em; }
1021
+ a { color: %(accent)s; }
982
1022
  h2 { font-size: 1.15em; color: %(accent)s; margin-top: 1.4em; }
983
1023
  table { border-collapse: collapse; margin: 0.5em 0; }
984
1024
  th, td { border: 1px solid %(rule)s; padding: 0.25em 0.7em; text-align: left; }
@@ -1014,8 +1054,10 @@ def show_help_browser(body):
1014
1054
  return False
1015
1055
  import webbrowser
1016
1056
  colours = ({"fg": "#ddd", "bg": "#111", "rule": "#555",
1057
+ "muted": "#aaa",
1017
1058
  "accent": "#f66", "thbg": "#222"} if dark_mode else
1018
1059
  {"fg": "#111", "bg": "#fff", "rule": "#bbb",
1060
+ "muted": "#555",
1019
1061
  "accent": "crimson", "thbg": "#f0f0f0"})
1020
1062
  html = ("<!DOCTYPE html><html><head><meta charset='utf-8'>"
1021
1063
  "<title>pyredshift help</title><style>%s</style></head>"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyredshift
3
- Version: 1.13
3
+ Version: 1.15
4
4
  Summary: Interactive redshifting of 1D astronomical spectra (Python successor of pdlredshift / redshift.f)
5
5
  Home-page: https://github.com/karlglazebrook/pyredshift
6
6
  Author: Karl Glazebrook
File without changes
File without changes
File without changes
File without changes
File without changes