pyredshift 1.12__tar.gz → 1.14__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.12
3
+ Version: 1.14
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
@@ -75,10 +75,15 @@ def read_csv(fname):
75
75
  continue
76
76
  parts = line.split(",")
77
77
  try:
78
- wav.append(float(parts[0]))
79
- spec.append(float(parts[1]))
78
+ x = float(parts[0])
80
79
  except (IndexError, ValueError):
81
- continue
80
+ continue # no wavelength - not a data row
81
+ try:
82
+ y = float(parts[1])
83
+ except (IndexError, ValueError):
84
+ y = np.nan # blank/bad flux cell -> bad pixel, plots as a gap
85
+ wav.append(x)
86
+ spec.append(y)
82
87
  return np.array(wav), np.array(spec), True
83
88
 
84
89
 
@@ -13,7 +13,7 @@ from setuptools import setup
13
13
 
14
14
  setup(
15
15
  name="pyredshift",
16
- version="1.12",
16
+ version="1.14",
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,15 @@
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.</p>
12
+
3
13
  <input type="radio" name="tabs" id="tab-keys" checked>
4
14
  <input type="radio" name="tabs" id="tab-lines">
5
15
  <input type="radio" name="tabs" id="tab-guide">
@@ -53,6 +53,19 @@ V1.11 - Proper file-format documentation: a per-format table in the
53
53
  script's -h and a File formats section in the help page, both
54
54
  generated from FORMAT_HELP here so they cannot drift apart.
55
55
  V1.12 - The version number is shown by -h/-help and on the help page.
56
+ V1.13 - CSV reader: blank flux cells are bad pixels (NaN, plotted as
57
+ gaps) instead of desynchronising the wavelength and flux arrays;
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.
56
69
  """
57
70
 
58
71
  import ctypes
@@ -66,10 +79,16 @@ from html.parser import HTMLParser
66
79
  import numpy as np
67
80
  import matplotlib
68
81
 
69
- # Pick a GUI backend unless the user forced one via MPLBACKEND
82
+ # Pick a GUI backend unless the user forced one via MPLBACKEND.
83
+ # matplotlib.use() validates lazily (a missing Qt only explodes later at
84
+ # figure creation), so import the backend module to prove it really works.
70
85
  if "MPLBACKEND" not in os.environ:
71
- for _backend in ("QtAgg", "MacOSX", "TkAgg"):
86
+ import importlib
87
+ for _backend, _mod in (("QtAgg", "backend_qtagg"),
88
+ ("MacOSX", "backend_macosx"),
89
+ ("TkAgg", "backend_tkagg")):
72
90
  try:
91
+ importlib.import_module("matplotlib.backends." + _mod)
73
92
  matplotlib.use(_backend)
74
93
  break
75
94
  except ImportError:
@@ -92,7 +111,7 @@ try:
92
111
  except AttributeError:
93
112
  pass
94
113
 
95
- __version__ = "1.12"
114
+ __version__ = "1.14"
96
115
 
97
116
  C_LIGHT = 2.99792458e8 # m/s
98
117
 
@@ -381,27 +400,38 @@ class StickyCursor(Cursor):
381
400
  self.linev.set_data((xdata, xdata),
382
401
  ((figbb.y0 - axbb.y0) / axbb.height,
383
402
  (figbb.y1 - axbb.y0) / axbb.height))
384
- self.linev.set_visible(self.visible and self.vertOn)
385
403
  self.lineh.set_data(((figbb.x0 - axbb.x0) / axbb.width,
386
404
  (figbb.x1 - axbb.x0) / axbb.width),
387
405
  (ydata, ydata))
388
- self.lineh.set_visible(self.visible and self.horizOn)
389
406
  if not (self.visible and (self.vertOn or self.horizOn)):
390
407
  return
391
- # Redraw
408
+ # Redraw. Animated artists still land in savefig output (bitten:
409
+ # crosshair printed into PDFs/PNGs), so they stay invisible except
410
+ # inside this blit pass - a save sees them switched off.
392
411
  if self.useblit:
393
412
  if self.background is not None:
394
413
  self.canvas.restore_region(self.background)
414
+ self.linev.set_visible(self.vertOn)
415
+ self.lineh.set_visible(self.horizOn)
395
416
  self.ax.draw_artist(self.linev)
396
417
  self.ax.draw_artist(self.lineh)
418
+ self.linev.set_visible(False)
419
+ self.lineh.set_visible(False)
397
420
  if readout_artist is not None:
398
421
  set_readout_text(event) # restore wiped the corner -
422
+ readout_artist.set_visible(True)
399
423
  fig.draw_artist(readout_artist) # redraw with fresh values
424
+ readout_artist.set_visible(False)
400
425
  self.canvas.blit(figbb)
401
426
  else:
427
+ self.linev.set_visible(self.visible and self.vertOn)
428
+ self.lineh.set_visible(self.visible and self.horizOn)
402
429
  self.canvas.draw_idle()
403
430
 
404
431
  def _redraw(self, event):
432
+ if self.canvas.is_saving():
433
+ return # savefig also fires draw_event: never blit the screen
434
+ # background into a file render (bitten: truncated PNGs)
405
435
  if self.active and self._last_event is not None:
406
436
  self.onmove(self._last_event)
407
437
  elif readout_lastev is not None:
@@ -649,8 +679,11 @@ readout_lastev = None # last motion event, to survive full redraws
649
679
 
650
680
  def make_readout():
651
681
  global readout_artist, readout_bg, readout_lastev
682
+ # visible=False except inside blit passes: savefig draws animated
683
+ # artists (mpl 3.9), so this keeps the readout out of saved output
652
684
  readout_artist = fig.text(0.995, 0.012, "", ha="right", va="bottom",
653
685
  family="monospace", fontsize=8, animated=True,
686
+ visible=False,
654
687
  color="white" if dark_mode else "black")
655
688
  readout_bg = None
656
689
  readout_lastev = None
@@ -665,6 +698,8 @@ def snapshot_readout(ev):
665
698
  the cursor takes ITS (full-figure) snapshot; the cursor's _redraw
666
699
  re-renders the readout after every repaint instead."""
667
700
  global readout_bg
701
+ if fig.canvas.is_saving():
702
+ return # don't snapshot a file render as the screen background
668
703
  W, H = fig.bbox.width, fig.bbox.height
669
704
  box = Bbox([[0.40 * W, 0.0], [W, 0.055 * H]])
670
705
  readout_bg = (fig.canvas.copy_from_bbox(box), box)
@@ -693,10 +728,14 @@ def set_readout_text(ev):
693
728
  def update_readout(ev):
694
729
  if readout_artist is None or readout_bg is None or f is None:
695
730
  return
731
+ if fig.canvas.is_saving():
732
+ return # no blitting into a file render
696
733
  set_readout_text(ev)
697
734
  bg, box = readout_bg
698
735
  fig.canvas.restore_region(bg)
736
+ readout_artist.set_visible(True)
699
737
  fig.draw_artist(readout_artist)
738
+ readout_artist.set_visible(False)
700
739
  fig.canvas.blit(box)
701
740
 
702
741
 
@@ -976,6 +1015,7 @@ body { font-family: -apple-system, "Helvetica Neue", sans-serif;
976
1015
  color: %(fg)s; background: %(bg)s; line-height: 1.45; }
977
1016
  h1 { font-size: 1.5em; border-bottom: 2px solid %(rule)s; padding-bottom: 0.2em; }
978
1017
  h1 .ver { font-size: 0.6em; font-weight: normal; color: %(accent)s; }
1018
+ p.lineage { font-size: 0.85em; color: %(rule)s; margin-top: -0.3em; }
979
1019
  h2 { font-size: 1.15em; color: %(accent)s; margin-top: 1.4em; }
980
1020
  table { border-collapse: collapse; margin: 0.5em 0; }
981
1021
  th, td { border: 1px solid %(rule)s; padding: 0.25em 0.7em; text-align: left; }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyredshift
3
- Version: 1.12
3
+ Version: 1.14
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