LSST Applications  21.0.0+04719a4bac,21.0.0-1-ga51b5d4+f5e6047307,21.0.0-11-g2b59f77+a9c1acf22d,21.0.0-11-ga42c5b2+86977b0b17,21.0.0-12-gf4ce030+76814010d2,21.0.0-13-g1721dae+760e7a6536,21.0.0-13-g3a573fe+768d78a30a,21.0.0-15-g5a7caf0+f21cbc5713,21.0.0-16-g0fb55c1+b60e2d390c,21.0.0-19-g4cded4ca+71a93a33c0,21.0.0-2-g103fe59+bb20972958,21.0.0-2-g45278ab+04719a4bac,21.0.0-2-g5242d73+3ad5d60fb1,21.0.0-2-g7f82c8f+8babb168e8,21.0.0-2-g8f08a60+06509c8b61,21.0.0-2-g8faa9b5+616205b9df,21.0.0-2-ga326454+8babb168e8,21.0.0-2-gde069b7+5e4aea9c2f,21.0.0-2-gecfae73+1d3a86e577,21.0.0-2-gfc62afb+3ad5d60fb1,21.0.0-25-g1d57be3cd+e73869a214,21.0.0-3-g357aad2+ed88757d29,21.0.0-3-g4a4ce7f+3ad5d60fb1,21.0.0-3-g4be5c26+3ad5d60fb1,21.0.0-3-g65f322c+e0b24896a3,21.0.0-3-g7d9da8d+616205b9df,21.0.0-3-ge02ed75+a9c1acf22d,21.0.0-4-g591bb35+a9c1acf22d,21.0.0-4-g65b4814+b60e2d390c,21.0.0-4-gccdca77+0de219a2bc,21.0.0-4-ge8a399c+6c55c39e83,21.0.0-5-gd00fb1e+05fce91b99,21.0.0-6-gc675373+3ad5d60fb1,21.0.0-64-g1122c245+4fb2b8f86e,21.0.0-7-g04766d7+cd19d05db2,21.0.0-7-gdf92d54+04719a4bac,21.0.0-8-g5674e7b+d1bd76f71f,master-gac4afde19b+a9c1acf22d,w.2021.13
LSST Data Management Base Package
interface.py
Go to the documentation of this file.
1 
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 
32 import re
33 import sys
34 import importlib
35 import lsst.afw.geom as afwGeom
36 import lsst.afw.image as afwImage
37 import lsst.log
38 
39 logger = lsst.log.Log.getLogger("afw.display.interface")
40 
41 #
42 # Symbolic names for mask/line colors. N.b. ds9 supports any X11 color for masks
43 #
44 WHITE = "white"
45 BLACK = "black"
46 RED = "red"
47 GREEN = "green"
48 BLUE = "blue"
49 CYAN = "cyan"
50 MAGENTA = "magenta"
51 YELLOW = "yellow"
52 ORANGE = "orange"
53 IGNORE = "ignore"
54 
55 
56 def _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 
118 class Display:
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 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 
558  class _Buffering:
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)
612  `lsst.afw.geom.ellipses.BaseCore`
613  Draw the ellipse (argument size is ignored). N.b. objects
614  derived from `~lsst.afw.geom.ellipses.BaseCore` include
615  `~lsst.afw.geom.ellipses.Axes` and `~lsst.afw.geom.ellipses.Quadrupole`.
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 
831 class 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 
847 def noop_callback(k, x, y):
848  """Callback function
849 
850  Parameters
851  ----------
852  key
853  x
854  y
855  """
856  return False
857 
858 
859 def 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 
870 def setDefaultBackend(backend):
871  Display.setDefaultBackend(backend)
872 
873 
875  return Display.getDefaultBackend()
876 
877 
878 def 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 
894 def setDefaultMaskTransparency(maskPlaneTransparency={}):
895  return Display.setDefaultMaskTransparency(maskPlaneTransparency)
896 
897 
898 def 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 
913 def 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 with quadrupole moments as parameters.
Definition: Quadrupole.h:47
A container for an Image and its associated metadata.
Definition: Image.h:404
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:58
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:760
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:526
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Definition: Log.h:706
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174