1 from __future__
import print_function
2 from builtins
import range
3 from builtins
import object
40 "WHITE",
"BLACK",
"RED",
"GREEN",
"BLUE",
"CYAN",
"MAGENTA",
"YELLOW",
"ORANGE",
41 "Display",
"Event",
"noop_callback",
"h_callback",
42 "setDefaultBackend",
"getDefaultBackend",
43 "setDefaultFrame",
"getDefaultFrame",
"incrDefaultFrame",
44 "setDefaultMaskTransparency",
"setDefaultMaskPlaneColor",
45 "getDisplay",
"delAllDisplays",
64 """!Return the DisplayImpl for the named backend
66 \param backend Name of device. Should be importable, either absolutely or relative to lsst.display
67 \param frame Identifier for this instance of the backend
68 \param args Arguments passed to DisplayImpl.__init__
69 \param kwrgs Keywords arguments passed to DisplayImpl.__init__
72 import lsst.afw.display as afwDisplay
73 display = afwDisplay.Display("ds9", frame=1)
75 _makeDisplayImpl(..., "ds9", 1)
76 and import the ds9 implementation of DisplayImpl from lsst.display.ds9
80 for dt
in (backend,
".%s" % backend,
"lsst.afw.display.%s" % backend):
84 if dt.startswith(
"."):
85 impargs[
"package"] =
"lsst.display"
87 _disp = importlib.import_module(dt, **impargs)
89 except (ImportError, SystemError)
as e:
100 raise ImportError(
"Could not load the requested backend: {}".
format(backend))
102 return _disp.DisplayImpl(display, *args, **kwargs)
108 _defaultBackend =
None
110 _defaultMaskPlaneColor = dict(
117 DETECTED_NEGATIVE=CYAN,
124 _defaultMaskTransparency = {}
126 def __init__(self, frame, backend=None, *args, **kwargs):
127 """!Create an object able to display images and overplot glyphs
129 \param frame An identifier for the display
130 \param backend The backend to use (defaults to value set by setDefaultBackend())
131 \param args Arguments to pass to the backend
132 \param kwargs Arguments to pass to the backend
135 if Display._defaultBackend
is None:
141 backend = Display._defaultBackend
153 for ik
in range(ord(
'a'), ord(
'z') + 1):
158 for k
in (
'Return',
'Shift_L',
'Shift_R'):
161 for k
in (
'q',
'Escape'):
164 def _h_callback(k, x, y):
167 for k
in sorted(self._callbacks.keys()):
169 print(
" %-6s %s" % (k, doc.split(
"\n")[0]
if doc
else "???"))
174 """!Support for python's with statement"""
178 """!Support for python's with statement"""
185 if hasattr(self,
"_impl")
and self.
_impl:
189 if self.
frame in Display._displays:
190 del Display._displays[self.
frame]
194 """!The backend's verbosity"""
195 return self._impl.verbose
200 self._impl.verbose = value
203 return "Display[%s]" % (self.
frame)
212 except Exception
as e:
213 raise RuntimeError(
"Unable to set backend to %s: \"%s\"" % (backend, e))
215 Display._defaultBackend = backend
219 return Display._defaultBackend
223 """Set the default frame for display"""
224 Display._defaultFrame = frame
228 """Get the default frame for display"""
229 return Display._defaultFrame
233 """Increment the default frame for display"""
234 Display._defaultFrame += 1
235 return Display._defaultFrame
239 if hasattr(maskPlaneTransparency,
"copy"):
240 maskPlaneTransparency = maskPlaneTransparency.copy()
242 Display._defaultMaskTransparency = maskPlaneTransparency
246 """!Set the default mapping from mask plane names to colours
247 \param name name of mask plane, or a dict mapping names to colours
248 \param color Desired color, or None if name is a dict
250 If name is None, use the hard-coded default dictionary
254 name = Display._defaultMaskPlaneColor
256 if isinstance(name, dict):
258 for k, v
in name.items():
264 Display._defaultMaskPlaneColor[name] = color
267 def getDisplay(frame=None, backend=None, create=True, verbose=False, *args, **kwargs):
268 """!Return the Display indexed by frame, creating it if needs be
270 \param frame The desired frame (None => use defaultFrame (see setDefaultFrame))
271 \param backend create the specified frame using this backend (or the default if None) \
272 if it doesn't already exist. If backend == "", it's an error to specify a non-existent frame
273 \param create create the display if it doesn't already exist.
274 \param verbose Allow backend to be chatty
275 \param args arguments passed to Display constructor
276 \param kwargs keyword arguments passed to Display constructor
280 frame = Display._defaultFrame
282 if not frame
in Display._displays:
284 raise RuntimeError(
"Frame %s does not exist" % frame)
286 Display._displays[frame] =
Display(frame, backend, verbose=verbose, *args, **kwargs)
288 Display._displays[frame].verbose = verbose
289 return Display._displays[frame]
293 """!Delete and close all known display
295 for disp
in list(Display._displays.values()):
297 Display._displays = {}
300 """!A generator for "standard" colours
302 \param omitBW Don't include Black and White
305 colorGenerator = interface.maskColorGenerator(omitBW=True)
307 print p, next(colorGenerator)
309 _maskColors = [WHITE, BLACK, RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW, ORANGE]
314 color = _maskColors[i%len(_maskColors)]
315 if omitBW
and color
in (BLACK, WHITE):
321 """!Request that mask plane name be displayed as color
323 \param name Name of mask plane or a dictionary of name -> colourName
324 \param color The name of the colour to use (must be None if name is a dict)
326 Colours may be specified as any X11-compliant string (e.g. <tt>"orchid"</tt>), or by one
327 of the following constants defined in \c afwDisplay: \c BLACK, \c WHITE, \c RED, \c BLUE,
328 \c GREEN, \c CYAN, \c MAGENTA, \c YELLOW.
330 The advantage of using the symbolic names is that the python interpreter can detect typos.
334 if isinstance(name, dict):
336 for k, v
in name.items():
343 """!Return the colour associated with the specified mask plane name"""
345 return self._maskPlaneColors.get(name)
348 """!Specify display's mask transparency (percent); or None to not set it when loading masks"""
350 if isinstance(transparency, dict):
352 for k, v
in transparency.items():
356 if transparency
is not None and (transparency < 0
or transparency > 100):
357 print(
"Mask transparency should be in the range [0, 100]; clipping", file=sys.stderr)
363 if transparency
is not None:
364 self._impl._setMaskTransparency(transparency, name)
367 """!Return the current display's mask transparency"""
369 self._impl._getMaskTransparency(name)
372 """!Uniconify and Raise display. N.b. throws an exception if frame doesn't exit"""
375 def mtv(self, data, title="", wcs=None):
376 """!Display an Image or Mask on a DISPLAY display
378 Historical note: the name "mtv" comes from Jim Gunn's forth imageprocessing
379 system, Mirella (named after Mirella Freni); The "m" stands for Mirella.
381 if hasattr(data,
"getXY0"):
382 self.
_xy0 = data.getXY0()
386 if re.search(
"::Exposure<", repr(data)):
388 raise RuntimeError(
"You may not specify a wcs with an Exposure")
389 data, wcs = data.getMaskedImage(), data.getWcs()
390 elif re.search(
"::DecoratedImage<", repr(data)):
392 self.
_xy0 = data.getXY0()
394 if re.search(
"::Image<", repr(data)):
395 self._impl._mtv(data,
None, wcs, title)
396 elif re.search(
"::Mask<", repr(data)):
401 self._impl._mtv(afwImage.ImageU(data.getArray()), data, wcs, title)
402 elif re.search(
"::MaskedImage<", repr(data)):
403 self._impl._mtv(data.getImage(), data.getMask(
True), wcs, title)
405 raise RuntimeError(
"Unsupported type %s" % repr(data))
410 """A class intended to be used with python's with statement"""
414 self._impl._buffer(
True)
416 self._impl._buffer(
False)
420 """Return a class intended to be used with python's with statement
422 with display.Buffering():
423 display.dot("+", xc, yc)
428 """!Flush the buffers"""
432 """!Erase the specified DISPLAY frame
436 def dot(self, symb, c, r, size=2, ctype=None, origin=afwImage.PARENT, *args, **kwargs):
437 """!Draw a symbol onto the specified DISPLAY frame at (col,row) = (c,r) [0-based coordinates]
444 @:Mxx,Mxy,Myy Draw an ellipse with moments (Mxx, Mxy, Myy) (argument size is ignored)
445 An object derived from afwGeom.ellipses.BaseCore Draw the ellipse (argument size is ignored)
446 Any other value is interpreted as a string to be drawn. Strings obey the fontFamily (which may be extended
447 with other characteristics, e.g. "times bold italic". Text will be drawn rotated by textAngle (textAngle is
450 N.b. objects derived from BaseCore include Axes and Quadrupole.
452 if isinstance(symb, int):
455 if origin == afwImage.PARENT
and self.
_xy0 is not None:
460 if isinstance(symb, afwGeom.ellipses.BaseCore)
or re.search(
r"^@:", symb):
462 mat = re.search(
r"^@:([^,]+),([^,]+),([^,]+)", symb)
467 mxx, mxy, myy = [float(_)
for _
in mat.groups()]
468 symb = afwGeom.ellipses.Quadrupole(mxx, myy, mxy)
470 symb = afwGeom.ellipses.Axes(symb)
472 self._impl._dot(symb, c, r, size, ctype, **kwargs)
474 def line(self, points, origin=afwImage.PARENT, symbs=False, ctype=None, size=0.5):
475 """!Draw a set of symbols or connect the points, a list of (col,row)
476 If symbs is True, draw points at the specified points using the desired symbol,
477 otherwise connect the dots. Ctype is the name of a colour (e.g. 'red')
479 If symbs supports indexing (which includes a string -- caveat emptor) the elements are used to label the points
485 symbs = len(points)*list(symbs)
487 for i, xy
in enumerate(points):
488 self.
dot(symbs[i], *xy, size=size, ctype=ctype)
491 if origin == afwImage.PARENT
and self.
_xy0 is not None:
493 _points = list(points)
494 for i, p
in enumerate(points):
495 _points[i] = (p[0] - x0, p[1] - y0)
498 self._impl._drawLines(points, ctype)
502 def scale(self, algorithm, min, max=None, unit=None, *args, **kwargs):
503 """!Set the range of the scaling from DN in the image to the image display
504 \param algorithm Desired scaling (e.g. "linear" or "asinh")
505 \param min Minimum value, or "minmax" or "zscale"
506 \param max Maximum value (must be None for minmax|zscale)
507 \param unit Units for min and max (e.g. Percent, Absolute, Sigma; None if min==minmax|zscale)
508 \param *args Optional arguments
509 \param **kwargs Optional keyword arguments
511 if min
in (
"minmax",
"zscale"):
512 assert max ==
None,
"You may not specify \"%s\" and max" % min
513 assert unit ==
None,
"You may not specify \"%s\" and unit" % min
515 raise RuntimeError(
"Please specify max")
517 self._impl._scale(algorithm, min, max, unit, *args, **kwargs)
521 def zoom(self, zoomfac=None, colc=None, rowc=None, origin=afwImage.PARENT):
522 """!Zoom frame by specified amount, optionally panning also"""
524 if (rowc
and colc
is None)
or (colc
and rowc
is None):
525 raise RuntimeError(
"Please specify row and column center to pan about")
528 if origin == afwImage.PARENT
and self.
_xy0 is not None:
533 self._impl._pan(colc, rowc)
535 if zoomfac ==
None and rowc ==
None:
538 if zoomfac
is not None:
539 self._impl._zoom(zoomfac)
541 def pan(self, colc=None, rowc=None, origin=afwImage.PARENT):
542 """!Pan to (rowc, colc); see also zoom"""
544 self.
zoom(
None, colc, rowc, origin)
547 """!Enter an interactive loop, listening for key presses in display and firing callbacks.
548 Exit with q, \c CR, \c ESC, or any other callback function that returns a ``True`` value.
550 interactFinished =
False
552 while not interactFinished:
553 ev = self._impl._getEvent()
556 k, x, y = ev.k, ev.x, ev.y
559 logger.warn(
"No callback registered for {0}".
format(k))
562 interactFinished = self.
_callbacks[k](k, x, y)
563 except Exception
as e:
564 logger.error(
"Display._callbacks[{0}]({0},{1},{2}) failed: {3}".
format(k, x, y, e))
567 """!Set the callback for key k to be func, returning the old callback
574 "Key '%s' is already in use by display, so I can't add a callback for it" % k)
576 ofunc = self._callbacks.get(k)
577 self.
_callbacks[k] = func
if func
else noop_callback
579 self._impl._setCallback(k, self.
_callbacks[k])
584 """!Return all callback keys
585 \param onlyActive If true only return keys that do something
588 return sorted([k
for k, func
in self._callbacks.items()
if
589 not (onlyActive
and func == noop_callback)])
596 """!A class to handle events such as key presses in image display windows"""
597 def __init__(self, k, x=float(
'nan'), y=float(
'nan')):
603 return "%s (%.2f, %.2f)" % (self.
k, self.
x, self.
y)
608 """!Callback function: arguments key, x, y"""
612 print(
"Enter q or <ESC> to leave interactive mode, h for this help, or a letter to fire a callback")
622 Display.setDefaultBackend(backend)
625 return Display.getDefaultBackend()
628 return Display.setDefaultFrame(frame)
631 """Get the default frame for display"""
632 return Display.getDefaultFrame()
635 """Increment the default frame for display"""
636 return Display.incrDefaultFrame()
639 return Display.setDefaultMaskTransparency(maskPlaneTransparency)
642 """!Set the default mapping from mask plane names to colours
643 \param name name of mask plane, or a dict mapping names to colours
644 \param color Desired color, or None if name is a dict
646 If name is None, use the hard-coded default dictionary
649 return Display.setDefaultMaskPlaneColor(name, color)
651 def getDisplay(frame=None, backend=None, create=True, verbose=False, *args, **kwargs):
652 """!Return the Display indexed by frame, creating it if needs be
654 See Display.getDisplay
656 \param frame The desired frame (None => use defaultFrame (see setDefaultFrame))
657 \param backend create the specified frame using this backend (or the default if None) \
658 if it doesn't already exist. If backend == "", it's an error to specify a non-existent frame
659 \param create create the display if it doesn't already exist.
660 \param verbose Allow backend to be chatty
661 \param args arguments passed to Display constructor
662 \param kwargs keyword arguments passed to Display constructor
665 return Display.getDisplay(frame, backend, create, verbose, *args, **kwargs)
668 """!Delete and close all known display
670 return Display.delAllDisplays()
def getMaskTransparency
Return the current display's mask transparency.
def getDisplay
Return the Display indexed by frame, creating it if needs be.
def setMaskTransparency
Specify display's mask transparency (percent); or None to not set it when loading masks...
def interact
Enter an interactive loop, listening for key presses in display and firing callbacks.
def setMaskPlaneColor
Request that mask plane name be displayed as color.
def zoom
Zoom frame by specified amount, optionally panning also.
def setDefaultMaskTransparency
def delAllDisplays
Delete and close all known display.
def mtv
Display an Image or Mask on a DISPLAY display.
def __enter__
Support for python's with statement.
def pan
Pan to (rowc, colc); see also zoom.
def verbose
The backend's verbosity.
def __init__
Create an object able to display images and overplot glyphs.
def getMaskPlaneColor
Return the colour associated with the specified mask plane name.
A class to handle events such as key presses in image display windows.
def setDefaultMaskTransparency
def dot
Draw a symbol onto the specified DISPLAY frame at (col,row) = (c,r) [0-based coordinates].
def _makeDisplayImpl
Return the DisplayImpl for the named backend.
boost::shared_ptr< Wcs > makeWcs(coord::Coord const &crval, geom::Point2D const &crpix, double CD11, double CD12, double CD21, double CD22)
Create a Wcs object from crval, crpix, CD, using CD elements (useful from python) ...
def scale
Set the range of the scaling from DN in the image to the image display.
def delAllDisplays
Delete and close all known display.
def show
Uniconify and Raise display.
def line
Draw a set of symbols or connect the points, a list of (col,row) If symbs is True, draw points at the specified points using the desired symbol, otherwise connect the dots.
def setCallback
Set the callback for key k to be func, returning the old callback.
def erase
Erase the specified DISPLAY frame.
static Log getLogger(Log const &logger)
def getDisplay
Return the Display indexed by frame, creating it if needs be.
def flush
Flush the buffers.
def __exit__
Support for python's with statement.
def getActiveCallbackKeys
Return all callback keys.
def noop_callback
Callback function: arguments key, x, y.
def maskColorGenerator
A generator for "standard" colours.
def setDefaultMaskPlaneColor
Set the default mapping from mask plane names to colours.
def setDefaultMaskPlaneColor
Set the default mapping from mask plane names to colours.