22 __all__ = [
"Ds9Error",
"getXpaAccessPoint",
"ds9Version",
"Buffer",
23 "selectFrame",
"ds9Cmd",
"initDS9",
"Ds9Event",
"DisplayImpl"]
37 from .
import xpa
as xpa
38 except ImportError
as e:
39 print(
"Cannot import xpa: %s" % (e), file=sys.stderr)
51 """Represents an error communicating with DS9. 58 _maskTransparency =
None 62 """Parse XPA_PORT if set and return an identifier to send DS9 commands. 67 xpaAccessPoint : `str` 68 Either a reference to the local host with the configured port, or the 73 If you don't have XPA_PORT set, the usual xpans tricks will be played 74 when we return ``"ds9"``. 76 xpa_port = os.environ.get(
"XPA_PORT")
78 mat = re.search(
r"^DS9:ds9\s+(\d+)\s+(\d+)", xpa_port)
80 port1, port2 = mat.groups()
82 return "127.0.0.1:%s" % (port1)
84 print(
"Failed to parse XPA_PORT=%s" % xpa_port, file=sys.stderr)
90 """Get the version of DS9 in use. 95 Version of DS9 in use. 98 v =
ds9Cmd(
"about", get=
True)
99 return v.splitlines()[1].split()[1]
100 except Exception
as e:
101 print(
"Error reading version: %s" % e, file=sys.stderr)
109 XPA_SZ_LINE = 4096 - 100
112 """Buffer to control sending commands to DS9. 116 The usual usage pattern is: 118 >>> with ds9.Buffering(): 119 ... # bunches of ds9.{dot,line} commands 121 ... # bunches more ds9.{dot,line} commands 131 def set(self, size, silent=True):
132 """Set the ds9 buffer size to size. 137 Size of buffer. Requesting a negative size provides the 138 largest possible buffer given bugs in xpa. 139 silent : `bool`, optional 140 Do not print error messages (default `True`). 143 size = XPA_SZ_LINE - 5
145 if size > XPA_SZ_LINE:
146 print(
"xpa silently hardcodes a limit of %d for buffer sizes (you asked for %d) " %
147 (XPA_SZ_LINE, size), file=sys.stderr)
156 self.
flush(silent=silent)
159 """Get the current DS9 buffer size. 169 """Replace current DS9 command buffer size. 173 size : `int`, optional 174 Size of buffer. A negative value sets the largest possible 181 self.
flush(silent=
True)
183 self.
set(size, silent=
True)
186 """Switch back to the previous command buffer size. 192 self.
flush(silent=
True)
198 """Flush the pending commands. 202 silent : `bool`, optional 203 Do not print error messages. 205 ds9Cmd(flush=
True, silent=silent)
211 """Convert integer frame number to DS9 command syntax. 222 return "frame %d" % (frame)
225 def ds9Cmd(cmd=None, trap=True, flush=False, silent=True, frame=None, get=False):
226 """Issue a DS9 command, raising errors as appropriate. 230 cmd : `str`, optional 232 trap : `bool`, optional 234 flush : `bool`, optional 236 silent : `bool`, optional 237 Do not print trapped error messages. 238 frame : `int`, optional 239 Frame number on which to execute command. 240 get : `bool`, optional 246 if frame
is not None:
254 if cmdBuffer._lenCommands + len(cmd) > XPA_SZ_LINE - 5:
255 ds9Cmd(flush=
True, silent=silent)
257 cmdBuffer._commands +=
";" + cmd
258 cmdBuffer._lenCommands += 1 + len(cmd)
260 if flush
or cmdBuffer._lenCommands >= cmdBuffer._getSize():
261 cmd = (cmdBuffer._commands +
"\n")
262 cmdBuffer._commands =
"" 263 cmdBuffer._lenCommands = 0
277 raise Ds9Error(
"XPA: %s, (%s)" % (e, cmd))
279 print(
"Caught ds9 exception processing command \"%s\": %s" % (cmd, e), file=sys.stderr)
287 execDs9 : `bool`, optional 288 If DS9 is not running, attempt to execute it. 292 ds9Cmd(
"iconify no; raise",
False)
300 needShow = (int(v1) <= 4)
303 except Ds9Error
as e:
304 if not re.search(
'xpa', os.environ[
'PATH']):
305 raise Ds9Error(
'You need the xpa binaries in your path to use ds9 with python')
310 import distutils.spawn
311 if not distutils.spawn.find_executable(
"ds9"):
312 raise NameError(
"ds9 doesn't appear to be on your path")
313 if "DISPLAY" not in os.environ:
314 raise RuntimeError(
"$DISPLAY isn't set, so I won't be able to start ds9 for you")
316 print(
"ds9 doesn't appear to be running (%s), I'll try to exec it for you" % e)
324 print(
"waiting for ds9...\r", end=
"")
337 """An event generated by a mouse or key click on the display. 341 interface.Event.__init__(self, k, x, y)
345 """Virtual device display implementation. 348 def __init__(self, display, verbose=False, *args, **kwargs):
349 virtualDevice.DisplayImpl.__init__(self, display, verbose)
352 """Called when the device is closed. 356 def _setMaskTransparency(self, transparency, maskplane):
357 """Specify DS9's mask transparency. 362 Percent transparency. 363 maskplane : `NoneType` 364 If `None`, transparency is enabled. Otherwise, this parameter is 367 if maskplane
is not None:
368 print(
"ds9 is unable to set transparency for individual maskplanes" % maskplane,
371 ds9Cmd(
"mask transparency %d" % transparency, frame=self.display.frame)
373 def _getMaskTransparency(self, maskplane):
374 """Return the current DS9's mask transparency. 379 This parameter does nothing. 382 return float(
ds9Cmd(
"mask transparency", get=
True))
385 """Uniconify and raise DS9. 389 Raises if ``self.display.frame`` doesn't exist. 391 ds9Cmd(
"raise", trap=
False, frame=self.display.frame)
393 def _mtv(self, image, mask=None, wcs=None, title=""):
394 """Display an Image and/or Mask on a DS9 display. 398 image : subclass of `lsst.afw.image.Image` 400 mask : subclass of `lsst.afw.image.Mask`, optional 402 wcs : `lsst.afw.geom.SkyWcs`, optional 404 title : `str`, optional 412 print(
"waiting for ds9...\r", end=
"")
426 _i_mtv(image, wcs, title,
False)
429 maskPlanes = mask.getMaskPlaneDict()
430 nMaskPlanes =
max(maskPlanes.values()) + 1
433 for key
in maskPlanes:
434 planes[maskPlanes[key]] = key
436 planeList = range(nMaskPlanes)
438 mask1 = mask.Factory(mask.getDimensions())
440 colorGenerator = self.display.maskColorGenerator(omitBW=
True)
445 if not ((1 << p) & usedPlanes):
454 color = next(colorGenerator)
455 elif color.lower() ==
"ignore":
458 ds9Cmd(
"mask color %s" % color)
459 _i_mtv(mask1, wcs, title,
True)
464 def _buffer(self, enable=True):
465 """Push and pop buffer size. 469 enable : `bool`, optional 470 If `True` (default), push size; else pop it. 483 """Erase all regions in current frame. 485 ds9Cmd(
"regions delete all", flush=
True, frame=self.display.frame)
487 def _dot(self, symb, c, r, size, ctype, fontFamily="helvetica", textAngle=None):
488 """Draw a symbol onto the specified DS9 frame. 492 symb : `str`, or subclass of `lsst.afw.geom.ellipses.BaseCore` 493 Symbol to be drawn. Possible values are: 495 - ``"+"``: Draw a "+" 496 - ``"x"``: Draw an "x" 497 - ``"*"``: Draw a "*" 498 - ``"o"``: Draw a circle 499 - ``"@:Mxx,Mxy,Myy"``: Draw an ellipse with moments (Mxx, Mxy, 500 Myy);(the ``size`` parameter is ignored) 501 - An object derived from `lsst.afw.geom.ellipses.BaseCore`: Draw 502 the ellipse (argument size is ignored) 504 Any other value is interpreted as a string to be drawn. 506 Column to draw symbol [0-based coordinates]. 508 Row to draw symbol [0-based coordinates]. 512 the name of a colour (e.g. ``"red"``) 513 fontFamily : `str`, optional 514 String font. May be extended with other characteristics, 515 e.g. ``"times bold italic"``. 516 textAngle: `float`, optional 517 Text will be drawn rotated by ``textAngle``. 521 Objects derived from `lsst.afw.geom.ellipses.BaseCore` include 522 `~lsst.afw.geom.ellipses.Axes` and `lsst.afw.geom.ellipses.Quadrupole`. 525 for region
in ds9Regions.dot(symb, c, r, size, ctype, fontFamily, textAngle):
526 cmd +=
'regions command {%s}; ' % region
530 def _drawLines(self, points, ctype):
531 """Connect the points. 535 points : `list` of (`int`, `int`) 536 A list of points specified as (col, row). 538 The name of a colour (e.g. ``"red"``). 541 for region
in ds9Regions.drawLines(points, ctype):
542 cmd +=
'regions command {%s}; ' % region
546 def _scale(self, algorithm, min, max, unit, *args, **kwargs):
547 """Set image color scale. 551 algorithm : {``"linear"``, ``"log"``, ``"pow"``, ``"sqrt"``, ``"squared"``, ``"asinh"``, ``"sinh"``, ``"histequ"``} # noqa: E501 552 Scaling algorithm. May be any value supported by DS9. 554 Minimum value for scale. 556 Maximum value for scale. 565 ds9Cmd(
"scale %s" % algorithm, frame=self.display.frame)
567 if min
in (
"minmax",
"zscale"):
568 ds9Cmd(
"scale mode %s" % (min))
571 print(
"ds9: ignoring scale unit %s" % unit)
573 ds9Cmd(
"scale limits %g %g" % (min, max), frame=self.display.frame)
578 def _zoom(self, zoomfac):
579 """Zoom frame by specified amount. 587 cmd +=
"zoom to %d; " % zoomfac
591 def _pan(self, colc, rowc):
597 Physical column to which to pan. 599 Physical row to which to pan. 603 cmd +=
"pan to %g %g physical; " % (colc + 1, rowc + 1)
608 """Listen for a key press on a frame in DS9 and return an event. 613 Event with (key, x, y). 615 vals =
ds9Cmd(
"imexam key coordinate", get=
True).split()
616 if vals[0] ==
"XPA$ERROR":
617 if vals[1:4] == [
'unknown',
'option',
'"-state"']:
620 print(
"Error return from imexam:",
" ".join(vals), file=sys.stderr)
638 haveGzip =
not os.system(
"gzip < /dev/null > /dev/null 2>&1")
641 def _i_mtv(data, wcs, title, isMask):
642 """Internal routine to display an image or a mask on a DS9 display. 646 data : Subclass of `lsst.afw.image.Image` or `lsst.afw.image.Mask` 648 wcs : `lsst.afw.geom.SkyWcs` 655 title = str(title)
if title
else "" 663 if data.getArray().dtype == np.uint16:
669 xpa_cmd =
"gzip | " + xpa_cmd
671 pfd = os.popen(xpa_cmd,
"w")
673 pfd = open(
"foo.fits",
"w")
675 ds9Cmd(flush=
True, silent=
True)
678 displayLib.writeFitsImage(pfd.fileno(), data, wcs, title)
679 except Exception
as e:
697 definedCallbacks =
True 699 for k
in (
'XPA$ERROR',):
700 interface.setCallback(k)
def initDS9(execDs9=True)
def set(self, size, silent=True)
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
The makeStatistics() overload to handle lsst::afw::math::MaskedVector<>
def ds9Cmd(cmd=None, trap=True, flush=False, silent=True, frame=None, get=False)
def __init__(self, size=0)
def pushSize(self, size=-1)
def __init__(self, k, x, y)
def __init__(self, display, verbose=False, args, kwargs)
def getMaskPlaneColor(name, frame=None)
def flush(self, silent=True)