LSST Applications 26.0.0,g0265f82a02+6660c170cc,g07994bdeae+30b05a742e,g0a0026dc87+17526d298f,g0a60f58ba1+17526d298f,g0e4bf8285c+96dd2c2ea9,g0ecae5effc+c266a536c8,g1e7d6db67d+6f7cb1f4bb,g26482f50c6+6346c0633c,g2bbee38e9b+6660c170cc,g2cc88a2952+0a4e78cd49,g3273194fdb+f6908454ef,g337abbeb29+6660c170cc,g337c41fc51+9a8f8f0815,g37c6e7c3d5+7bbafe9d37,g44018dc512+6660c170cc,g4a941329ef+4f7594a38e,g4c90b7bd52+5145c320d2,g58be5f913a+bea990ba40,g635b316a6c+8d6b3a3e56,g67924a670a+bfead8c487,g6ae5381d9b+81bc2a20b4,g93c4d6e787+26b17396bd,g98cecbdb62+ed2cb6d659,g98ffbb4407+81bc2a20b4,g9ddcbc5298+7f7571301f,ga1e77700b3+99e9273977,gae46bcf261+6660c170cc,gb2715bf1a1+17526d298f,gc86a011abf+17526d298f,gcf0d15dbbd+96dd2c2ea9,gdaeeff99f8+0d8dbea60f,gdb4ec4c597+6660c170cc,ge23793e450+96dd2c2ea9,gf041782ebf+171108ac67
LSST Data Management Base Package
Loading...
Searching...
No Matches
interface.py
Go to the documentation of this file.
2# This file is part of afw.
3#
4# Developed for the LSST Data Management System.
5# This product includes software developed by the LSST Project
6# (https://www.lsst.org).
7# See the COPYRIGHT file at the top-level directory of this distribution
8# for details of code ownership.
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program. If not, see <https://www.gnu.org/licenses/>.
22
23__all__ = [
24 "WHITE", "BLACK", "RED", "GREEN", "BLUE", "CYAN", "MAGENTA", "YELLOW", "ORANGE", "IGNORE",
25 "Display", "Event", "noop_callback", "h_callback",
26 "setDefaultBackend", "getDefaultBackend",
27 "setDefaultFrame", "getDefaultFrame", "incrDefaultFrame",
28 "setDefaultMaskTransparency", "setDefaultMaskPlaneColor",
29 "getDisplay", "delAllDisplays",
30]
31
32import re
33import sys
34import importlib
35import lsst.afw.geom as afwGeom
36import lsst.afw.image as afwImage
37import lsst.log
38
39logger = lsst.log.Log.getLogger(__name__)
40
41#
42# Symbolic names for mask/line colors. N.b. ds9 supports any X11 color for masks
43#
44WHITE = "white"
45BLACK = "black"
46RED = "red"
47GREEN = "green"
48BLUE = "blue"
49CYAN = "cyan"
50MAGENTA = "magenta"
51YELLOW = "yellow"
52ORANGE = "orange"
53IGNORE = "ignore"
54
55
56def _makeDisplayImpl(display, backend, *args, **kwargs):
57 """Return the ``DisplayImpl`` for the named backend
58
59 Parameters
60 ----------
61 display : `str`
62 Name of device. Should be importable, either absolutely or relative to lsst.display
63 backend : `str`
64 The desired backend
65 *args
66 Arguments passed to DisplayImpl.__init__
67 *kwargs
68 Keywords arguments passed to DisplayImpl.__init__
69
70 Examples
71 --------
72 E.g.
73
74 .. code-block:: py
75
76 import lsst.afw.display as afwDisplay
77 display = afwDisplay.Display(backend="ds9")
78
79 would call
80
81 .. code-block:: py
82
83 _makeDisplayImpl(..., "ds9", 1)
84
85 and import the ds9 implementation of ``DisplayImpl`` from `lsst.display.ds9`
86 """
87 _disp = None
88 exc = None
89 candidateBackends = (f"lsst.display.{backend}", backend, f".{backend}", f"lsst.afw.display.{backend}")
90 for dt in candidateBackends:
91 exc = None
92 # only specify the root package if we are not doing an absolute import
93 impargs = {}
94 if dt.startswith("."):
95 impargs["package"] = "lsst.display"
96 try:
97 _disp = importlib.import_module(dt, **impargs)
98 # If _disp doesn't have a DisplayImpl attribute, we probably
99 # picked up an irrelevant module due to a name collision
100 if hasattr(_disp, "DisplayImpl"):
101 break
102 else:
103 _disp = None
104 except (ImportError, SystemError) as e:
105 # SystemError can be raised in Python 3.5 if a relative import
106 # is attempted when the root package, lsst.display, does not exist.
107 # Copy the exception into outer scope
108 exc = e
109
110 if not _disp or not hasattr(_disp.DisplayImpl, "_show"):
111 # If available, re-use the final exception from above
112 e = ImportError(f"Could not load the requested backend: {backend} "
113 f"(tried {', '.join(candidateBackends)}, but none worked).")
114 if exc is not None:
115 raise e from exc
116 else:
117 raise e
118
119 if display:
120 _impl = _disp.DisplayImpl(display, *args, **kwargs)
121 if not hasattr(_impl, "frame"):
122 _impl.frame = display.frame
123
124 return _impl
125 else:
126 return True
127
128
130 """Create an object able to display images and overplot glyphs
131
132 Parameters
133 ----------
134 frame
135 An identifier for the display
136 backend : `str`
137 The backend to use (defaults to value set by setDefaultBackend())
138 *args
139 Arguments to pass to the backend
140 **kwargs
141 Arguments to pass to the backend
142 """
143 _displays = {}
144 _defaultBackend = None
145 _defaultFrame = 0
146 _defaultMaskPlaneColor = dict(
147 BAD=RED,
148 CR=MAGENTA,
149 EDGE=YELLOW,
150 INTERPOLATED=GREEN,
151 SATURATED=GREEN,
152 DETECTED=BLUE,
153 DETECTED_NEGATIVE=CYAN,
154 SUSPECT=YELLOW,
155 NO_DATA=ORANGE,
156 # deprecated names
157 INTRP=GREEN,
158 SAT=GREEN,
159 )
160 _defaultMaskTransparency = {}
161 _defaultImageColormap = "gray"
162
163 def __init__(self, frame=None, backend=None, *args, **kwargs):
164 if frame is None:
165 frame = getDefaultFrame()
166
167 if backend is None:
168 if Display._defaultBackend is None:
169 try:
170 setDefaultBackend("ds9")
171 except RuntimeError:
172 setDefaultBackend("virtualDevice")
173
174 backend = Display._defaultBackend
175
176 self.frame = frame
177 self._impl = _makeDisplayImpl(self, backend, *args, **kwargs)
178 self.name = backend
179
180 self._xy0 = None # displayed data's XY0
181 self.setMaskTransparency(Display._defaultMaskTransparency)
183 self.setMaskPlaneColor(Display._defaultMaskPlaneColor)
184 self.setImageColormap(Display._defaultImageColormap)
185
186 self._callbacks = {}
187
188 for ik in range(ord('a'), ord('z') + 1):
189 k = f"{ik:c}"
190 self.setCallback(k, noRaise=True)
191 self.setCallback(k.upper(), noRaise=True)
192
193 for k in ('Return', 'Shift_L', 'Shift_R'):
194 self.setCallback(k)
195
196 for k in ('q', 'Escape'):
197 self.setCallback(k, lambda k, x, y: True)
198
199 def _h_callback(k, x, y):
200 h_callback(k, x, y)
201
202 for k in sorted(self._callbacks.keys()):
203 doc = self._callbacks[k].__doc__
204 print(" %-6s %s" % (k, doc.split("\n")[0] if doc else "???"))
205
206 self.setCallback('h', _h_callback)
207
208 Display._displays[frame] = self
209
210 def __enter__(self):
211 """Support for python's with statement
212 """
213 return self
214
215 def __exit__(self, *args):
216 """Support for python's with statement
217 """
218 self.close()
219
220 def __del__(self):
221 self.close()
222
223 def __getattr__(self, name):
224 """Return the attribute of ``self._impl``, or ``._impl`` if it is requested
225
226 Parameters:
227 -----------
228 name : `str`
229 name of the attribute requested
230
231 Returns:
232 --------
233 attribute : `object`
234 the attribute of self._impl for the requested name
235 """
236
237 if name == '_impl':
238 return object.__getattr__(self, name)
239
240 if not (hasattr(self, "_impl") and self._impl):
241 raise AttributeError("Device has no _impl attached")
242
243 try:
244 return getattr(self._impl, name)
245 except AttributeError:
246 raise AttributeError(
247 f"Device {self.name} has no attribute \"{name}\"")
248
249 def close(self):
250 if getattr(self, "_impl", None) is not None:
251 self._impl._close()
252 del self._impl
253 self._impl = None
254
255 if self.frame in Display._displays:
256 del Display._displays[self.frame]
257
258 @property
259 def verbose(self):
260 """The backend's verbosity
261 """
262 return self._impl.verbose
263
264 @verbose.setter
265 def verbose(self, value):
266 if self._impl:
267 self._impl.verbose = value
268
269 def __str__(self):
270 return f"Display[{self.frame}]"
271
272 #
273 # Handle Displays, including the default one (the frame to use when a user specifies None)
274 #
275 @staticmethod
276 def setDefaultBackend(backend):
277 try:
278 _makeDisplayImpl(None, backend)
279 except Exception as e:
280 raise RuntimeError(
281 f"Unable to set backend to {backend}: \"{e}\"")
282
283 Display._defaultBackend = backend
284
285 @staticmethod
287 return Display._defaultBackend
288
289 @staticmethod
290 def setDefaultFrame(frame=0):
291 """Set the default frame for display
292 """
293 Display._defaultFrame = frame
294
295 @staticmethod
297 """Get the default frame for display
298 """
299 return Display._defaultFrame
300
301 @staticmethod
303 """Increment the default frame for display
304 """
305 Display._defaultFrame += 1
306 return Display._defaultFrame
307
308 @staticmethod
309 def setDefaultMaskTransparency(maskPlaneTransparency={}):
310 if hasattr(maskPlaneTransparency, "copy"):
311 maskPlaneTransparency = maskPlaneTransparency.copy()
312
313 Display._defaultMaskTransparency = maskPlaneTransparency
314
315 @staticmethod
316 def setDefaultMaskPlaneColor(name=None, color=None):
317 """Set the default mapping from mask plane names to colors
318
319 Parameters
320 ----------
321 name : `str` or `dict`
322 name of mask plane, or a dict mapping names to colors
323 If name is `None`, use the hard-coded default dictionary
324 color
325 Desired color, or `None` if name is a dict
326 """
327
328 if name is None:
329 name = Display._defaultMaskPlaneColor
330
331 if isinstance(name, dict):
332 assert color is None
333 for k, v in name.items():
335 return
336 #
337 # Set the individual color values
338 #
339 Display._defaultMaskPlaneColor[name] = color
340
341 @staticmethod
343 """Set the default colormap for images
344
345 Parameters
346 ----------
347 cmap : `str`
348 Name of colormap, as interpreted by the backend
349
350 Notes
351 -----
352 The only colormaps that all backends are required to honor
353 (if they pay any attention to setImageColormap) are "gray" and "grey"
354 """
355
356 Display._defaultImageColormap = cmap
357
358 def setImageColormap(self, cmap):
359 """Set the colormap to use for images
360
361 Parameters
362 ----------
363 cmap : `str`
364 Name of colormap, as interpreted by the backend
365
366 Notes
367 -----
368 The only colormaps that all backends are required to honor
369 (if they pay any attention to setImageColormap) are "gray" and "grey"
370 """
371
372 self._impl._setImageColormap(cmap)
373
374 @staticmethod
375 def getDisplay(frame=None, backend=None, create=True, verbose=False, *args, **kwargs):
376 """Return a specific `Display`, creating it if need be
377
378 Parameters
379 ----------
380 frame
381 The desired frame (`None` => use defaultFrame (see `~Display.setDefaultFrame`))
382 backend : `str`
383 create the specified frame using this backend (or the default if
384 `None`) if it doesn't already exist. If ``backend == ""``, it's an
385 error to specify a non-existent ``frame``.
386 create : `bool`
387 create the display if it doesn't already exist.
388 verbose : `bool`
389 Allow backend to be chatty
390 *args
391 arguments passed to `Display` constructor
392 **kwargs
393 keyword arguments passed to `Display` constructor
394 """
395
396 if frame is None:
397 frame = Display._defaultFrame
398
399 if frame not in Display._displays:
400 if backend == "":
401 raise RuntimeError(f"Frame {frame} does not exist")
402
403 Display._displays[frame] = Display(
404 frame, backend, verbose=verbose, *args, **kwargs)
405
406 Display._displays[frame].verbose = verbose
407 return Display._displays[frame]
408
409 @staticmethod
411 """Delete and close all known displays
412 """
413 for disp in list(Display._displays.values()):
414 disp.close()
415 Display._displays = {}
416
417 def maskColorGenerator(self, omitBW=True):
418 """A generator for "standard" colors
419
420 Parameters
421 ----------
422 omitBW : `bool`
423 Don't include `BLACK` and `WHITE`
424
425 Examples
426 --------
427
428 .. code-block:: py
429
430 colorGenerator = interface.maskColorGenerator(omitBW=True)
431 for p in planeList:
432 print(p, next(colorGenerator))
433 """
434 _maskColors = [WHITE, BLACK, RED, GREEN,
435 BLUE, CYAN, MAGENTA, YELLOW, ORANGE]
436
437 i = -1
438 while True:
439 i += 1
440 color = _maskColors[i%len(_maskColors)]
441 if omitBW and color in (BLACK, WHITE):
442 continue
443
444 yield color
445
446 def setMaskPlaneColor(self, name, color=None):
447 """Request that mask plane name be displayed as color
448
449 Parameters
450 ----------
451 name : `str` or `dict`
452 Name of mask plane or a dictionary of name -> colorName
453 color : `str`
454 The name of the color to use (must be `None` if ``name`` is a `dict`)
455
456 Colors may be specified as any X11-compliant string (e.g. `"orchid"`), or by one
457 of the following constants in `lsst.afw.display` : `BLACK`, `WHITE`, `RED`, `BLUE`,
458 `GREEN`, `CYAN`, `MAGENTA`, `YELLOW`.
459
460 If the color is "ignore" (or `IGNORE`) then that mask plane is not displayed
461
462 The advantage of using the symbolic names is that the python interpreter can detect typos.
463 """
464
465 if isinstance(name, dict):
466 assert color is None
467 for k, v in name.items():
468 self.setMaskPlaneColor(k, v)
469 return
470
471 self._maskPlaneColors[name] = color
472
473 def getMaskPlaneColor(self, name=None):
474 """Return the color associated with the specified mask plane name
475
476 Parameters
477 ----------
478 name : `str`
479 Desired mask plane; if `None`, return entire dict
480 """
481
482 if name is None:
483 return self._maskPlaneColors
484 else:
485 color = self._maskPlaneColors.get(name)
486
487 if color is None:
488 color = self._defaultMaskPlaneColor.get(name)
489
490 return color
491
492 def setMaskTransparency(self, transparency=None, name=None):
493 """Specify display's mask transparency (percent); or `None` to not set it when loading masks
494 """
495
496 if isinstance(transparency, dict):
497 assert name is None
498 for k, v in transparency.items():
499 self.setMaskTransparency(v, k)
500 return
501
502 if transparency is not None and (transparency < 0 or transparency > 100):
503 print(
504 "Mask transparency should be in the range [0, 100]; clipping", file=sys.stderr)
505 if transparency < 0:
506 transparency = 0
507 else:
508 transparency = 100
509
510 if transparency is not None:
511 self._impl._setMaskTransparency(transparency, name)
512
513 def getMaskTransparency(self, name=None):
514 """Return the current display's mask transparency
515 """
516
517 return self._impl._getMaskTransparency(name)
518
519 def show(self):
520 """Uniconify and Raise display.
521
522 Notes
523 -----
524 Throws an exception if frame doesn't exit
525 """
526 return self._impl._show()
527
528 def __addMissingMaskPlanes(self, mask):
529 """Assign colours to any missing mask planes found in mask"""
530
531 maskPlanes = mask.getMaskPlaneDict()
532 nMaskPlanes = max(maskPlanes.values()) + 1
533
534 planes = {} # build inverse dictionary from mask plane index to name
535 for key in maskPlanes:
536 planes[maskPlanes[key]] = key
537
538 colorGenerator = self.display.maskColorGenerator(omitBW=True)
539 for p in range(nMaskPlanes):
540 name = planes[p] # ordered by plane index
541 if name not in self._defaultMaskPlaneColor:
542 self.setDefaultMaskPlaneColor(name, next(colorGenerator))
543
544 def mtv(self, data, title="", wcs=None):
545 """Display an `~lsst.afw.image.Image` or `~lsst.afw.image.Mask` on a display
546
547 Notes
548 -----
549 Historical note: the name "mtv" comes from Jim Gunn's forth imageprocessing
550 system, Mirella (named after Mirella Freni); The "m" stands for Mirella.
551 """
552 if hasattr(data, "getXY0"):
553 self._xy0 = data.getXY0()
554 else:
555 self._xy0 = None
556
557 # it's an Exposure; display the MaskedImage with the WCS
558 if isinstance(data, afwImage.Exposure):
559 if wcs:
560 raise RuntimeError(
561 "You may not specify a wcs with an Exposure")
562 data, wcs = data.getMaskedImage(), data.getWcs()
563 elif isinstance(data, afwImage.DecoratedImage): # it's a DecoratedImage; display it
564 try:
565 wcs = afwGeom.makeSkyWcs(data.getMetadata())
566 except TypeError:
567 wcs = None
568 data = data.image
569
570 self._xy0 = data.getXY0() # DecoratedImage doesn't have getXY0()
571
572 if isinstance(data, afwImage.Image): # it's an Image; display it
573 self._impl._mtv(data, None, wcs, title)
574 # it's a Mask; display it, bitplane by bitplane
575 elif isinstance(data, afwImage.Mask):
576 self.__addMissingMaskPlanes(data)
577 #
578 # Some displays can't display a Mask without an image; so display an Image too,
579 # with pixel values set to the mask
580 #
581 self._impl._mtv(afwImage.ImageI(data.getArray()), data, wcs, title)
582 # it's a MaskedImage; display Image and overlay Mask
583 elif isinstance(data, afwImage.MaskedImage):
584 self.__addMissingMaskPlanes(data.mask)
585 self._impl._mtv(data.getImage(), data.getMask(), wcs, title)
586 else:
587 raise RuntimeError(f"Unsupported type {data!r}")
588 #
589 # Graphics commands
590 #
591
593 """A class intended to be used with python's with statement
594 """
595
596 def __init__(self, _impl):
597 self._impl = _impl
598
599 def __enter__(self):
600 self._impl._buffer(True)
601
602 def __exit__(self, *args):
603 self._impl._buffer(False)
604 self._impl._flush()
605
606 def Buffering(self):
607 """Return a class intended to be used with python's with statement
608
609 Examples
610 --------
611 .. code-block:: py
612
613 with display.Buffering():
614 display.dot("+", xc, yc)
615 """
616 return self._Buffering(self._impl)
617
618 def flush(self):
619 """Flush the buffers
620 """
621 self._impl._flush()
622
623 def erase(self):
624 """Erase the specified display frame
625 """
626 self._impl._erase()
627
628 def dot(self, symb, c, r, size=2, ctype=None, origin=afwImage.PARENT, *args, **kwargs):
629 """Draw a symbol onto the specified display frame
630
631 Parameters
632 ----------
633 symb
634 Possible values are:
635
636 ``"+"``
637 Draw a +
638 ``"x"``
639 Draw an x
640 ``"*"``
641 Draw a *
642 ``"o"``
643 Draw a circle
644 ``"@:Mxx,Mxy,Myy"``
645 Draw an ellipse with moments (Mxx, Mxy, Myy) (argument size is ignored)
647 Draw the ellipse (argument size is ignored). N.b. objects
648 derived from `~lsst.afw.geom.ellipses.BaseCore` include
650 Any other value
651 Interpreted as a string to be drawn.
652 c, r
653 The column and row where the symbol is drawn [0-based coordinates]
654 size : `int`
655 Size of symbol, in pixels
656 ctype : `str`
657 The desired color, either e.g. `lsst.afw.display.RED` or a color name known to X11
658 origin : `lsst.afw.image.ImageOrigin`
659 Coordinate system for the given positions.
660 *args
661 Extra arguments to backend
662 **kwargs
663 Extra keyword arguments to backend
664 """
665 if isinstance(symb, int):
666 symb = f"{symb:d}"
667
668 if origin == afwImage.PARENT and self._xy0 is not None:
669 x0, y0 = self._xy0
670 r -= y0
671 c -= x0
672
673 if isinstance(symb, afwGeom.ellipses.BaseCore) or re.search(r"^@:", symb):
674 try:
675 mat = re.search(r"^@:([^,]+),([^,]+),([^,]+)", symb)
676 except TypeError:
677 pass
678 else:
679 if mat:
680 mxx, mxy, myy = [float(_) for _ in mat.groups()]
681 symb = afwGeom.Quadrupole(mxx, myy, mxy)
682
683 symb = afwGeom.ellipses.Axes(symb)
684
685 self._impl._dot(symb, c, r, size, ctype, **kwargs)
686
687 def line(self, points, origin=afwImage.PARENT, symbs=False, ctype=None, size=0.5):
688 """Draw a set of symbols or connect points
689
690 Parameters
691 ----------
692 points : `list`
693 a list of (col, row)
694 origin : `lsst.afw.image.ImageOrigin`
695 Coordinate system for the given positions.
696 symbs : `bool` or sequence
697 If ``symbs`` is `True`, draw points at the specified points using the desired symbol,
698 otherwise connect the dots.
699
700 If ``symbs`` supports indexing (which includes a string -- caveat emptor) the
701 elements are used to label the points
702 ctype : `str`
703 ``ctype`` is the name of a color (e.g. 'red')
704 size : `float`
705 """
706 if symbs:
707 try:
708 symbs[1]
709 except TypeError:
710 symbs = len(points)*list(symbs)
711
712 for i, xy in enumerate(points):
713 self.dot(symbs[i], *xy, size=size, ctype=ctype)
714 else:
715 if len(points) > 0:
716 if origin == afwImage.PARENT and self._xy0 is not None:
717 x0, y0 = self._xy0
718 _points = list(points) # make a mutable copy
719 for i, p in enumerate(points):
720 _points[i] = (p[0] - x0, p[1] - y0)
721 points = _points
722
723 self._impl._drawLines(points, ctype)
724 #
725 # Set gray scale
726 #
727
728 def scale(self, algorithm, min, max=None, unit=None, *args, **kwargs):
729 """Set the range of the scaling from DN in the image to the image display
730
731 Parameters
732 ----------
733 algorithm : `str`
734 Desired scaling (e.g. "linear" or "asinh")
735 min
736 Minimum value, or "minmax" or "zscale"
737 max
738 Maximum value (must be `None` for minmax|zscale)
739 unit
740 Units for min and max (e.g. Percent, Absolute, Sigma; `None` if min==minmax|zscale)
741 *args
742 Optional arguments to the backend
743 **kwargs
744 Optional keyword arguments to the backend
745 """
746 if min in ("minmax", "zscale"):
747 assert max is None, f"You may not specify \"{min}\" and max"
748 assert unit is None, f"You may not specify \"{min}\" and unit"
749 elif max is None:
750 raise RuntimeError("Please specify max")
751
752 self._impl._scale(algorithm, min, max, unit, *args, **kwargs)
753 #
754 # Zoom and Pan
755 #
756
757 def zoom(self, zoomfac=None, colc=None, rowc=None, origin=afwImage.PARENT):
758 """Zoom frame by specified amount, optionally panning also
759 """
760
761 if (rowc and colc is None) or (colc and rowc is None):
762 raise RuntimeError(
763 "Please specify row and column center to pan about")
764
765 if rowc is not None:
766 if origin == afwImage.PARENT and self._xy0 is not None:
767 x0, y0 = self._xy0
768 colc -= x0
769 rowc -= y0
770
771 self._impl._pan(colc, rowc)
772
773 if zoomfac is None and rowc is None:
774 zoomfac = 2
775
776 if zoomfac is not None:
777 self._impl._zoom(zoomfac)
778
779 def pan(self, colc=None, rowc=None, origin=afwImage.PARENT):
780 """Pan to a location
781
782 Parameters
783 ----------
784 colc, rowc
785 the coordinates to pan to
786 origin : `lsst.afw.image.ImageOrigin`
787 Coordinate system for the given positions.
788
789 See also
790 --------
791 Display.zoom
792 """
793
794 self.zoom(None, colc, rowc, origin)
795
796 def interact(self):
797 """Enter an interactive loop, listening for key presses in display and firing callbacks.
798
799 Exit with ``q``, ``CR``, ``ESC``, or any other callback function that returns a `True` value.
800 """
801 interactFinished = False
802
803 while not interactFinished:
804 ev = self._impl._getEvent()
805 if not ev:
806 continue
807 k, x, y = ev.k, ev.x, ev.y # for now
808
809 if k not in self._callbacks:
810 logger.warn("No callback registered for {0}".format(k))
811 else:
812 try:
813 interactFinished = self._callbacks[k](k, x, y)
814 except Exception as e:
815 logger.error(
816 "Display._callbacks['{0}']({0},{1},{2}) failed: {3}".format(k, x, y, e))
817
818 def setCallback(self, k, func=None, noRaise=False):
819 """Set the callback for a key
820
821 Parameters
822 ----------
823 k
824 The key to assign the callback to
825 func : callable
826 The callback assigned to ``k``
827 noRaise : `bool`
828
829 Returns
830 -------
831 oldFunc : callable
832 The callback previously assigned to ``k``.
833 """
834
835 if k in "f":
836 if noRaise:
837 return
838 raise RuntimeError(
839 f"Key '{k}' is already in use by display, so I can't add a callback for it")
840
841 ofunc = self._callbacks.get(k)
842 self._callbacks[k] = func if func else noop_callback
843
844 self._impl._setCallback(k, self._callbacks[k])
845
846 return ofunc
847
848 def getActiveCallbackKeys(self, onlyActive=True):
849 """Return all callback keys
850
851 Parameters
852 ----------
853 onlyActive : `bool`
854 If `True` only return keys that do something
855 """
856
857 return sorted([k for k, func in self._callbacks.items() if
858 not (onlyActive and func == noop_callback)])
859
860#
861# Callbacks for display events
862#
863
864
865class Event:
866 """A class to handle events such as key presses in image display windows
867 """
868
869 def __init__(self, k, x=float('nan'), y=float('nan')):
870 self.k = k
871 self.x = x
872 self.y = y
873
874 def __str__(self):
875 return f"{self.k} ({self.x:.2f}, {self.y:.2f}"
876#
877# Default fallback function
878#
879
880
881def noop_callback(k, x, y):
882 """Callback function
883
884 Parameters
885 ----------
886 key
887 x
888 y
889 """
890 return False
891
892
893def h_callback(k, x, y):
894 print("Enter q or <ESC> to leave interactive mode, h for this help, or a letter to fire a callback")
895 return False
896
897#
898# Handle Displays, including the default one (the frame to use when a user specifies None)
899#
900# If the default frame is None, image display is disabled
901#
902
903
904def setDefaultBackend(backend):
905 Display.setDefaultBackend(backend)
906
907
909 return Display.getDefaultBackend()
910
911
912def setDefaultFrame(frame=0):
913 return Display.setDefaultFrame(frame)
914
915
917 """Get the default frame for display
918 """
919 return Display.getDefaultFrame()
920
921
923 """Increment the default frame for display
924 """
925 return Display.incrDefaultFrame()
926
927
928def setDefaultMaskTransparency(maskPlaneTransparency={}):
929 return Display.setDefaultMaskTransparency(maskPlaneTransparency)
930
931
932def setDefaultMaskPlaneColor(name=None, color=None):
933 """Set the default mapping from mask plane names to colors
934
935 Parameters
936 ----------
937 name : `str` or `dict`
938 name of mask plane, or a dict mapping names to colors.
939 If ``name`` is `None`, use the hard-coded default dictionary
940 color : `str`
941 Desired color, or `None` if ``name`` is a dict
942 """
943
944 return Display.setDefaultMaskPlaneColor(name, color)
945
946
947def getDisplay(frame=None, backend=None, create=True, verbose=False, *args, **kwargs):
948 """Return a specific `Display`, creating it if need be
949
950 Parameters
951 ----------
952 frame
953 The desired frame (`None` => use defaultFrame (see `setDefaultFrame`))
954 backend : `str`
955 Create the specified frame using this backend (or the default if
956 `None`) if it doesn't already exist. If ``backend == ""``, it's an
957 error to specify a non-existent ``frame``.
958 create : `bool`
959 Create the display if it doesn't already exist.
960 verbose : `bool`
961 Allow backend to be chatty
962 *args
963 arguments passed to `Display` constructor
964 **kwargs
965 keyword arguments passed to `Display` constructor
966
967 See also
968 --------
969 Display.getDisplay
970 """
971
972 return Display.getDisplay(frame, backend, create, verbose, *args, **kwargs)
973
974
976 """Delete and close all known displays
977 """
978 return Display.delAllDisplays()
std::vector< SchemaItem< Flag > > * items
int max
mtv(self, data, title="", wcs=None)
Definition interface.py:544
setMaskTransparency(self, transparency=None, name=None)
Definition interface.py:492
getActiveCallbackKeys(self, onlyActive=True)
Definition interface.py:848
dot(self, symb, c, r, size=2, ctype=None, origin=afwImage.PARENT, *args, **kwargs)
Definition interface.py:628
line(self, points, origin=afwImage.PARENT, symbs=False, ctype=None, size=0.5)
Definition interface.py:687
setDefaultMaskTransparency(maskPlaneTransparency={})
Definition interface.py:309
pan(self, colc=None, rowc=None, origin=afwImage.PARENT)
Definition interface.py:779
setCallback(self, k, func=None, noRaise=False)
Definition interface.py:818
setMaskPlaneColor(self, name, color=None)
Definition interface.py:446
setDefaultMaskPlaneColor(name=None, color=None)
Definition interface.py:316
scale(self, algorithm, min, max=None, unit=None, *args, **kwargs)
Definition interface.py:728
__init__(self, frame=None, backend=None, *args, **kwargs)
Definition interface.py:163
maskColorGenerator(self, omitBW=True)
Definition interface.py:417
zoom(self, zoomfac=None, colc=None, rowc=None, origin=afwImage.PARENT)
Definition interface.py:757
__init__(self, k, x=float('nan'), y=float('nan'))
Definition interface.py:869
An ellipse core for the semimajor/semiminor axis and position angle parametrization (a,...
Definition Axes.h:47
A base class for parametrizations of the "core" of an ellipse - the ellipticity and size.
Definition BaseCore.h:55
An ellipse core with quadrupole moments as parameters.
Definition Quadrupole.h:47
A container for an Image and its associated metadata.
Definition Image.h:406
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Definition Exposure.h:72
A class to represent a 2-dimensional array of pixels.
Definition Image.h:51
Represent a 2-dimensional array of bitmask pixels.
Definition Mask.h:77
A class to manipulate images, masks, and variance as a single object.
Definition MaskedImage.h:74
static Log getLogger(Log const &logger)
Definition Log.h:772
daf::base::PropertyList * list
Definition fits.cc:928
setDefaultMaskTransparency(maskPlaneTransparency={})
Definition interface.py:928
setDefaultMaskPlaneColor(name=None, color=None)
Definition interface.py:932
_makeDisplayImpl(display, backend, *args, **kwargs)
Definition interface.py:56
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
Definition SkyWcs.cc:521