LSST Applications g070148d5b3+33e5256705,g0d53e28543+25c8b88941,g0da5cf3356+2dd1178308,g1081da9e2a+62d12e78cb,g17e5ecfddb+7e422d6136,g1c76d35bf8+ede3a706f7,g295839609d+225697d880,g2e2c1a68ba+cc1f6f037e,g2ffcdf413f+853cd4dcde,g38293774b4+62d12e78cb,g3b44f30a73+d953f1ac34,g48ccf36440+885b902d19,g4b2f1765b6+7dedbde6d2,g5320a0a9f6+0c5d6105b6,g56b687f8c9+ede3a706f7,g5c4744a4d9+ef6ac23297,g5ffd174ac0+0c5d6105b6,g6075d09f38+66af417445,g667d525e37+2ced63db88,g670421136f+2ced63db88,g71f27ac40c+2ced63db88,g774830318a+463cbe8d1f,g7876bc68e5+1d137996f1,g7985c39107+62d12e78cb,g7fdac2220c+0fd8241c05,g96f01af41f+368e6903a7,g9ca82378b8+2ced63db88,g9d27549199+ef6ac23297,gabe93b2c52+e3573e3735,gb065e2a02a+3dfbe639da,gbc3249ced9+0c5d6105b6,gbec6a3398f+0c5d6105b6,gc9534b9d65+35b9f25267,gd01420fc67+0c5d6105b6,geee7ff78d7+a14128c129,gf63283c776+ede3a706f7,gfed783d017+0c5d6105b6,w.2022.47
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 .. 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.frame = frame
166 self._impl = _makeDisplayImpl(self, backend, *args, **kwargs)
167 self.name = backend
168
169 self._xy0 = None # displayed data's XY0
170 self.setMaskTransparency(Display._defaultMaskTransparency)
171 self._maskPlaneColors = {}
172 self.setMaskPlaneColor(Display._defaultMaskPlaneColor)
173 self.setImageColormap(Display._defaultImageColormap)
174
175 self._callbacks = {}
176
177 for ik in range(ord('a'), ord('z') + 1):
178 k = f"{ik:c}"
179 self.setCallback(k, noRaise=True)
180 self.setCallback(k.upper(), noRaise=True)
181
182 for k in ('Return', 'Shift_L', 'Shift_R'):
183 self.setCallback(k)
184
185 for k in ('q', 'Escape'):
186 self.setCallback(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.keys()):
192 doc = self._callbacks[k].__doc__
193 print(" %-6s %s" % (k, doc.split("\n")[0] if doc else "???"))
194
195 self.setCallback('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.close()
208
209 def __del__(self):
210 self.close()
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 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):
230 raise AttributeError("Device has no _impl attached")
231
232 try:
233 return getattr(self._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._close()
241 del self._impl
242 self._impl = None
243
244 if self.frame in Display._displays:
245 del Display._displays[self.frame]
246
247 @property
248 def verbose(self):
249 """The backend's verbosity
250 """
251 return self._impl.verbose
252
253 @verbose.setter
254 def verbose(self, value):
255 if self._impl:
256 self._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._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.setMaskPlaneColor(k, v)
458 return
459
460 self._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
473 else:
474 color = self._maskPlaneColors.get(name)
475
476 if color is None:
477 color = self._defaultMaskPlaneColor.get(name)
478
479 return color
480
481 def setMaskTransparency(self, transparency=None, name=None):
482 """Specify display's mask transparency (percent); or `None` to not set it when loading masks
483 """
484
485 if isinstance(transparency, dict):
486 assert name is None
487 for k, v in transparency.items():
488 self.setMaskTransparency(v, k)
489 return
490
491 if transparency is not None and (transparency < 0 or transparency > 100):
492 print(
493 "Mask transparency should be in the range [0, 100]; clipping", file=sys.stderr)
494 if transparency < 0:
495 transparency = 0
496 else:
497 transparency = 100
498
499 if transparency is not None:
500 self._impl._setMaskTransparency(transparency, name)
501
502 def getMaskTransparency(self, name=None):
503 """Return the current display's mask transparency
504 """
505
506 return self._impl._getMaskTransparency(name)
507
508 def show(self):
509 """Uniconify and Raise display.
510
511 Notes
512 -----
513 Throws an exception if frame doesn't exit
514 """
515 return self._impl._show()
516
517 def __addMissingMaskPlanes(self, mask):
518 """Assign colours to any missing mask planes found in mask"""
519
520 maskPlanes = mask.getMaskPlaneDict()
521 nMaskPlanes = max(maskPlanes.values()) + 1
522
523 planes = {} # build inverse dictionary from mask plane index to name
524 for key in maskPlanes:
525 planes[maskPlanes[key]] = key
526
527 colorGenerator = self.display.maskColorGenerator(omitBW=True)
528 for p in range(nMaskPlanes):
529 name = planes[p] # ordered by plane index
530 if name not in self._defaultMaskPlaneColor:
531 self.setDefaultMaskPlaneColor(name, next(colorGenerator))
532
533 def mtv(self, data, title="", wcs=None):
534 """Display an `~lsst.afw.image.Image` or `~lsst.afw.image.Mask` on a display
535
536 Notes
537 -----
538 Historical note: the name "mtv" comes from Jim Gunn's forth imageprocessing
539 system, Mirella (named after Mirella Freni); The "m" stands for Mirella.
540 """
541 if hasattr(data, "getXY0"):
542 self._xy0 = data.getXY0()
543 else:
544 self._xy0 = None
545
546 # it's an Exposure; display the MaskedImage with the WCS
547 if isinstance(data, afwImage.Exposure):
548 if wcs:
549 raise RuntimeError(
550 "You may not specify a wcs with an Exposure")
551 data, wcs = data.getMaskedImage(), data.getWcs()
552 elif isinstance(data, afwImage.DecoratedImage): # it's a DecoratedImage; display it
553 try:
554 wcs = afwGeom.makeSkyWcs(data.getMetadata())
555 except TypeError:
556 wcs = None
557 data = data.image
558
559 self._xy0 = data.getXY0() # DecoratedImage doesn't have getXY0()
560
561 if isinstance(data, afwImage.Image): # it's an Image; display it
562 self._impl._mtv(data, None, wcs, title)
563 # it's a Mask; display it, bitplane by bitplane
564 elif isinstance(data, afwImage.Mask):
565 self.__addMissingMaskPlanes(data)
566 #
567 # Some displays can't display a Mask without an image; so display an Image too,
568 # with pixel values set to the mask
569 #
570 self._impl._mtv(afwImage.ImageI(data.getArray()), data, wcs, title)
571 # it's a MaskedImage; display Image and overlay Mask
572 elif isinstance(data, afwImage.MaskedImage):
573 self.__addMissingMaskPlanes(data.mask)
574 self._impl._mtv(data.getImage(), data.getMask(), wcs, title)
575 else:
576 raise RuntimeError(f"Unsupported type {data!r}")
577 #
578 # Graphics commands
579 #
580
582 """A class intended to be used with python's with statement
583 """
584
585 def __init__(self, _impl):
586 self._impl = _impl
587
588 def __enter__(self):
589 self._impl._buffer(True)
590
591 def __exit__(self, *args):
592 self._impl._buffer(False)
593 self._impl._flush()
594
595 def Buffering(self):
596 """Return a class intended to be used with python's with statement
597
598 Examples
599 --------
600 .. code-block:: py
601
602 with display.Buffering():
603 display.dot("+", xc, yc)
604 """
605 return self._Buffering(self._impl)
606
607 def flush(self):
608 """Flush the buffers
609 """
610 self._impl._flush()
611
612 def erase(self):
613 """Erase the specified display frame
614 """
615 self._impl._erase()
616
617 def dot(self, symb, c, r, size=2, ctype=None, origin=afwImage.PARENT, *args, **kwargs):
618 """Draw a symbol onto the specified display frame
619
620 Parameters
621 ----------
622 symb
623 Possible values are:
624
625 ``"+"``
626 Draw a +
627 ``"x"``
628 Draw an x
629 ``"*"``
630 Draw a *
631 ``"o"``
632 Draw a circle
633 ``"@:Mxx,Mxy,Myy"``
634 Draw an ellipse with moments (Mxx, Mxy, Myy) (argument size is ignored)
636 Draw the ellipse (argument size is ignored). N.b. objects
637 derived from `~lsst.afw.geom.ellipses.BaseCore` include
639 Any other value
640 Interpreted as a string to be drawn.
641 c, r
642 The column and row where the symbol is drawn [0-based coordinates]
643 size : `int`
644 Size of symbol, in pixels
645 ctype : `str`
646 The desired color, either e.g. `lsst.afw.display.RED` or a color name known to X11
647 origin : `lsst.afw.image.ImageOrigin`
648 Coordinate system for the given positions.
649 *args
650 Extra arguments to backend
651 **kwargs
652 Extra keyword arguments to backend
653 """
654 if isinstance(symb, int):
655 symb = f"{symb:d}"
656
657 if origin == afwImage.PARENT and self._xy0 is not None:
658 x0, y0 = self._xy0
659 r -= y0
660 c -= x0
661
662 if isinstance(symb, afwGeom.ellipses.BaseCore) or re.search(r"^@:", symb):
663 try:
664 mat = re.search(r"^@:([^,]+),([^,]+),([^,]+)", symb)
665 except TypeError:
666 pass
667 else:
668 if mat:
669 mxx, mxy, myy = [float(_) for _ in mat.groups()]
670 symb = afwGeom.Quadrupole(mxx, myy, mxy)
671
672 symb = afwGeom.ellipses.Axes(symb)
673
674 self._impl._dot(symb, c, r, size, ctype, **kwargs)
675
676 def line(self, points, origin=afwImage.PARENT, symbs=False, ctype=None, size=0.5):
677 """Draw a set of symbols or connect points
678
679 Parameters
680 ----------
681 points : `list`
682 a list of (col, row)
683 origin : `lsst.afw.image.ImageOrigin`
684 Coordinate system for the given positions.
685 symbs : `bool` or sequence
686 If ``symbs`` is `True`, draw points at the specified points using the desired symbol,
687 otherwise connect the dots.
688
689 If ``symbs`` supports indexing (which includes a string -- caveat emptor) the
690 elements are used to label the points
691 ctype : `str`
692 ``ctype`` is the name of a color (e.g. 'red')
693 size : `float`
694 """
695 if symbs:
696 try:
697 symbs[1]
698 except TypeError:
699 symbs = len(points)*list(symbs)
700
701 for i, xy in enumerate(points):
702 self.dot(symbs[i], *xy, size=size, ctype=ctype)
703 else:
704 if len(points) > 0:
705 if origin == afwImage.PARENT and self._xy0 is not None:
706 x0, y0 = self._xy0
707 _points = list(points) # make a mutable copy
708 for i, p in enumerate(points):
709 _points[i] = (p[0] - x0, p[1] - y0)
710 points = _points
711
712 self._impl._drawLines(points, ctype)
713 #
714 # Set gray scale
715 #
716
717 def scale(self, algorithm, min, max=None, unit=None, *args, **kwargs):
718 """Set the range of the scaling from DN in the image to the image display
719
720 Parameters
721 ----------
722 algorithm : `str`
723 Desired scaling (e.g. "linear" or "asinh")
724 min
725 Minimum value, or "minmax" or "zscale"
726 max
727 Maximum value (must be `None` for minmax|zscale)
728 unit
729 Units for min and max (e.g. Percent, Absolute, Sigma; `None` if min==minmax|zscale)
730 *args
731 Optional arguments to the backend
732 **kwargs
733 Optional keyword arguments to the backend
734 """
735 if min in ("minmax", "zscale"):
736 assert max is None, f"You may not specify \"{min}\" and max"
737 assert unit is None, f"You may not specify \"{min}\" and unit"
738 elif max is None:
739 raise RuntimeError("Please specify max")
740
741 self._impl._scale(algorithm, min, max, unit, *args, **kwargs)
742 #
743 # Zoom and Pan
744 #
745
746 def zoom(self, zoomfac=None, colc=None, rowc=None, origin=afwImage.PARENT):
747 """Zoom frame by specified amount, optionally panning also
748 """
749
750 if (rowc and colc is None) or (colc and rowc is None):
751 raise RuntimeError(
752 "Please specify row and column center to pan about")
753
754 if rowc is not None:
755 if origin == afwImage.PARENT and self._xy0 is not None:
756 x0, y0 = self._xy0
757 colc -= x0
758 rowc -= y0
759
760 self._impl._pan(colc, rowc)
761
762 if zoomfac is None and rowc is None:
763 zoomfac = 2
764
765 if zoomfac is not None:
766 self._impl._zoom(zoomfac)
767
768 def pan(self, colc=None, rowc=None, origin=afwImage.PARENT):
769 """Pan to a location
770
771 Parameters
772 ----------
773 colc, rowc
774 the coordinates to pan to
775 origin : `lsst.afw.image.ImageOrigin`
776 Coordinate system for the given positions.
777
778 See also
779 --------
780 Display.zoom
781 """
782
783 self.zoom(None, colc, rowc, origin)
784
785 def interact(self):
786 """Enter an interactive loop, listening for key presses in display and firing callbacks.
787
788 Exit with ``q``, ``CR``, ``ESC``, or any other callback function that returns a `True` value.
789 """
790 interactFinished = False
791
792 while not interactFinished:
793 ev = self._impl._getEvent()
794 if not ev:
795 continue
796 k, x, y = ev.k, ev.x, ev.y # for now
797
798 if k not in self._callbacks:
799 logger.warn("No callback registered for {0}".format(k))
800 else:
801 try:
802 interactFinished = self._callbacks[k](k, x, y)
803 except Exception as e:
804 logger.error(
805 "Display._callbacks['{0}']({0},{1},{2}) failed: {3}".format(k, x, y, e))
806
807 def setCallback(self, k, func=None, noRaise=False):
808 """Set the callback for a key
809
810 Parameters
811 ----------
812 k
813 The key to assign the callback to
814 func : callable
815 The callback assigned to ``k``
816 noRaise : `bool`
817
818 Returns
819 -------
820 oldFunc : callable
821 The callback previously assigned to ``k``.
822 """
823
824 if k in "f":
825 if noRaise:
826 return
827 raise RuntimeError(
828 f"Key '{k}' is already in use by display, so I can't add a callback for it")
829
830 ofunc = self._callbacks.get(k)
831 self._callbacks[k] = func if func else noop_callback
832
833 self._impl._setCallback(k, self._callbacks[k])
834
835 return ofunc
836
837 def getActiveCallbackKeys(self, onlyActive=True):
838 """Return all callback keys
839
840 Parameters
841 ----------
842 onlyActive : `bool`
843 If `True` only return keys that do something
844 """
845
846 return sorted([k for k, func in self._callbacks.items() if
847 not (onlyActive and func == noop_callback)])
848
849#
850# Callbacks for display events
851#
852
853
854class Event:
855 """A class to handle events such as key presses in image display windows
856 """
857
858 def __init__(self, k, x=float('nan'), y=float('nan')):
859 self.k = k
860 self.x = x
861 self.y = y
862
863 def __str__(self):
864 return f"{self.k} ({self.x:.2f}, {self.y:.2f}"
865#
866# Default fallback function
867#
868
869
870def noop_callback(k, x, y):
871 """Callback function
872
873 Parameters
874 ----------
875 key
876 x
877 y
878 """
879 return False
880
881
882def h_callback(k, x, y):
883 print("Enter q or <ESC> to leave interactive mode, h for this help, or a letter to fire a callback")
884 return False
885
886#
887# Handle Displays, including the default one (the frame to use when a user specifies None)
888#
889# If the default frame is None, image display is disabled
890#
891
892
893def setDefaultBackend(backend):
894 Display.setDefaultBackend(backend)
895
896
898 return Display.getDefaultBackend()
899
900
901def setDefaultFrame(frame=0):
902 return Display.setDefaultFrame(frame)
903
904
906 """Get the default frame for display
907 """
908 return Display.getDefaultFrame()
909
910
912 """Increment the default frame for display
913 """
914 return Display.incrDefaultFrame()
915
916
917def setDefaultMaskTransparency(maskPlaneTransparency={}):
918 return Display.setDefaultMaskTransparency(maskPlaneTransparency)
919
920
921def setDefaultMaskPlaneColor(name=None, color=None):
922 """Set the default mapping from mask plane names to colors
923
924 Parameters
925 ----------
926 name : `str` or `dict`
927 name of mask plane, or a dict mapping names to colors.
928 If ``name`` is `None`, use the hard-coded default dictionary
929 color : `str`
930 Desired color, or `None` if ``name`` is a dict
931 """
932
933 return Display.setDefaultMaskPlaneColor(name, color)
934
935
936def getDisplay(frame=None, backend=None, create=True, verbose=False, *args, **kwargs):
937 """Return a specific `Display`, creating it if need be
938
939 Parameters
940 ----------
941 frame
942 The desired frame (`None` => use defaultFrame (see `setDefaultFrame`))
943 backend : `str`
944 Create the specified frame using this backend (or the default if
945 `None`) if it doesn't already exist. If ``backend == ""``, it's an
946 error to specify a non-existent ``frame``.
947 create : `bool`
948 Create the display if it doesn't already exist.
949 verbose : `bool`
950 Allow backend to be chatty
951 *args
952 arguments passed to `Display` constructor
953 **kwargs
954 keyword arguments passed to `Display` constructor
955
956 See also
957 --------
958 Display.getDisplay
959 """
960
961 return Display.getDisplay(frame, backend, create, verbose, *args, **kwargs)
962
963
965 """Delete and close all known displays
966 """
967 return Display.delAllDisplays()
std::vector< SchemaItem< Flag > > * items
int max
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:617
def zoom(self, zoomfac=None, colc=None, rowc=None, origin=afwImage.PARENT)
Definition: interface.py:746
def getActiveCallbackKeys(self, onlyActive=True)
Definition: interface.py:837
def line(self, points, origin=afwImage.PARENT, symbs=False, ctype=None, size=0.5)
Definition: interface.py:676
def setCallback(self, k, func=None, noRaise=False)
Definition: interface.py:807
def mtv(self, data, title="", wcs=None)
Definition: interface.py:533
def __addMissingMaskPlanes(self, mask)
Definition: interface.py:517
def setMaskTransparency(self, transparency=None, name=None)
Definition: interface.py:481
def getMaskPlaneColor(self, name=None)
Definition: interface.py:462
def pan(self, colc=None, rowc=None, origin=afwImage.PARENT)
Definition: interface.py:768
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:717
def __init__(self, frame=None, backend=None, *args, **kwargs)
Definition: interface.py:152
def getMaskTransparency(self, name=None)
Definition: interface.py:502
def __init__(self, k, x=float('nan'), y=float('nan'))
Definition: interface.py:858
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:74
static Log getLogger(Log const &logger)
Definition: Log.h:772
daf::base::PropertyList * list
Definition: fits.cc:928
def noop_callback(k, x, y)
Definition: interface.py:870
def setDefaultMaskTransparency(maskPlaneTransparency={})
Definition: interface.py:917
def setDefaultFrame(frame=0)
Definition: interface.py:901
def setDefaultMaskPlaneColor(name=None, color=None)
Definition: interface.py:921
def setDefaultBackend(backend)
Definition: interface.py:893
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
Definition: SkyWcs.cc:521
Definition: Log.h:717