Package gmisclib :: Module g_ds9
[frames] | no frames]

Source Code for Module gmisclib.g_ds9

  1  #!/usr/bin/env python 
  2   
  3  """Handles interactions with ds9 (U{http://hea-www.harvard.edu/RD/ds9/}). 
  4  Ds9 is an image display and analysis tool commonly used in astrophysics, 
  5  but it's good for any kind of greyscale image. 
  6   
  7  Note: there is an extra, special property Coordsys.   This is not fed to 
  8  ds9.  It is merely something that you can use to remember what coordinate 
  9  system the regions are in. 
 10  """ 
 11   
 12   
 13  import re 
 14  import threading 
 15  import os 
 16  import time 
 17  import copy as COPY 
 18  from gmisclib import die 
 19  from gmisclib import g_exec 
 20   
 21  COLORS = ('white', 'black', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow') 
 22  PLOTSYMS = ('circle', 'diamond', 'plus', 'cross') 
 23   
24 -class UnknownProperty(KeyError):
25 - def __init__(self, *s):
26 KeyError.__init__(self, *s)
27 28
29 -class execwait(object):
30 - def __init__(self, s):
31 self.t = threading.Thread(target=os.system, args=(s,), 32 name='execwait: %s' % s 33 ) 34 self.t.start()
35
36 - def wait(self):
37 self.t.join()
38 39
40 -class ds9_io(object):
41 - def __init__(self, ds9start=None, xpaname='ds9', 42 xpaset=['xpaset', ], 43 xpaset_p=['xpaset', '-p'], 44 xpaget=['xpaget'] 45 ):
46 if ds9start is None: 47 ds9start = 'ds9' 48 self.setap = tuple(xpaset_p) + (xpaname,) 49 self.seta = tuple(xpaset) + (xpaname,) 50 self.geta = tuple(xpaget) + (xpaname,) 51 self.ds9ew = None 52 self.debug = 0 53 54 try: 55 tmp = self.getall('about') 56 if tmp: 57 self.closed = False 58 return 59 except g_exec.ExecError: 60 pass 61 62 self.ds9ew = execwait(ds9start) 63 sleep = 0.10 64 time.sleep(2*sleep) 65 while True: 66 try: 67 tmp = self.getall('about') 68 if tmp: 69 self.closed = False 70 return 71 except g_exec.ExecError: 72 sleep += 0.1 73 time.sleep(sleep)
74 75 76
77 - def set(self, *args):
78 tmp = self.setap + tuple(args) 79 g_exec.getall(tmp[0], tmp)
80 81
82 - def set_with_input(self, input, *args):
83 """Run the X{xpaset} command and feed it data on its standard input. 84 Each element in the input list becomes one line of data 85 to C{xpaset}. 86 @param input: 87 @type input: L{list} or iterator 88 @param args: The command line used to execute C{xpaset}. 89 @type args: C{[ "xpaset", ...]} 90 """ 91 tmp = self.seta + tuple(args) 92 g_exec.getall(tmp[0], tmp, 93 input=[q+'\n' for q in input], 94 debug=self.debug)
95 96
97 - def getlast(self, *args):
98 return self.getall(*args)[-1]
99 100
101 - def getall(self, *args):
102 return list(self.getiter(*args))
103 104
105 - def getiter(self, *args):
106 tmp = self.geta + tuple(args) 107 for q in g_exec.getiter_raw(tmp[0], tmp): 108 yield q.rstrip()
109 110
111 - def close(self):
112 if self.ds9ew and not self.closed: 113 self.set('quit') 114 self.ds9ew.wait() 115 self.ds9ew = None 116 self.closed = True
117 118
119 - def __del__(self):
120 try: 121 self.close() 122 except g_exec.ExecError: 123 # Too late to report an error now. 124 # If it doesn't shut down, that's just too bad. 125 pass
126 127
128 -class BadTagError(ValueError):
129 - def __init__(self, *args):
130 ValueError.__init__(self, *args)
131 132
133 -class Region(object):
134 - def __init__(self, kwargs, coordsys):
135 self._props = kwargs 136 self._pshared = True 137 self.Coordsys = coordsys
138
139 - def __str__(self):
140 raise RuntimeError, 'Virtual function'
141
142 - def addprop(self, k, v):
143 if self._pshared: 144 self._props = self._props.copy() 145 self._pshared = False 146 self._props[k] = v
147
148 - def has_prop(self, k):
149 return self._props.has_key(k)
150
151 - def getprop(self, *args):
152 return self._props.get(*args)
153 154
155 - def props(self):
156 o = [ '#' ] 157 for (k, v) in self._props.items(): 158 try: 159 pof = PROPout[k] 160 except KeyError, x: 161 raise UnknownProperty( *(x.args) ) 162 o.extend( pof(k, v) ) 163 if len(o) > 1: 164 return ' '.join(o) 165 return ''
166 167
168 - def chgprops(self, **kwargs):
169 if self._pshared: 170 self._props = self._props.copy() 171 self._pshared = False 172 self._props.update(kwargs) 173 return self
174
175 - def compatible_coord(self, coordsys):
176 return (self.Coordsys is None or coordsys is None 177 or self.Coordsys == coordsys)
178 179 180
181 -def copy(r, **kwargs):
182 """Copy a region and update some of the keyword 183 arguments (e.g. color). The original object is unchanged, 184 and you can copy any subclass of Region. 185 """ 186 assert isinstance(r, Region) 187 tmp = COPY.copy(r) # Deepcopy? 188 tmp.chgprops(**kwargs) 189 return tmp
190 191
192 -class line(Region):
193 - def __init__(self, x0, y0, x1, y1, Coordsys=None, **kwargs):
194 Region.__init__(self, kwargs, Coordsys) 195 self.x0 = x0 196 self.y0 = y0 197 self.x1 = x1 198 self.y1 = y1
199
200 - def __str__(self):
201 return 'line %10g %10g %10g %10g %s' % ( self.x0+1, self.y0+1, 202 self.x1+1, self.y1+1, self.props() )
203
204 - def end(self, whichend):
205 if whichend == 0: 206 return point(self.x0, self.y0, **self._props) 207 elif whichend == 1: 208 return point(self.x1, self.y1, **self._props) 209 else: 210 raise ValueError, "Whichend == 0 or 1"
211
212 - def center(self):
213 return point(0.5*(self.x0+self.x1), 0.5*(self.y0+self.y1), Coordsys=self.Coordsys)
214 215
216 -class circle(Region):
217 - def __init__(self, x, y, r, Coordsys=None, **kwargs):
218 Region.__init__(self, kwargs, Coordsys) 219 self.x = x 220 self.y = y 221 self.r = r
222
223 - def __str__(self):
224 return 'circle %10g %10g %10g %s' % ( self.x+1, self.y+1, self.r, self.props() )
225
226 - def center(self):
227 return point(self.x, self.y, Coordsys=self.Coordsys)
228 229
230 -class point(Region):
231 - def __init__(self, x, y, Coordsys=None, **kwargs):
232 Region.__init__(self, kwargs, Coordsys) 233 self.x = x 234 self.y = y
235
236 - def __str__(self):
237 return 'point %10g %10g %s' % ( self.x+1, self.y+1, self.props() )
238 239 240 241
242 -class text(Region):
243 - def __init__(self, x, y, text, Coordsys=None, **kwargs):
244 Region.__init__(self, kwargs, Coordsys) 245 self.addprop('text', text) 246 self.x = x 247 self.y = y 248 self.text = text
249
250 - def __str__(self):
251 return 'text %10g %10g %s' % ( self.x+1, self.y+1, self.props() )
252 253
254 -def dequote(s):
255 if s.startswith('{') and s.endswith('}'): 256 return s[1:-1] 257 elif len(s)>1 and s.startswith('"') and s.endswith('"'): 258 return s[1:-1] 259 elif len(s)>1 and s.startswith("'") and s.endswith("'"): 260 return s[1:-1] 261 return s
262 263 264 265 PROPin = {'color': str, 'text': dequote, 'font': dequote, 266 'select': int, 'edit': int, 'move': int, 'rotate': int, 267 'delete': int, 'fixed': int, 268 'line': None, 'tag': None, 269 'ruler': int, 'width': int, 270 'point': str, 'highlite': int, 'include': int 271 } 272 273
274 -def _kvquote(k, v):
275 return [ '%s={%s}' % (k, v) ]
276 277
278 -def _kvint(k, v):
279 return [ '%s=%d' % (k, v) ]
280
281 -def _kvstr(k, v):
282 return [ '%s=%s' % (k, v) ]
283
284 -def _kvnull(k, v):
285 return []
286
287 -def _kvline(k, v):
288 return [ '%s = %d %d' % (k, v[0], v[1]) ]
289 290 _oktag = re.compile("^[a-zA-Z][a-zA-Z0-9]*$")
291 -def _kvtags(k, v):
292 for tmp in v: 293 if not _oktag.match(tmp): 294 raise BadTagError, 'tags can contain only alphanumerics: "%s"' % tmp 295 return [ 'tag={%s}' % tmp for tmp in v ]
296
297 -def _kverr(k, v):
298 raise RuntimeError, "'tags', not 'tag'."
299 300 PROPout = {'color': _kvstr, 'text': _kvquote, 'font': _kvquote, 301 'select': _kvint, 'edit': _kvint, 'move': _kvint, 'rotate': _kvint, 302 'delete': _kvint, 'fixed': _kvint, 303 'line': _kvline, 'tags': _kvtags, 'tag': _kverr, 304 'ruler': _kvint, 'width': _kvint, 305 'point': _kvstr, 'highlite': _kvint, 'include': _kvint 306 } 307 308 309 310 _rparse = re.compile(r"""{[^}]*}|"[^"]*"|'[^']'|[^\s,()=#]+|[#]|=""") 311
312 -def make_chunks(s):
313 return _rparse.findall(s)
314 315 316
317 -def property_parser(ap):
318 kwargs = {} 319 i = 0 320 while i+2 < len(ap): 321 arg = ap[i] 322 if arg in PROPin and ap[i+1] == '=': 323 if arg == 'line' and i+3 < len(ap): 324 kwargs[arg] = ( int(ap[i+2]), int(ap[i+3]) ) 325 i += 4 326 elif arg == 'tag': 327 if 'tags' not in kwargs: 328 # This is just setting up a list to extend if we 329 # have more than one occurrence of 'tags'. 330 kwargs['tags'] = [ dequote(ap[i+2]) ] 331 else: 332 kwargs['tags'].append( dequote(ap[i+2]) ) 333 i += 3 334 else: 335 kwargs[arg] = PROPin[arg]( ap[i+2] ) 336 i += 3 337 else: 338 die.info('ap=%s' % str(ap)) 339 die.die('Unexpected word in property: %s' % arg) 340 # print 'PP:', kwargs 341 return kwargs
342 343
344 -def test_property_parser():
345 print "TPP" 346 pp = property_parser 347 assert pp(['select', '=', '1']) == {'select': 1} 348 assert pp(['text', '=', '1']) == {'text': '1'} 349 assert pp(['text', '=', '{foo}']) == {'text': 'foo'} 350 assert pp(['text', '=', '"foo"']) == {'text': 'foo'} 351 assert pp(['line', '=', '1', '1']) == {'line': (1,1)} 352 assert pp(['select', '=', '1', 'fixed', '=', '0']) == {'select': 1, 'fixed': 0}
353 354
355 -def region_parser(s, defprop):
356 s = s.strip() 357 kwargs = defprop.copy() 358 if s.startswith('-'): 359 kwargs['include'] = 0 360 s = s[1:] 361 elif s.startswith('+'): 362 kwargs['include'] = 1 363 s = s[1:] 364 365 # print 's=', s 366 aa = make_chunks(s) 367 x = aa[0] 368 # print 'aa=', aa 369 if '#' in aa: 370 hi = aa.index('#') 371 # print 'hi=', hi 372 ap = aa[hi+1:] 373 aa = aa[1:hi] 374 else: 375 aa = aa[1:] 376 ap = [] 377 # print 'region_parser:', aa, '###', ap 378 379 args = [ float(q) for q in aa ] 380 kwargs.update( property_parser(ap) ) 381 return (x, args, kwargs)
382 383
384 -def test_region_parser():
385 print "TRP" 386 x, args, kwargs = region_parser('line(0,0,1,2)', {}) 387 assert x == 'line' 388 assert args == [0, 0, 1, 2] 389 assert kwargs == {} 390 391 x, args, kwargs = region_parser('+point(-1,2)', {}) 392 assert x == 'point' 393 assert args == [-1, 2] 394 assert kwargs == {'include': 1} 395 396 x, args, kwargs = region_parser('-point(-1,2) # color=red', {}) 397 assert x == 'point' 398 assert args == [-1, 2] 399 assert kwargs == {'include': 0, 'color': 'red'} 400 401 x, args, kwargs = region_parser('circle(0,2,3) # text={foo}', {}) 402 assert x == 'circle' 403 assert args == [0,2,3] 404 assert kwargs == {'text': 'foo'} 405 406 x, args, kwargs = region_parser('circle(0,2,3) # text="foo" rotate =1', {}) 407 assert x == 'circle' 408 assert args == [0,2,3] 409 assert kwargs == {'text': 'foo', 'rotate': 1} 410 411 x, args, kwargs = region_parser('circle(0,2,3) # text="foo bar" rotate = 0 fixed= 1', {}) 412 assert x == 'circle' 413 assert args == [0,2,3] 414 assert kwargs == {'text': 'foo bar', 'rotate': 0, 'fixed': 1} 415 416 x, args, kwargs = region_parser('circle(0,2,3) # text={foo bar} line=1 1 fixed=0', {}) 417 assert x == 'circle' 418 assert args == [0,2,3] 419 assert kwargs == {'text': 'foo bar', 'line': (1,1), 'fixed': 0}
420 421 422 423 _text_region = re.compile(r'\s*#\s*text[( \t]([0-9eE.+-]+)[, \t]+([0-9eE.+-]+)[) \t]\s*(.*)$') 424
425 -def text_kluge(s):
426 m = _text_region.match(s) 427 assert m 428 kwargs = property_parser(make_chunks(m.group(3))) 429 # print '#kwargs=', kwargs 430 txt = kwargs.pop('text') 431 return text(float(m.group(1)), float(m.group(2)), txt, **kwargs)
432
433 -def test_text_kluge():
434 print "TTK" 435 tmp = text_kluge('# text(2,1) text={fred} color=red tag={RefImgF}') 436 assert tmp.getprop('color') == 'red' 437 assert tmp.getprop('text') == 'fred' 438 assert tmp.getprop('tags') == ['RefImgF',] 439 assert abs(tmp.x-2.0)<0.0001 and abs(tmp.y-1.0)<0.00001
440 441
442 -def r_list_factory(slist, Coordsys):
443 gp = {'include': 1, 'text': '', 'color': 'green', 444 'font': 'helvetica 10 normal', 'edit': 1, 445 'move': 1, 'delete': 1, 'fixed': 0 446 } 447 for s in slist: 448 s = s.strip() 449 # print 's=', s 450 if _text_region.match(s): 451 yield text_kluge(s) 452 continue 453 elif s.startswith('#'): 454 # print 'Comment', s 455 continue 456 if s.startswith('global'): 457 gp = property_parser(make_chunks(s[len('global'):])) 458 continue 459 x, args, kwargs = region_parser(s, gp) 460 if x is None: 461 pass 462 elif x == 'physical': 463 pass 464 elif x == 'image': 465 pass 466 elif x == 'circle': 467 assert len(args) == 3 468 yield circle(args[0]-1, args[1]-1, args[2], Coordsys=Coordsys, **kwargs) 469 elif x == 'line': 470 assert len(args) == 4 471 yield line(args[0]-1, args[1]-1, 472 args[2]-1, args[3]-1, Coordsys=Coordsys, **kwargs) 473 elif x == 'point': 474 assert len(args) == 2 475 yield point(args[0]-1, args[1]-1, Coordsys=Coordsys, **kwargs) 476 elif x == 'text': 477 if len(args) == 2: 478 # We happen to know that one of the kwargs 479 # is text, which will provide the third 480 # argument to the text() constructor. 481 yield text(args[0]-1, args[1]-1, Coordsys=Coordsys, **kwargs) 482 elif len(args) == 3: 483 yield text(args[0]-1, args[1]-1, args[2], Coordsys=Coordsys, **kwargs) 484 else: 485 die.die('Unsupported region: %s in <%s>' % (x, s))
486 487
488 -def read_regions_iter(fd, Coordsys=None):
489 """Read regions from a file. This is an iterator. 490 Fd is a file descriptor. 491 """ 492 return r_list_factory(fd, Coordsys)
493 494
495 -def read_regions(fd, Coordsys=None):
496 """Read regions from a file. 497 Fd is a file descriptor. 498 """ 499 return list(r_list_factory(fd, Coordsys))
500 501 502 PLOTSTYLES = ('discrete', 'line', 'step', 'quadratic', 'errorbar') 503 504
505 -def _format_plot(xylist, format):
506 if format == '(x,y)': 507 ds9fmt = 'xy' 508 o = [ '%10g %10g' % xy for xy in xylist ] 509 elif format == '(x,y,ex)': 510 ds9fmt = 'xyex' 511 o = [ '%10g %10g %6g' % xye for xye in xylist ] 512 elif format == '(x,y,ey)': 513 ds9fmt = 'xyey' 514 o = [ '%10g %10g %6g' % xye for xye in xylist ] 515 elif format == '(x,y,ex,ey)': 516 ds9fmt = 'xyexey' 517 o = [ '%10g %10g %6g %6g' % xyexey for xyexey in xylist ] 518 elif format == 'array(:,xy)': 519 ds9fmt = 'xy' 520 o = [ '%10g %10g' % (xylist[i,0], xylist[i,1]) for i in range(xylist.shape[0]) ] 521 elif format == 'array(:,xyey)': 522 ds9fmt = 'xyey' 523 o = [ '%10g %10g %6g' % (xylist[i,0], xylist[i,1], xylist[i,2]) 524 for i in range(xylist.shape[0]) 525 ] 526 elif format == 'array(xy,:)': 527 ds9fmt = 'xy' 528 o = [ '%10g %10g' % (xylist[0,i], xylist[1,i]) for i in range(xylist.shape[1]) ] 529 elif format == 'array(xyey,:)': 530 ds9fmt = 'xyey' 531 o = [ '%10g %10g %6g' % (xylist[0,i], xylist[1,i], xylist[2,i]) 532 for i in range(xylist.shape[1]) 533 ] 534 535 else: 536 raise ValueError, 'Unknown plot data format: %s' % format 537 return (ds9fmt, o)
538 539 540 541
542 -class ds9(ds9_io):
543 - def __init__(self, ds9start=None):
544 ds9_io.__init__(self, ds9start=ds9start) 545 self.set('mode', 'pointer') 546 self.frame = -1
547
548 - def load(self, fn):
549 self.set('regions', 'delete', 'all') 550 self.set('file', fn)
551
552 - def get_current_fname(self):
553 return self.getlast( 'iis', 'filename' )
554
555 - def get_frames(self):
556 tmp = self.getlast( 'frame', 'all' ).split() 557 if 'XPA$END' in tmp: 558 # This works around a bug in ds9: 559 # if it has no data frames, it'll tell you 560 # about the buttons and other stuff. 561 return [] 562 return tmp
563
564 - def frame_cmd(self, *cmds):
565 """A catch-all command for anything that's not otherwise implemented.""" 566 self.set('frame', *cmds)
567 568
569 - def del_frames(self, framelist):
570 """Note: deleting all the frames crashes ds9. 571 """ 572 for i in framelist: 573 self.select_frame(i) 574 self.frame_cmd('delete')
575 576
577 - def select_frame(self, i):
578 """Select a frame on the attached ds9 image display. 579 The frame will be created if it didn't already exist. 580 @param i: Which frame? Apparently, any integer is OK, 581 even negative. 582 @type i: L{int} 583 """ 584 si = str(int(i)) 585 if si != self.frame: 586 self.set('frame', 'frameno', si) 587 self.frame = si
588 589
590 - def get_regions_iter(self):
591 return r_list_factory(self.getiter('regions', '-format', 'ds9', 592 '-delim', 'nl' ), 593 None 594 )
595
596 - def get_regions(self):
597 return list(self.get_regions_iter())
598
599 - def delete_all_regions(self):
600 self.set('regions', 'delete', 'all')
601
602 - def delete_region_group(self, *group):
603 for g in group: 604 self.set('regions', 'group', g, 'delete')
605
606 - def add_region(self, *region):
607 """Add a list of regions to the current frame. 608 @param region: one or more regions. 609 @type region: C{[ region ]}, where C{region} is normally 610 an instance of L{Region}, but can be anything that 611 produces a string suitable for X{ds9}. 612 See C{http://hea-www.harvard.edu/RD/ds9/ref/xpa.html#regions}. 613 """ 614 # print 'region=', str(region) 615 self.set_with_input([ str(r) for r in region ], 'regions')
616 617
618 - def region_cmd(self, *cmds):
619 """A catch-all command for anything that's not otherwise implemented. 620 This does C{xpaset -p}, so you cannot use it for region definitions 621 that need multi-line input via L{set_with_input}. 622 """ 623 self.set('regions', *cmds)
624
625 - def zoom(self, z):
626 self.set('zoom', 'to', str(z))
627
628 - def scale_cmd(self, *cmds):
629 """A catch-all command for anything that's not otherwise implemented.""" 630 self.set('scale', *cmds)
631
632 - def cmap_cmd(self, *cmds):
633 """A catch-all command for anything that's not otherwise implemented.""" 634 self.set('cmap', *cmds)
635
636 - def set_mode(self, m):
637 assert m in ['tile', 'blink', 'single'] 638 if m == 'tile': 639 self.set('tile', 'yes') 640 else: 641 self.set('tile', 'no') 642 if m == 'single': 643 self.set('single') 644 if m == 'blink': 645 self.set('blink')
646
647 - def pan_to(self, x, y, coordsys):
648 self.set('pan', 'to', str(x), str(y), coordsys)
649 650 plotname_ok = re.compile('[a-z][a-zA-Z_]*') 651
652 - def plot(self, name, xylist, title='', xlabel='', ylabel='', 653 format='(x,y)', line='solid', 654 color='black', symbol=None):
655 assert name != 'new' 656 assert self.plotname_ok.match(name), "Bad plot name: %s" % name 657 ds9fmt, formatted = _format_plot(xylist, format) 658 if name in self.list_plots(): 659 assert title=='' 660 assert xlabel=='' 661 assert ylabel=='' 662 self.set_with_input( formatted, 'plot %s data %s' % (name, ds9fmt)) 663 else: 664 self.set_with_input( formatted, 665 'plot new name %s {%s} {%s} {%s} %s' 666 % (name, title, xlabel, ylabel, ds9fmt) 667 ) 668 assert color in COLORS 669 self.set('plot', 'view', 'discrete', ['yes', 'no'][symbol is None]) 670 self.set('plot', 'view', 'line', ['yes', 'no'][line is None]) 671 if symbol is not None: 672 assert symbol in PLOTSYMS 673 self.set('plot', 'color', 'discrete', color) 674 self.set('plot', 'line', 'discrete', symbol) 675 if line is not None: 676 self.set('plot', 'color', 'line', color) 677 self.set('plot', 'line', 'dash', ['no', 'yes'][line=='solid'])
678
679 - def close_plot(self, name):
680 self.set('plot', 'close', '{%s}' % name)
681
682 - def list_plots(self):
683 return self.getlast('plot').split()
684
685 - def comment(self, s):
686 pass
687
688 - def flush(self):
689 pass
690 691
692 -class ds9_file(object):
693 """This is just a convenient way to write regions to a file, 694 instead of writing them to a ds9 session. Note that plots 695 and other interactive things are simply no-ops. 696 """ 697
698 - def __init__(self, fd):
699 self.fd = fd 700 self.frame = -1 701 self.fd.writelines('# Region file format: DS9 version 4.0\n')
702
703 - def select_frame(self, i):
704 si = str(i) 705 if si != self.frame: 706 self.fd.writelines('# frame %s\n' % i) 707 self.frame = si
708 709
710 - def add_region(self, region):
711 self.fd.writelines( [ str(region), '\n'] )
712 713
714 - def comment(self, s):
715 self.fd.writelines( ['# ', s, '\n'] )
716
717 - def flush(self):
718 return self.fd.flush()
719
720 - def plot(self, name, *args, **kwargs):
721 self.comment('plot %s' % name)
722 723
724 - def list_plots(self):
725 return []
726
727 -def test_io():
728 print "TIO" 729 ds9_io()
730 731
732 -def test_tags():
733 print "TT" 734 tmp = ds9() 735 tmp.add_region(line(1,1,50,50, tags=('aa', 't2', 'xyz'), text='foo bar') ) 736 for r in tmp.get_regions(): 737 if r.getprop('text', '') == 'foo bar': 738 assert r.x0 == 1 and r.y0==1 and r.x1==50 and r.y1==50 739 assert 'aa' in r.getprop('tags') 740 assert 't2' in r.getprop('tags') 741 assert 'xyz' in r.getprop('tags')
742 743
744 -def test_plot():
745 print "TPL" 746 import math 747 xylist1 = [ (x, math.cos(x*0.3)) for x in range(100) ] 748 xylist2 = [ (x, math.sin(x*0.33)) for x in range(100) ] 749 tmp = ds9() 750 tmp.plot('some_data', xylist1, 'Some Data', 'x-axis', 'y-axis') 751 tmp.plot('some_data', xylist2, color='red') 752 time.sleep(6)
753 754 755 756 if __name__ == '__main__': 757 import sys 758 if len(sys.argv) == 2: 759 for r in read_regions_iter(open(sys.argv[1], 'r')): 760 print r 761 sys.exit(0) 762 test_property_parser() 763 test_text_kluge() 764 test_region_parser() 765 test_io() 766 test_tags() 767 test_plot() 768