LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
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 .. code-block:: py
74
75 import lsst.afw.display as afwDisplay
76 display = afwDisplay.Display(display=1, backend="ds9")
77 would call
78 .. code-block:: py
79
80 _makeDisplayImpl(..., "ds9", 1)
81 and import the ds9 implementation of ``DisplayImpl`` from `lsst.display.ds9`
82 """
83 _disp = None
84 exc = None
85 for dt in (f"lsst.display.{backend}", backend, f".{backend}", f"lsst.afw.display.{backend}"):
86 exc = None
87 # only specify the root package if we are not doing an absolute import
88 impargs = {}
89 if dt.startswith("."):
90 impargs["package"] = "lsst.display"
91 try:
92 _disp = importlib.import_module(dt, **impargs)
93 break
94 except (ImportError, SystemError) as e:
95 # SystemError can be raised in Python 3.5 if a relative import
96 # is attempted when the root package, lsst.display, does not exist.
97 # Copy the exception into outer scope
98 exc = e
99
100 if not _disp or not hasattr(_disp.DisplayImpl, "_show"):
101 if exc is not None:
102 # re-raise the final exception
103 raise exc
104 else:
105 raise ImportError(
106 "Could not load the requested backend: {}".format(backend))
107
108 if display:
109 _impl = _disp.DisplayImpl(display, *args, **kwargs)
110 if not hasattr(_impl, "frame"):
111 _impl.frame = display.frame
112
113 return _impl
114 else:
115 return True
116
117
119 """Create an object able to display images and overplot glyphs
120
121 Parameters
122 ----------
123 frame
124 An identifier for the display
125 backend : `str`
126 The backend to use (defaults to value set by setDefaultBackend())
127 *args
128 Arguments to pass to the backend
129 **kwargs
130 Arguments to pass to the backend
131 """
132 _displays = {}
133 _defaultBackend = None
134 _defaultFrame = 0
135 _defaultMaskPlaneColor = dict(
136 BAD=RED,
137 CR=MAGENTA,
138 EDGE=YELLOW,
139 INTERPOLATED=GREEN,
140 SATURATED=GREEN,
141 DETECTED=BLUE,
142 DETECTED_NEGATIVE=CYAN,
143 SUSPECT=YELLOW,
144 NO_DATA=ORANGE,
145 # deprecated names
146 INTRP=GREEN,
147 SAT=GREEN,
148 )
149 _defaultMaskTransparency = {}
150 _defaultImageColormap = "gray"
151
152 def __init__(self, frame=None, backend=None, *args, **kwargs):
153 if frame is None:
154 frame = getDefaultFrame()
155
156 if backend is None:
157 if Display._defaultBackend is None:
158 try:
159 setDefaultBackend("ds9")
160 except RuntimeError:
161 setDefaultBackend("virtualDevice")
162
163 backend = Display._defaultBackend
164
165 self.frameframe = frame
166 self._impl_impl = _makeDisplayImpl(self, backend, *args, **kwargs)
167 self.namename = backend
168
169 self._xy0_xy0 = None # displayed data's XY0
170 self.setMaskTransparencysetMaskTransparency(Display._defaultMaskTransparency)
171 self._maskPlaneColors_maskPlaneColors = {}
172 self.setMaskPlaneColorsetMaskPlaneColor(Display._defaultMaskPlaneColor)
173 self.setImageColormapsetImageColormap(Display._defaultImageColormap)
174
175 self._callbacks_callbacks = {}
176
177 for ik in range(ord('a'), ord('z') + 1):
178 k = f"{ik:c}"
179 self.setCallbacksetCallback(k, noRaise=True)
180 self.setCallbacksetCallback(k.upper(), noRaise=True)
181
182 for k in ('Return', 'Shift_L', 'Shift_R'):
183 self.setCallbacksetCallback(k)
184
185 for k in ('q', 'Escape'):
186 self.setCallbacksetCallback(k, lambda k, x, y: True)
187
188 def _h_callback(k, x, y):
189 h_callback(k, x, y)
190
191 for k in sorted(self._callbacks_callbacks.keys()):
192 doc = self._callbacks_callbacks[k].__doc__
193 print(" %-6s %s" % (k, doc.split("\n")[0] if doc else "???"))
194
195 self.setCallbacksetCallback('h', _h_callback)
196
197 Display._displays[frame] = self
198
199 def __enter__(self):
200 """Support for python's with statement
201 """
202 return self
203
204 def __exit__(self, *args):
205 """Support for python's with statement
206 """
207 self.closeclose()
208
209 def __del__(self):
210 self.closeclose()
211
212 def __getattr__(self, name):
213 """Return the attribute of ``self._impl``, or ``._impl`` if it is requested
214
215 Parameters:
216 -----------
217 name : `str`
218 name of the attribute requested
219
220 Returns:
221 --------
222 attribute : `object`
223 the attribute of self._impl_impl for the requested name
224 """
225
226 if name == '_impl':
227 return object.__getattr__(self, name)
228
229 if not (hasattr(self, "_impl") and self._impl_impl):
230 raise AttributeError("Device has no _impl attached")
231
232 try:
233 return getattr(self._impl_impl, name)
234 except AttributeError:
235 raise AttributeError(
236 f"Device {self.name} has no attribute \"{name}\"")
237
238 def close(self):
239 if getattr(self, "_impl", None) is not None:
240 self._impl_impl._close()
241 del self._impl_impl
242 self._impl_impl = None
243
244 if self.frameframe in Display._displays:
245 del Display._displays[self.frameframe]
246
247 @property
248 def verbose(self):
249 """The backend's verbosity
250 """
251 return self._impl_impl.verbose
252
253 @verbose.setter
254 def verbose(self, value):
255 if self._impl_impl:
256 self._impl_impl.verbose = value
257
258 def __str__(self):
259 return f"Display[{self.frame}]"
260
261 #
262 # Handle Displays, including the default one (the frame to use when a user specifies None)
263 #
264 @staticmethod
265 def setDefaultBackend(backend):
266 try:
267 _makeDisplayImpl(None, backend)
268 except Exception as e:
269 raise RuntimeError(
270 f"Unable to set backend to {backend}: \"{e}\"")
271
272 Display._defaultBackend = backend
273
274 @staticmethod
276 return Display._defaultBackend
277
278 @staticmethod
279 def setDefaultFrame(frame=0):
280 """Set the default frame for display
281 """
282 Display._defaultFrame = frame
283
284 @staticmethod
286 """Get the default frame for display
287 """
288 return Display._defaultFrame
289
290 @staticmethod
292 """Increment the default frame for display
293 """
294 Display._defaultFrame += 1
295 return Display._defaultFrame
296
297 @staticmethod
298 def setDefaultMaskTransparency(maskPlaneTransparency={}):
299 if hasattr(maskPlaneTransparency, "copy"):
300 maskPlaneTransparency = maskPlaneTransparency.copy()
301
302 Display._defaultMaskTransparency = maskPlaneTransparency
303
304 @staticmethod
305 def setDefaultMaskPlaneColor(name=None, color=None):
306 """Set the default mapping from mask plane names to colors
307
308 Parameters
309 ----------
310 name : `str` or `dict`
311 name of mask plane, or a dict mapping names to colors
312 If name is `None`, use the hard-coded default dictionary
313 color
314 Desired color, or `None` if name is a dict
315 """
316
317 if name is None:
318 name = Display._defaultMaskPlaneColor
319
320 if isinstance(name, dict):
321 assert color is None
322 for k, v in name.items():
324 return
325 #
326 # Set the individual color values
327 #
328 Display._defaultMaskPlaneColor[name] = color
329
330 @staticmethod
332 """Set the default colormap for images
333
334 Parameters
335 ----------
336 cmap : `str`
337 Name of colormap, as interpreted by the backend
338
339 Notes
340 -----
341 The only colormaps that all backends are required to honor
342 (if they pay any attention to setImageColormap) are "gray" and "grey"
343 """
344
345 Display._defaultImageColormap = cmap
346
347 def setImageColormap(self, cmap):
348 """Set the colormap to use for images
349
350 Parameters
351 ----------
352 cmap : `str`
353 Name of colormap, as interpreted by the backend
354
355 Notes
356 -----
357 The only colormaps that all backends are required to honor
358 (if they pay any attention to setImageColormap) are "gray" and "grey"
359 """
360
361 self._impl_impl._setImageColormap(cmap)
362
363 @staticmethod
364 def getDisplay(frame=None, backend=None, create=True, verbose=False, *args, **kwargs):
365 """Return a specific `Display`, creating it if need be
366
367 Parameters
368 ----------
369 frame
370 The desired frame (`None` => use defaultFrame (see `~Display.setDefaultFrame`))
371 backend : `str`
372 create the specified frame using this backend (or the default if
373 `None`) if it doesn't already exist. If ``backend == ""``, it's an
374 error to specify a non-existent ``frame``.
375 create : `bool`
376 create the display if it doesn't already exist.
377 verbose : `bool`
378 Allow backend to be chatty
379 *args
380 arguments passed to `Display` constructor
381 **kwargs
382 keyword arguments passed to `Display` constructor
383 """
384
385 if frame is None:
386 frame = Display._defaultFrame
387
388 if frame not in Display._displays:
389 if backend == "":
390 raise RuntimeError(f"Frame {frame} does not exist")
391
392 Display._displays[frame] = Display(
393 frame, backend, verbose=verbose, *args, **kwargs)
394
395 Display._displays[frame].verbose = verbose
396 return Display._displays[frame]
397
398 @staticmethod
400 """Delete and close all known displays
401 """
402 for disp in list(Display._displays.values()):
403 disp.close()
404 Display._displays = {}
405
406 def maskColorGenerator(self, omitBW=True):
407 """A generator for "standard" colors
408
409 Parameters
410 ----------
411 omitBW : `bool`
412 Don't include `BLACK` and `WHITE`
413
414 Examples
415 --------
416
417 .. code-block:: py
418
419 colorGenerator = interface.maskColorGenerator(omitBW=True)
420 for p in planeList:
421 print p, next(colorGenerator)
422 """
423 _maskColors = [WHITE, BLACK, RED, GREEN,
424 BLUE, CYAN, MAGENTA, YELLOW, ORANGE]
425
426 i = -1
427 while True:
428 i += 1
429 color = _maskColors[i%len(_maskColors)]
430 if omitBW and color in (BLACK, WHITE):
431 continue
432
433 yield color
434
435 def setMaskPlaneColor(self, name, color=None):
436 """Request that mask plane name be displayed as color
437
438 Parameters
439 ----------
440 name : `str` or `dict`
441 Name of mask plane or a dictionary of name -> colorName
442 color : `str`
443 The name of the color to use (must be `None` if ``name`` is a `dict`)
444
445 Colors may be specified as any X11-compliant string (e.g. `"orchid"`), or by one
446 of the following constants in `lsst.afw.display` : `BLACK`, `WHITE`, `RED`, `BLUE`,
447 `GREEN`, `CYAN`, `MAGENTA`, `YELLOW`.
448
449 If the color is "ignore" (or `IGNORE`) then that mask plane is not displayed
450
451 The advantage of using the symbolic names is that the python interpreter can detect typos.
452 """
453
454 if isinstance(name, dict):
455 assert color is None
456 for k, v in name.items():
457 self.setMaskPlaneColorsetMaskPlaneColor(k, v)
458 return
459
460 self._maskPlaneColors_maskPlaneColors[name] = color
461
462 def getMaskPlaneColor(self, name=None):
463 """Return the color associated with the specified mask plane name
464
465 Parameters
466 ----------
467 name : `str`
468 Desired mask plane; if `None`, return entire dict
469 """
470
471 if name is None:
472 return self._maskPlaneColors_maskPlaneColors
473 else:
474 return self._maskPlaneColors_maskPlaneColors.get(name)
475
476 def setMaskTransparency(self, transparency=None, name=None):
477 """Specify display's mask transparency (percent); or `None` to not set it when loading masks
478 """
479
480 if isinstance(transparency, dict):
481 assert name is None
482 for k, v in transparency.items():
483 self.setMaskTransparencysetMaskTransparency(v, k)
484 return
485
486 if transparency is not None and (transparency < 0 or transparency > 100):
487 print(
488 "Mask transparency should be in the range [0, 100]; clipping", file=sys.stderr)
489 if transparency < 0:
490 transparency = 0
491 else:
492 transparency = 100
493
494 if transparency is not None:
495 self._impl_impl._setMaskTransparency(transparency, name)
496
497 def getMaskTransparency(self, name=None):
498 """Return the current display's mask transparency
499 """
500
501 return self._impl_impl._getMaskTransparency(name)
502
503 def show(self):
504 """Uniconify and Raise display.
505
506 Notes
507 -----
508 Throws an exception if frame doesn't exit
509 """
510 return self._impl_impl._show()
511
512 def mtv(self, data, title="", wcs=None):
513 """Display an `~lsst.afw.image.Image` or `~lsst.afw.image.Mask` on a display
514
515 Notes
516 -----
517 Historical note: the name "mtv" comes from Jim Gunn's forth imageprocessing
518 system, Mirella (named after Mirella Freni); The "m" stands for Mirella.
519 """
520 if hasattr(data, "getXY0"):
521 self._xy0_xy0 = data.getXY0()
522 else:
523 self._xy0_xy0 = None
524
525 # it's an Exposure; display the MaskedImage with the WCS
526 if isinstance(data, afwImage.Exposure):
527 if wcs:
528 raise RuntimeError(
529 "You may not specify a wcs with an Exposure")
530 data, wcs = data.getMaskedImage(), data.getWcs()
531 elif isinstance(data, afwImage.DecoratedImage): # it's a DecoratedImage; display it
532 try:
533 wcs = afwGeom.makeSkyWcs(data.getMetadata())
534 except TypeError:
535 wcs = None
536 data = data.image
537
538 self._xy0_xy0 = data.getXY0() # DecoratedImage doesn't have getXY0()
539
540 if isinstance(data, afwImage.Image): # it's an Image; display it
541 self._impl_impl._mtv(data, None, wcs, title)
542 # it's a Mask; display it, bitplane by bitplane
543 elif isinstance(data, afwImage.Mask):
544 #
545 # Some displays can't display a Mask without an image; so display an Image too,
546 # with pixel values set to the mask
547 #
548 self._impl_impl._mtv(afwImage.ImageI(data.getArray()), data, wcs, title)
549 # it's a MaskedImage; display Image and overlay Mask
550 elif isinstance(data, afwImage.MaskedImage):
551 self._impl_impl._mtv(data.getImage(), data.getMask(), wcs, title)
552 else:
553 raise RuntimeError(f"Unsupported type {data!r}")
554 #
555 # Graphics commands
556 #
557
559 """A class intended to be used with python's with statement
560 """
561
562 def __init__(self, _impl):
563 self._impl_impl = _impl
564
565 def __enter__(self):
566 self._impl_impl._buffer(True)
567
568 def __exit__(self, *args):
569 self._impl_impl._buffer(False)
570 self._impl_impl._flush()
571
572 def Buffering(self):
573 """Return a class intended to be used with python's with statement
574
575 Examples
576 --------
577 .. code-block:: py
578
579 with display.Buffering():
580 display.dot("+", xc, yc)
581 """
582 return self._Buffering_Buffering(self._impl_impl)
583
584 def flush(self):
585 """Flush the buffers
586 """
587 self._impl_impl._flush()
588
589 def erase(self):
590 """Erase the specified display frame
591 """
592 self._impl_impl._erase()
593
594 def dot(self, symb, c, r, size=2, ctype=None, origin=afwImage.PARENT, *args, **kwargs):
595 """Draw a symbol onto the specified display frame
596
597 Parameters
598 ----------
599 symb
600 Possible values are:
601
602 ``"+"``
603 Draw a +
604 ``"x"``
605 Draw an x
606 ``"*"``
607 Draw a *
608 ``"o"``
609 Draw a circle
610 ``"@:Mxx,Mxy,Myy"``
611 Draw an ellipse with moments (Mxx, Mxy, Myy) (argument size is ignored)
613 Draw the ellipse (argument size is ignored). N.b. objects
614 derived from `~lsst.afw.geom.ellipses.BaseCore` include
616 Any other value
617 Interpreted as a string to be drawn.
618 c, r
619 The column and row where the symbol is drawn [0-based coordinates]
620 size : `int`
621 Size of symbol, in pixels
622 ctype : `str`
623 The desired color, either e.g. `lsst.afw.display.RED` or a color name known to X11
624 origin : `lsst.afw.image.ImageOrigin`
625 Coordinate system for the given positions.
626 *args
627 Extra arguments to backend
628 **kwargs
629 Extra keyword arguments to backend
630 """
631 if isinstance(symb, int):
632 symb = f"{symb:d}"
633
634 if origin == afwImage.PARENT and self._xy0_xy0 is not None:
635 x0, y0 = self._xy0_xy0
636 r -= y0
637 c -= x0
638
639 if isinstance(symb, afwGeom.ellipses.BaseCore) or re.search(r"^@:", symb):
640 try:
641 mat = re.search(r"^@:([^,]+),([^,]+),([^,]+)", symb)
642 except TypeError:
643 pass
644 else:
645 if mat:
646 mxx, mxy, myy = [float(_) for _ in mat.groups()]
647 symb = afwGeom.Quadrupole(mxx, myy, mxy)
648
649 symb = afwGeom.ellipses.Axes(symb)
650
651 self._impl_impl._dot(symb, c, r, size, ctype, **kwargs)
652
653 def line(self, points, origin=afwImage.PARENT, symbs=False, ctype=None, size=0.5):
654 """Draw a set of symbols or connect points
655
656 Parameters
657 ----------
658 points : `list`
659 a list of (col, row)
660 origin : `lsst.afw.image.ImageOrigin`
661 Coordinate system for the given positions.
662 symbs : `bool` or sequence
663 If ``symbs`` is `True`, draw points at the specified points using the desired symbol,
664 otherwise connect the dots.
665
666 If ``symbs`` supports indexing (which includes a string -- caveat emptor) the
667 elements are used to label the points
668 ctype : `str`
669 ``ctype`` is the name of a color (e.g. 'red')
670 size : `float`
671 """
672 if symbs:
673 try:
674 symbs[1]
675 except TypeError:
676 symbs = len(points)*list(symbs)
677
678 for i, xy in enumerate(points):
679 self.dotdot(symbs[i], *xy, size=size, ctype=ctype)
680 else:
681 if len(points) > 0:
682 if origin == afwImage.PARENT and self._xy0_xy0 is not None:
683 x0, y0 = self._xy0_xy0
684 _points = list(points) # make a mutable copy
685 for i, p in enumerate(points):
686 _points[i] = (p[0] - x0, p[1] - y0)
687 points = _points
688
689 self._impl_impl._drawLines(points, ctype)
690 #
691 # Set gray scale
692 #
693
694 def scale(self, algorithm, min, max=None, unit=None, *args, **kwargs):
695 """Set the range of the scaling from DN in the image to the image display
696
697 Parameters
698 ----------
699 algorithm : `str`
700 Desired scaling (e.g. "linear" or "asinh")
701 min
702 Minimum value, or "minmax" or "zscale"
703 max
704 Maximum value (must be `None` for minmax|zscale)
705 unit
706 Units for min and max (e.g. Percent, Absolute, Sigma; `None` if min==minmax|zscale)
707 *args
708 Optional arguments to the backend
709 **kwargs
710 Optional keyword arguments to the backend
711 """
712 if min in ("minmax", "zscale"):
713 assert max is None, f"You may not specify \"{min}\" and max"
714 assert unit is None, f"You may not specify \"{min}\" and unit"
715 elif max is None:
716 raise RuntimeError("Please specify max")
717
718 self._impl_impl._scale(algorithm, min, max, unit, *args, **kwargs)
719 #
720 # Zoom and Pan
721 #
722
723 def zoom(self, zoomfac=None, colc=None, rowc=None, origin=afwImage.PARENT):
724 """Zoom frame by specified amount, optionally panning also
725 """
726
727 if (rowc and colc is None) or (colc and rowc is None):
728 raise RuntimeError(
729 "Please specify row and column center to pan about")
730
731 if rowc is not None:
732 if origin == afwImage.PARENT and self._xy0_xy0 is not None:
733 x0, y0 = self._xy0_xy0
734 colc -= x0
735 rowc -= y0
736
737 self._impl_impl._pan(colc, rowc)
738
739 if zoomfac is None and rowc is None:
740 zoomfac = 2
741
742 if zoomfac is not None:
743 self._impl_impl._zoom(zoomfac)
744
745 def pan(self, colc=None, rowc=None, origin=afwImage.PARENT):
746 """Pan to a location
747
748 Parameters
749 ----------
750 colc, rowc
751 the coordinates to pan to
752 origin : `lsst.afw.image.ImageOrigin`
753 Coordinate system for the given positions.
754
755 See also
756 --------
757 Display.zoom
758 """
759
760 self.zoomzoom(None, colc, rowc, origin)
761
762 def interact(self):
763 """Enter an interactive loop, listening for key presses in display and firing callbacks.
764
765 Exit with ``q``, ``CR``, ``ESC``, or any other callback function that returns a `True` value.
766 """
767 interactFinished = False
768
769 while not interactFinished:
770 ev = self._impl_impl._getEvent()
771 if not ev:
772 continue
773 k, x, y = ev.k, ev.x, ev.y # for now
774
775 if k not in self._callbacks_callbacks:
776 logger.warn("No callback registered for {0}".format(k))
777 else:
778 try:
779 interactFinished = self._callbacks_callbacks[k](k, x, y)
780 except Exception as e:
781 logger.error(
782 "Display._callbacks['{0}']({0},{1},{2}) failed: {3}".format(k, x, y, e))
783
784 def setCallback(self, k, func=None, noRaise=False):
785 """Set the callback for a key
786
787 Parameters
788 ----------
789 k
790 The key to assign the callback to
791 func : callable
792 The callback assigned to ``k``
793 noRaise : `bool`
794
795 Returns
796 -------
797 oldFunc : callable
798 The callback previously assigned to ``k``.
799 """
800
801 if k in "f":
802 if noRaise:
803 return
804 raise RuntimeError(
805 f"Key '{k}' is already in use by display, so I can't add a callback for it")
806
807 ofunc = self._callbacks_callbacks.get(k)
808 self._callbacks_callbacks[k] = func if func else noop_callback
809
810 self._impl_impl._setCallback(k, self._callbacks_callbacks[k])
811
812 return ofunc
813
814 def getActiveCallbackKeys(self, onlyActive=True):
815 """Return all callback keys
816
817 Parameters
818 ----------
819 onlyActive : `bool`
820 If `True` only return keys that do something
821 """
822
823 return sorted([k for k, func in self._callbacks_callbacks.items() if
824 not (onlyActive and func == noop_callback)])
825
826#
827# Callbacks for display events
828#
829
830
831class Event:
832 """A class to handle events such as key presses in image display windows
833 """
834
835 def __init__(self, k, x=float('nan'), y=float('nan')):
836 self.kk = k
837 self.xx = x
838 self.yy = y
839
840 def __str__(self):
841 return f"{self.k} ({self.x:.2f}, {self.y:.2f}"
842#
843# Default fallback function
844#
845
846
847def noop_callback(k, x, y):
848 """Callback function
849
850 Parameters
851 ----------
852 key
853 x
854 y
855 """
856 return False
857
858
859def h_callback(k, x, y):
860 print("Enter q or <ESC> to leave interactive mode, h for this help, or a letter to fire a callback")
861 return False
862
863#
864# Handle Displays, including the default one (the frame to use when a user specifies None)
865#
866# If the default frame is None, image display is disabled
867#
868
869
870def setDefaultBackend(backend):
871 Display.setDefaultBackend(backend)
872
873
875 return Display.getDefaultBackend()
876
877
878def setDefaultFrame(frame=0):
879 return Display.setDefaultFrame(frame)
880
881
883 """Get the default frame for display
884 """
885 return Display.getDefaultFrame()
886
887
889 """Increment the default frame for display
890 """
891 return Display.incrDefaultFrame()
892
893
894def setDefaultMaskTransparency(maskPlaneTransparency={}):
895 return Display.setDefaultMaskTransparency(maskPlaneTransparency)
896
897
898def setDefaultMaskPlaneColor(name=None, color=None):
899 """Set the default mapping from mask plane names to colors
900
901 Parameters
902 ----------
903 name : `str` or `dict`
904 name of mask plane, or a dict mapping names to colors.
905 If ``name`` is `None`, use the hard-coded default dictionary
906 color : `str`
907 Desired color, or `None` if ``name`` is a dict
908 """
909
910 return Display.setDefaultMaskPlaneColor(name, color)
911
912
913def getDisplay(frame=None, backend=None, create=True, verbose=False, *args, **kwargs):
914 """Return a specific `Display`, creating it if need be
915
916 Parameters
917 ----------
918 frame
919 The desired frame (`None` => use defaultFrame (see `setDefaultFrame`))
920 backend : `str`
921 Create the specified frame using this backend (or the default if
922 `None`) if it doesn't already exist. If ``backend == ""``, it's an
923 error to specify a non-existent ``frame``.
924 create : `bool`
925 Create the display if it doesn't already exist.
926 verbose : `bool`
927 Allow backend to be chatty
928 *args
929 arguments passed to `Display` constructor
930 **kwargs
931 keyword arguments passed to `Display` constructor
932
933 See also
934 --------
935 Display.getDisplay
936 """
937
938 return Display.getDisplay(frame, backend, create, verbose, *args, **kwargs)
939
940
942 """Delete and close all known displays
943 """
944 return Display.delAllDisplays()
std::vector< SchemaItem< Flag > > * items
def maskColorGenerator(self, omitBW=True)
Definition: interface.py:406
def setDefaultMaskTransparency(maskPlaneTransparency={})
Definition: interface.py:298
def dot(self, symb, c, r, size=2, ctype=None, origin=afwImage.PARENT, *args, **kwargs)
Definition: interface.py:594
def zoom(self, zoomfac=None, colc=None, rowc=None, origin=afwImage.PARENT)
Definition: interface.py:723
def getActiveCallbackKeys(self, onlyActive=True)
Definition: interface.py:814
def line(self, points, origin=afwImage.PARENT, symbs=False, ctype=None, size=0.5)
Definition: interface.py:653
def setCallback(self, k, func=None, noRaise=False)
Definition: interface.py:784
def mtv(self, data, title="", wcs=None)
Definition: interface.py:512
def setMaskTransparency(self, transparency=None, name=None)
Definition: interface.py:476
def getDisplay(frame=None, backend=None, create=True, verbose=False, *args, **kwargs)
Definition: interface.py:364
def getMaskPlaneColor(self, name=None)
Definition: interface.py:462
def pan(self, colc=None, rowc=None, origin=afwImage.PARENT)
Definition: interface.py:745
def setMaskPlaneColor(self, name, color=None)
Definition: interface.py:435
def setDefaultMaskPlaneColor(name=None, color=None)
Definition: interface.py:305
def scale(self, algorithm, min, max=None, unit=None, *args, **kwargs)
Definition: interface.py:694
def __init__(self, frame=None, backend=None, *args, **kwargs)
Definition: interface.py:152
def getMaskTransparency(self, name=None)
Definition: interface.py:497
def __init__(self, k, x=float('nan'), y=float('nan'))
Definition: interface.py:835
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:430
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:73
static Log getLogger(Log const &logger)
Definition: Log.h:772
daf::base::PropertyList * list
Definition: fits.cc:913
def noop_callback(k, x, y)
Definition: interface.py:847
def getDisplay(frame=None, backend=None, create=True, verbose=False, *args, **kwargs)
Definition: interface.py:913
def setDefaultMaskTransparency(maskPlaneTransparency={})
Definition: interface.py:894
def setDefaultFrame(frame=0)
Definition: interface.py:878
def setDefaultMaskPlaneColor(name=None, color=None)
Definition: interface.py:898
def setDefaultBackend(backend)
Definition: interface.py:870
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
Definition: SkyWcs.cc:521
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Definition: Log.h:717
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174