pyredshift 1.9__tar.gz → 1.11__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.
- {pyredshift-1.9 → pyredshift-1.11}/PKG-INFO +4 -2
- {pyredshift-1.9 → pyredshift-1.11}/README.md +3 -1
- {pyredshift-1.9 → pyredshift-1.11}/pyredshift +7 -2
- {pyredshift-1.9 → pyredshift-1.11}/setup.py +1 -1
- {pyredshift-1.9 → pyredshift-1.11}/src/pyredshift/pyredshift-help.html +12 -4
- {pyredshift-1.9 → pyredshift-1.11}/src/pyredshift/redshift.py +153 -32
- {pyredshift-1.9 → pyredshift-1.11}/src/pyredshift.egg-info/PKG-INFO +4 -2
- {pyredshift-1.9 → pyredshift-1.11}/LICENSE +0 -0
- {pyredshift-1.9 → pyredshift-1.11}/pyproject.toml +0 -0
- {pyredshift-1.9 → pyredshift-1.11}/setup.cfg +0 -0
- {pyredshift-1.9 → pyredshift-1.11}/src/pyredshift/__init__.py +0 -0
- {pyredshift-1.9 → pyredshift-1.11}/src/pyredshift/pyredshift.lines +0 -0
- {pyredshift-1.9 → pyredshift-1.11}/src/pyredshift.egg-info/SOURCES.txt +0 -0
- {pyredshift-1.9 → pyredshift-1.11}/src/pyredshift.egg-info/dependency_links.txt +0 -0
- {pyredshift-1.9 → pyredshift-1.11}/src/pyredshift.egg-info/requires.txt +0 -0
- {pyredshift-1.9 → pyredshift-1.11}/src/pyredshift.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyredshift
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.11
|
|
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
|
|
@@ -134,7 +134,9 @@ short guide in separate tabs. The essentials:
|
|
|
134
134
|
- **Navigate**: drag the mouse for a rubber-band zoom (a purely horizontal
|
|
135
135
|
drag zooms X only), `z`/`u` and `i`/`o` zoom about the cursor, `[` `]`
|
|
136
136
|
pan, `w` whole range, `a` autoscale, `h` home. The matplotlib toolbar
|
|
137
|
-
works too and stays in sync.
|
|
137
|
+
works too and stays in sync. As in PGPLOT the cursor works over the
|
|
138
|
+
whole window — press `x`/`y`/`e` just outside the axis to select a
|
|
139
|
+
range flush with the plot edge.
|
|
138
140
|
- **Analyse**: `b` bin, `s` smooth, `_` iterative continuum fit,
|
|
139
141
|
`m` equivalent width and line flux, `B` zap artefacts, `p` print to PDF.
|
|
140
142
|
- **Quit**: `q` — the final redshift is reported on the terminal.
|
|
@@ -100,7 +100,9 @@ short guide in separate tabs. The essentials:
|
|
|
100
100
|
- **Navigate**: drag the mouse for a rubber-band zoom (a purely horizontal
|
|
101
101
|
drag zooms X only), `z`/`u` and `i`/`o` zoom about the cursor, `[` `]`
|
|
102
102
|
pan, `w` whole range, `a` autoscale, `h` home. The matplotlib toolbar
|
|
103
|
-
works too and stays in sync.
|
|
103
|
+
works too and stays in sync. As in PGPLOT the cursor works over the
|
|
104
|
+
whole window — press `x`/`y`/`e` just outside the axis to select a
|
|
105
|
+
range flush with the plot edge.
|
|
104
106
|
- **Analyse**: `b` bin, `s` smooth, `_` iterative continuum fit,
|
|
105
107
|
`m` equivalent width and line flux, `B` zap artefacts, `p` print to PDF.
|
|
106
108
|
- **Quit**: `q` — the final redshift is reported on the terminal.
|
|
@@ -264,9 +264,14 @@ def read_spectrum(fname, fmt=None):
|
|
|
264
264
|
def main():
|
|
265
265
|
ap = argparse.ArgumentParser(
|
|
266
266
|
prog="pyredshift",
|
|
267
|
+
usage="pyredshift [-help] [-f FORMAT] [-z REDSHIFT] [--retro] "
|
|
268
|
+
"[--microns] file",
|
|
267
269
|
description="Interactive redshifting of 1D spectra (port of pdlredshift).",
|
|
268
|
-
|
|
269
|
-
|
|
270
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
271
|
+
# format documentation lives in the module (FORMAT_HELP) so the
|
|
272
|
+
# -h epilog and the '?' help page cannot drift apart
|
|
273
|
+
epilog=redshift_module.formats_text())
|
|
274
|
+
ap.add_argument("-help", action="help", help=argparse.SUPPRESS)
|
|
270
275
|
ap.add_argument("file", help="spectrum file (FITS, CSV or 2-column ascii)")
|
|
271
276
|
ap.add_argument("-f", "--format", default=None, metavar="FORMAT",
|
|
272
277
|
help="force the input format instead of auto-detecting")
|
|
@@ -13,7 +13,7 @@ from setuptools import setup
|
|
|
13
13
|
|
|
14
14
|
setup(
|
|
15
15
|
name="pyredshift",
|
|
16
|
-
version="1.
|
|
16
|
+
version="1.11",
|
|
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(),
|
|
@@ -57,6 +57,11 @@ redshift at that position (same as ESC + shortcut key)</td></tr>
|
|
|
57
57
|
<tr><td>d</td><td>display again</td></tr>
|
|
58
58
|
</table>
|
|
59
59
|
|
|
60
|
+
<p>The cursor works over the whole window, as in PGPLOT: keys and clicks
|
|
61
|
+
outside the axes box extrapolate their coordinates, so <code>x</code>,
|
|
62
|
+
<code>y</code> and <code>e</code> can be pressed just outside the axis to
|
|
63
|
+
select a range flush with (or beyond) the plot edge.</p>
|
|
64
|
+
|
|
60
65
|
<h2>Analysis</h2>
|
|
61
66
|
<table>
|
|
62
67
|
<tr><th>Key</th><th>Action</th></tr>
|
|
@@ -93,13 +98,16 @@ colours as plotted):</p>
|
|
|
93
98
|
<h2>Guide</h2>
|
|
94
99
|
<p>pyredshift is a tool for measuring the redshift of a galaxy or quasar
|
|
95
100
|
from a 1D spectrum, by eye. Run it as <code>pyredshift file</code> —
|
|
96
|
-
the format (
|
|
97
|
-
|
|
98
|
-
<code
|
|
99
|
-
|
|
101
|
+
the file format is auto-detected (see the table below), with
|
|
102
|
+
<code>-f</code> to force one, <code>-z</code> to start from a known
|
|
103
|
+
redshift and <code>--retro</code> for a retro PGPLOT-style black
|
|
104
|
+
background. It plots the spectrum histogram-style with a
|
|
100
105
|
crosshair cursor, and overlays rest-frame line identifications that
|
|
101
106
|
follow the trial redshift shown at top left.</p>
|
|
102
107
|
|
|
108
|
+
<h2>File formats</h2>
|
|
109
|
+
{formats}
|
|
110
|
+
|
|
103
111
|
<p>The basic game: spot a feature you can identify, put the crosshair on
|
|
104
112
|
it and press <code>ESC</code> then the line's shortcut key (or
|
|
105
113
|
<code>g</code> to type any rest wavelength) — the line labels jump
|
|
@@ -44,6 +44,14 @@ V1.8 - Right-click pops up the quick line list as a menu: pick a line
|
|
|
44
44
|
lines are now goldenrod on white, yellow on the retro background.
|
|
45
45
|
V1.9 - First PyPI release. --microns flag (keeps micron wavelengths so
|
|
46
46
|
micron-mode display is reachable from files); packaging metadata.
|
|
47
|
+
V1.10 - The cursor works over the whole window, as PGPLOT's did: keys and
|
|
48
|
+
clicks outside the axes box extrapolate their coordinates (so
|
|
49
|
+
'x'/'y'/'e' can be pressed just outside the axis, pdlredshift
|
|
50
|
+
style), the crosshair and the readout follow into the margins,
|
|
51
|
+
and rubber-band drags may extend past the plot edge.
|
|
52
|
+
V1.11 - Proper file-format documentation: a per-format table in the
|
|
53
|
+
script's -h and a File formats section in the help page, both
|
|
54
|
+
generated from FORMAT_HELP here so they cannot drift apart.
|
|
47
55
|
"""
|
|
48
56
|
|
|
49
57
|
import ctypes
|
|
@@ -83,12 +91,33 @@ try:
|
|
|
83
91
|
except AttributeError:
|
|
84
92
|
pass
|
|
85
93
|
|
|
86
|
-
__version__ = "1.
|
|
94
|
+
__version__ = "1.11"
|
|
87
95
|
|
|
88
96
|
C_LIGHT = 2.99792458e8 # m/s
|
|
89
97
|
|
|
90
98
|
TEMPLATE_NAME = "GNIRS_N4608" # template for the 't' key
|
|
91
99
|
|
|
100
|
+
# One description per reader in the pyredshift script - the single source
|
|
101
|
+
# for both the script's -h epilog and the Formats section of the help page.
|
|
102
|
+
# Keep in sync when adding a reader to the script.
|
|
103
|
+
FORMAT_HELP = [
|
|
104
|
+
("fits", "1D (or 2D - row 1 used) FITS image with WCS; CD1_1 "
|
|
105
|
+
"overrides CDELT1, IRAF/SDSS log-lambda flags handled"),
|
|
106
|
+
("sdss", "SDSS/BOSS binary table (LOGLAM/FLUX, either case)"),
|
|
107
|
+
("table", "generic binary table with wave+flux columns; TUNIT "
|
|
108
|
+
"(um, nm, A) converted automatically "
|
|
109
|
+
"(aliases: jwst, jwst2, gabe, dja)"),
|
|
110
|
+
("xs", "XSHOOTER 1D table spectrum (nm)"),
|
|
111
|
+
("xs2", "XSHOOTER as a FITS image in extension 1"),
|
|
112
|
+
("outthere", "OutThere multi-extension grism spectra, stitched "
|
|
113
|
+
"and flat-calibrated"),
|
|
114
|
+
("csv", "comma-separated, header rows skipped"),
|
|
115
|
+
("ascii", "2-column text"),
|
|
116
|
+
]
|
|
117
|
+
FORMAT_NOTES = ("Default is auto-detection. Wavelengths that look like "
|
|
118
|
+
"microns are converted to Angstroms (--microns keeps them); "
|
|
119
|
+
"NaNs are treated as bad pixels and plot as gaps.")
|
|
120
|
+
|
|
92
121
|
CONFIG_FILE = os.path.expanduser("~/.pyredshift.json") # remembers window size
|
|
93
122
|
DEFAULT_FIGSIZE = (13.0, 5.5) # inches
|
|
94
123
|
|
|
@@ -210,11 +239,35 @@ def linelist_html():
|
|
|
210
239
|
return "<table class='linelist'>\n%s\n</table>" % "\n".join(rows)
|
|
211
240
|
|
|
212
241
|
|
|
242
|
+
def formats_html():
|
|
243
|
+
"""FORMAT_HELP as table rows + notes for the help page."""
|
|
244
|
+
rows = ["<tr><th>Format</th><th>Description</th></tr>"]
|
|
245
|
+
for name, desc in FORMAT_HELP:
|
|
246
|
+
rows.append("<tr><td><code>%s</code></td><td>%s</td></tr>"
|
|
247
|
+
% (name, desc))
|
|
248
|
+
return ("<table>\n%s\n</table>\n<p>%s</p>"
|
|
249
|
+
% ("\n".join(rows), FORMAT_NOTES))
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def formats_text():
|
|
253
|
+
"""FORMAT_HELP as aligned plain text for the script's -h epilog."""
|
|
254
|
+
import textwrap
|
|
255
|
+
width = max(len(name) for name, _ in FORMAT_HELP)
|
|
256
|
+
lines = ["Formats (-f):"]
|
|
257
|
+
for name, desc in FORMAT_HELP:
|
|
258
|
+
lines.append(textwrap.fill(
|
|
259
|
+
desc, width=79, initial_indent=" %-*s " % (width, name),
|
|
260
|
+
subsequent_indent=" " * (width + 4)))
|
|
261
|
+
lines.append(textwrap.fill(FORMAT_NOTES, width=79))
|
|
262
|
+
return "\n".join(lines)
|
|
263
|
+
|
|
264
|
+
|
|
213
265
|
def help_html():
|
|
214
266
|
"""The help HTML body with the placeholders filled in."""
|
|
215
267
|
if HELP_RAW is None:
|
|
216
268
|
return None
|
|
217
|
-
return HELP_RAW.format(template=TEMPLATE_NAME, linelist=linelist_html()
|
|
269
|
+
return HELP_RAW.format(template=TEMPLATE_NAME, linelist=linelist_html(),
|
|
270
|
+
formats=formats_html())
|
|
218
271
|
|
|
219
272
|
|
|
220
273
|
# ---------------------------------------------------------------------------
|
|
@@ -260,6 +313,19 @@ norm = 1.0
|
|
|
260
313
|
# ---------------------------------------------------------------------------
|
|
261
314
|
# Cursor and blocking input - the pgband() replacement
|
|
262
315
|
# ---------------------------------------------------------------------------
|
|
316
|
+
def surface_xy(ax_, ev):
|
|
317
|
+
"""Data coords for an event anywhere on the figure surface.
|
|
318
|
+
|
|
319
|
+
PGPLOT's cursor was not confined to the axes box - outside it (margins,
|
|
320
|
+
over widgets) extrapolate from the pixel position."""
|
|
321
|
+
if ev.inaxes is ax_:
|
|
322
|
+
return ev.xdata, ev.ydata
|
|
323
|
+
if ev.x is None or ev.y is None:
|
|
324
|
+
return None, None
|
|
325
|
+
xd, yd = ax_.transData.inverted().transform((ev.x, ev.y))
|
|
326
|
+
return float(xd), float(yd)
|
|
327
|
+
|
|
328
|
+
|
|
263
329
|
class StickyCursor(Cursor):
|
|
264
330
|
"""Cursor that survives full canvas repaints and toolbar pan/zoom.
|
|
265
331
|
|
|
@@ -268,19 +334,37 @@ class StickyCursor(Cursor):
|
|
|
268
334
|
after every repaint. Also reimplements onmove() without the base class's
|
|
269
335
|
widgetlock check, so the crosshair stays live while the toolbar pan/zoom
|
|
270
336
|
mode is switched on.
|
|
337
|
+
|
|
338
|
+
The crosshair spans the whole window like PGPLOT's, not just the axes
|
|
339
|
+
box, so the background snapshot and blit cover the full figure - and the
|
|
340
|
+
cursor re-draws the (animated) readout text as part of its pass, since a
|
|
341
|
+
full-figure restore would otherwise erase it.
|
|
271
342
|
"""
|
|
272
343
|
|
|
273
344
|
def __init__(self, ax_, **kwargs):
|
|
274
345
|
self._last_event = None
|
|
275
346
|
super().__init__(ax_, **kwargs)
|
|
347
|
+
self.linev.set_clip_on(False) # let the crosshair escape the box
|
|
348
|
+
self.lineh.set_clip_on(False)
|
|
276
349
|
self.connect_event("draw_event", self._redraw)
|
|
277
350
|
|
|
351
|
+
def clear(self, event):
|
|
352
|
+
# As Cursor.clear (mpl 3.9), but snapshot the whole figure: the
|
|
353
|
+
# crosshair now extends into the margins. Runs on draw_event while
|
|
354
|
+
# animated artists are excluded, so the snapshot is clean.
|
|
355
|
+
if self.ignore(event) or self.canvas.is_saving():
|
|
356
|
+
return
|
|
357
|
+
if self.useblit:
|
|
358
|
+
self.background = self.canvas.copy_from_bbox(self.canvas.figure.bbox)
|
|
359
|
+
|
|
278
360
|
def onmove(self, event):
|
|
279
361
|
# Copy of Cursor.onmove from matplotlib 3.9, minus the widgetlock
|
|
280
|
-
# check (version sensitive - revisit if matplotlib is upgraded)
|
|
362
|
+
# check (version sensitive - revisit if matplotlib is upgraded),
|
|
363
|
+
# with whole-window coords and full-figure blitting.
|
|
281
364
|
if self.ignore(event):
|
|
282
365
|
return
|
|
283
|
-
|
|
366
|
+
xdata, ydata = surface_xy(self.ax, event)
|
|
367
|
+
if xdata is None:
|
|
284
368
|
self._last_event = None # or _redraw() would resurrect it
|
|
285
369
|
self.linev.set_visible(False)
|
|
286
370
|
self.lineh.set_visible(False)
|
|
@@ -290,10 +374,16 @@ class StickyCursor(Cursor):
|
|
|
290
374
|
return
|
|
291
375
|
self._last_event = event
|
|
292
376
|
self.needclear = True
|
|
293
|
-
|
|
294
|
-
self.
|
|
377
|
+
# Span the full window: line extents are in axes-fraction coords
|
|
378
|
+
figbb = self.canvas.figure.bbox
|
|
379
|
+
axbb = self.ax.bbox
|
|
380
|
+
self.linev.set_data((xdata, xdata),
|
|
381
|
+
((figbb.y0 - axbb.y0) / axbb.height,
|
|
382
|
+
(figbb.y1 - axbb.y0) / axbb.height))
|
|
295
383
|
self.linev.set_visible(self.visible and self.vertOn)
|
|
296
|
-
self.lineh.
|
|
384
|
+
self.lineh.set_data(((figbb.x0 - axbb.x0) / axbb.width,
|
|
385
|
+
(figbb.x1 - axbb.x0) / axbb.width),
|
|
386
|
+
(ydata, ydata))
|
|
297
387
|
self.lineh.set_visible(self.visible and self.horizOn)
|
|
298
388
|
if not (self.visible and (self.vertOn or self.horizOn)):
|
|
299
389
|
return
|
|
@@ -303,13 +393,21 @@ class StickyCursor(Cursor):
|
|
|
303
393
|
self.canvas.restore_region(self.background)
|
|
304
394
|
self.ax.draw_artist(self.linev)
|
|
305
395
|
self.ax.draw_artist(self.lineh)
|
|
306
|
-
|
|
396
|
+
if readout_artist is not None:
|
|
397
|
+
set_readout_text(event) # restore wiped the corner -
|
|
398
|
+
fig.draw_artist(readout_artist) # redraw with fresh values
|
|
399
|
+
self.canvas.blit(figbb)
|
|
307
400
|
else:
|
|
308
401
|
self.canvas.draw_idle()
|
|
309
402
|
|
|
310
403
|
def _redraw(self, event):
|
|
311
|
-
if self._last_event is not None:
|
|
404
|
+
if self.active and self._last_event is not None:
|
|
312
405
|
self.onmove(self._last_event)
|
|
406
|
+
elif readout_lastev is not None:
|
|
407
|
+
# No crosshair to re-draw (fresh cursor after draw_plot, or
|
|
408
|
+
# deactivated while the line menu is up) - the readout must
|
|
409
|
+
# still survive the repaint
|
|
410
|
+
update_readout(readout_lastev)
|
|
313
411
|
|
|
314
412
|
|
|
315
413
|
def normkey(ch):
|
|
@@ -325,6 +423,11 @@ def pgband(allow_drag=False):
|
|
|
325
423
|
Mouse button gives ch='A', as PGPLOT did. Returns ch='q' if the window
|
|
326
424
|
is closed.
|
|
327
425
|
|
|
426
|
+
Like the PGPLOT cursor, key presses and left clicks work over the whole
|
|
427
|
+
window, not just inside the axes box - coordinates are extrapolated from
|
|
428
|
+
the pixel position (handy for 'x'/'y'/'e' just outside the axis).
|
|
429
|
+
Clicks on widgets (the ? button) are still ignored.
|
|
430
|
+
|
|
328
431
|
With allow_drag=True a left-button drag rubber-band zooms (like 'e') and
|
|
329
432
|
returns ch='drag' after updating the view state; a purely horizontal drag
|
|
330
433
|
zooms X only, a purely vertical one Y only. Short drags (<5 pixels)
|
|
@@ -343,31 +446,40 @@ def pgband(allow_drag=False):
|
|
|
343
446
|
toolbar = getattr(fig.canvas.manager, "toolbar", None)
|
|
344
447
|
return getattr(toolbar, "mode", "") if toolbar is not None else ""
|
|
345
448
|
|
|
449
|
+
def data_xy(ev):
|
|
450
|
+
# PGPLOT-style whole-surface cursor read (see surface_xy)
|
|
451
|
+
return surface_xy(ax, ev)
|
|
452
|
+
|
|
346
453
|
def on_key(ev):
|
|
347
|
-
|
|
454
|
+
xd, yd = data_xy(ev)
|
|
455
|
+
done(xd, yd, normkey(ev.key))
|
|
348
456
|
|
|
349
457
|
def on_press(ev):
|
|
350
|
-
if ev.inaxes is not ax:
|
|
351
|
-
return # clicks
|
|
352
|
-
if allow_drag and ev.button == 3:
|
|
458
|
+
if ev.inaxes is not ax and ev.inaxes is not None:
|
|
459
|
+
return # clicks on widgets (the ? button) aren't cursor reads
|
|
460
|
+
if allow_drag and ev.button == 3 and ev.inaxes is ax:
|
|
353
461
|
done(ev.xdata, ev.ydata, "menu") # right-click: quick line menu
|
|
354
462
|
return
|
|
355
|
-
|
|
463
|
+
xd, yd = data_xy(ev)
|
|
464
|
+
if allow_drag and ev.button == 1 and ev.inaxes is ax and not toolbar_mode():
|
|
356
465
|
drag["xpx"], drag["ypx"] = ev.x, ev.y # pixels, for threshold
|
|
357
|
-
drag["x0"], drag["y0"] =
|
|
358
|
-
drag["x1"], drag["y1"] =
|
|
466
|
+
drag["x0"], drag["y0"] = xd, yd
|
|
467
|
+
drag["x1"], drag["y1"] = xd, yd
|
|
359
468
|
drag["rect"] = ax.add_patch(Rectangle(
|
|
360
|
-
(
|
|
469
|
+
(xd, yd), 0, 0, fill=False,
|
|
361
470
|
edgecolor="red", lw=0.8, ls="--"))
|
|
362
471
|
else:
|
|
363
|
-
done(
|
|
472
|
+
done(xd, yd, "A")
|
|
364
473
|
|
|
365
474
|
def on_motion(ev):
|
|
366
|
-
if "rect" not in drag or ev.inaxes is not ax:
|
|
475
|
+
if "rect" not in drag or (ev.inaxes is not ax and ev.inaxes is not None):
|
|
367
476
|
return
|
|
368
|
-
|
|
477
|
+
xd, yd = data_xy(ev)
|
|
478
|
+
if xd is None:
|
|
479
|
+
return
|
|
480
|
+
drag["x1"], drag["y1"] = xd, yd
|
|
369
481
|
drag["rect"].set_bounds(drag["x0"], drag["y0"],
|
|
370
|
-
|
|
482
|
+
xd - drag["x0"], yd - drag["y0"])
|
|
371
483
|
fig.canvas.draw_idle()
|
|
372
484
|
|
|
373
485
|
def on_release(ev):
|
|
@@ -547,31 +659,40 @@ def make_readout():
|
|
|
547
659
|
|
|
548
660
|
def snapshot_readout(ev):
|
|
549
661
|
"""Cache the bottom-right corner after every full draw (the readout
|
|
550
|
-
artist is animated, so it is never part of the cached image)
|
|
551
|
-
re-
|
|
552
|
-
|
|
662
|
+
artist is animated, so it is never part of the cached image). Only a
|
|
663
|
+
snapshot: re-rendering here would bake the text into the buffer before
|
|
664
|
+
the cursor takes ITS (full-figure) snapshot; the cursor's _redraw
|
|
665
|
+
re-renders the readout after every repaint instead."""
|
|
553
666
|
global readout_bg
|
|
554
667
|
W, H = fig.bbox.width, fig.bbox.height
|
|
555
668
|
box = Bbox([[0.40 * W, 0.0], [W, 0.055 * H]])
|
|
556
669
|
readout_bg = (fig.canvas.copy_from_bbox(box), box)
|
|
557
|
-
if readout_lastev is not None:
|
|
558
|
-
update_readout(readout_lastev)
|
|
559
670
|
|
|
560
671
|
|
|
561
|
-
def
|
|
672
|
+
def set_readout_text(ev):
|
|
673
|
+
"""Recompute the readout for an event - separate from the blit so the
|
|
674
|
+
cursor can refresh the text mid-pass (a new redshift must update the
|
|
675
|
+
rest wavelength immediately, not on the next mouse move)."""
|
|
562
676
|
global readout_lastev
|
|
563
|
-
if readout_artist is None or
|
|
677
|
+
if readout_artist is None or f is None:
|
|
564
678
|
return
|
|
565
679
|
readout_lastev = ev
|
|
566
|
-
|
|
567
|
-
|
|
680
|
+
xd, yd = surface_xy(ax, ev)
|
|
681
|
+
if xd is not None: # whole window, like the crosshair (pix clamps at ends)
|
|
682
|
+
i = int(np.argmin(np.abs(w - xd)))
|
|
568
683
|
wfmt = "%.5f" if micron_mode else "%.2f"
|
|
569
|
-
rest = wfmt % (
|
|
684
|
+
rest = wfmt % (xd / (1 + zshift)) if found else "-"
|
|
570
685
|
text = ("pix %d y %.4g λ %s rest %s flux %.4g"
|
|
571
|
-
% (i,
|
|
686
|
+
% (i, yd, wfmt % xd, rest, f[i]))
|
|
572
687
|
else:
|
|
573
688
|
text = ""
|
|
574
689
|
readout_artist.set_text(text)
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
def update_readout(ev):
|
|
693
|
+
if readout_artist is None or readout_bg is None or f is None:
|
|
694
|
+
return
|
|
695
|
+
set_readout_text(ev)
|
|
575
696
|
bg, box = readout_bg
|
|
576
697
|
fig.canvas.restore_region(bg)
|
|
577
698
|
fig.draw_artist(readout_artist)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyredshift
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.11
|
|
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
|
|
@@ -134,7 +134,9 @@ short guide in separate tabs. The essentials:
|
|
|
134
134
|
- **Navigate**: drag the mouse for a rubber-band zoom (a purely horizontal
|
|
135
135
|
drag zooms X only), `z`/`u` and `i`/`o` zoom about the cursor, `[` `]`
|
|
136
136
|
pan, `w` whole range, `a` autoscale, `h` home. The matplotlib toolbar
|
|
137
|
-
works too and stays in sync.
|
|
137
|
+
works too and stays in sync. As in PGPLOT the cursor works over the
|
|
138
|
+
whole window — press `x`/`y`/`e` just outside the axis to select a
|
|
139
|
+
range flush with the plot edge.
|
|
138
140
|
- **Analyse**: `b` bin, `s` smooth, `_` iterative continuum fit,
|
|
139
141
|
`m` equivalent width and line flux, `B` zap artefacts, `p` print to PDF.
|
|
140
142
|
- **Quit**: `q` — the final redshift is reported on the terminal.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|