casa
$Rev:20696$
|
00001 import os 00002 import re 00003 import sys 00004 import time 00005 import base64 00006 import string 00007 import inspect 00008 from taskinit import casac ### needed for regionmanager 00009 00010 try: 00011 import dbus 00012 try: 00013 bus = dbus.SessionBus( ) 00014 have_dbus_module = True 00015 except: 00016 print "warning: dbus is not properly configured, viewer scripting will not be available" 00017 have_dbus_module = False 00018 bus = None 00019 except: 00020 print "warning: dbus is not available, viewer scripting will not be available" 00021 have_dbus_module = False 00022 bus = None 00023 00024 00025 def dbus_connection( ): 00026 return bus 00027 00028 00029 def seqselect(test, list): 00030 """ 00031 Select the elements from a sequence that 00032 satisfy the given test function 00033 - compare The test function should have following 00034 signature def test(item): and must return a boolean 00035 - list The List from which element need to be selected 00036 """ 00037 selected = [ ] 00038 for item in list: 00039 if test(item) == True: 00040 selected.append(item) 00041 return selected 00042 00043 00044 class viewertool(object): 00045 "manage task engines" 00046 00047 __t = string.maketrans("abcdefghijklmnopqrstuvwxyz0123456789/*:%$#@!&()~+,.:;{}[]|\\\"'^","abcdefghijklmnopqrstuvwxyz0123456789__________________________") 00048 __rgm = casac.regionmanager() 00049 00050 ### 00051 ### 'use_existing' defaults to false because: 00052 ### o for linux a new dbus session daemon is started for each casapy 00053 ### o for osx it would result in multiple casapy sessions using the same viewer 00054 ### o for casa.py included into python it makes sense to avoid a new viewer 00055 ### appearing (and sticking around) for each include 00056 ### 00057 def __init__( self, with_gui=True, pre_launch=False, use_existing=False ): 00058 00059 if type(with_gui) != bool: 00060 raise Exception, "the 'with_gui' parameter must be a boolean" 00061 00062 self.__state = { } 00063 self.__state['proxy'] = None 00064 self.__state['gui'] = with_gui 00065 self.__state['launched'] = False 00066 self.__state['dbus name'] = None 00067 00068 if type(with_gui) == bool and with_gui == False: 00069 basename = "vtoolng" 00070 else: 00071 basename = "vtool" 00072 00073 ## for viewer used from plain python, see if a viewer is already available on dbus first... 00074 bus = dbus_connection( ) 00075 if bus != None and type(use_existing) == bool and use_existing == True : 00076 candidates = seqselect(lambda x: x.startswith('edu.nrao.casa.%s_' % (basename)),map(str,bus.list_names( ))) 00077 if len( candidates ) > 0 : 00078 candidate = candidates[0] 00079 p = re.compile('[^\.]+') 00080 segments = p.findall(candidate) 00081 if len(segments) == 4 : 00082 self.__state['dbus name'] = segments[3] 00083 self.__state['launched'] = True 00084 00085 if self.__state['dbus name'] == None: 00086 self.__state['dbus name'] = "%s_%s" % (basename, (base64.b64encode(os.urandom(16))).translate(self.__t,'=')) 00087 00088 if pre_launch: 00089 self.__launch( ) 00090 00091 00092 def __launch( self ): 00093 00094 ## if we've already launched the viewer 00095 if type(self.__state['launched']) == bool and self.__state['launched'] == True: 00096 return 00097 00098 if dbus_connection( ) == None: 00099 raise Exception, "dbus is not available; cannot script the viewer" 00100 00101 a=inspect.stack() 00102 stacklevel=0 00103 for k in range(len(a)): 00104 if a[k][1] == "<string>" or (string.find(a[k][1], 'ipython console') > 0 or string.find(a[k][1],"casapy.py") > 0): 00105 stacklevel=k 00106 00107 myf=sys._getframe(stacklevel).f_globals 00108 00109 viewer_path = None 00110 if type(myf) == dict and myf.has_key('casa') and type(myf['casa']) == dict and myf['casa'].has_key('helpers') \ 00111 and type(myf['casa']['helpers']) == dict and myf['casa']['helpers'].has_key('viewer'): 00112 viewer_path = myf['casa']['helpers']['viewer'] #### set in casapy.py 00113 if len(os.path.dirname(viewer_path)) == 0: 00114 for dir in os.getenv('PATH').split(':') : 00115 dd = dir + os.sep + viewer_path 00116 if os.path.exists(dd) and os.access(dd,os.X_OK) : 00117 viewer_path = dd 00118 break 00119 args = [ viewer_path, "--casapy" ] 00120 else: 00121 for exe in ['casaviewer']: 00122 for dir in os.getenv('PATH').split(':') : 00123 dd = dir + os.sep + exe 00124 if os.path.exists(dd) and os.access(dd,os.X_OK) : 00125 viewer_path = dd 00126 break 00127 if viewer_path is not None: 00128 break 00129 args = [ viewer_path ] 00130 00131 if viewer_path == None or not os.access(viewer_path,os.X_OK): 00132 raise RuntimeError("cannot find casa viewer executable") 00133 00134 if self.__state['gui']: 00135 args += [ '--server=' + self.__state['dbus name'] ] 00136 else: 00137 args += [ '--nogui=' + self.__state['dbus name'] ] 00138 00139 if type(myf) == dict and myf.has_key('casa') and type(myf['casa']) == dict and myf['casa'].has_key('files') \ 00140 and type(myf['casa']['files']) == dict and myf['casa']['files'].has_key('logfile'): 00141 args += [ '--casalogfile=' + myf['casa']['files']['logfile'] ] 00142 00143 if type(myf) == dict and myf.has_key('casa') and type(myf['casa']) == dict and myf['casa'].has_key('flags') \ 00144 and type(myf['casa']['flags']) == dict and myf['casa']['flags'].has_key('--rcdir'): 00145 args += [ "--rcdir=" + myf['casa']['flags']['--rcdir'] ] 00146 00147 if (os.uname()[0]=='Darwin'): 00148 vwrpid=os.spawnvp( os.P_NOWAIT, viewer_path, args ) 00149 elif (os.uname()[0]=='Linux'): 00150 vwrpid=os.spawnlp( os.P_NOWAIT, viewer_path, *args ) 00151 else: 00152 raise Exception,'unrecognized operating system' 00153 00154 self.__state['launched'] = True 00155 00156 00157 def __connect( self ): 00158 if not self.__state['launched']: 00159 self.__launch( ) 00160 00161 if not self.__state['launched']: 00162 raise Exception, 'launch failed' 00163 00164 error = None 00165 for i in range(1,500): 00166 time.sleep(0.1) 00167 try: 00168 self.__state['proxy'] = bus.get_object( "edu.nrao.casa." + self.__state['dbus name'], "/casa/" + self.__state['dbus name'] ) 00169 if self.__state['proxy'] == None: 00170 time.sleep(0.25) 00171 continue 00172 error = None 00173 break 00174 except dbus.DBusException, e: 00175 if e.get_dbus_name() == 'org.freedesktop.DBus.Error.Disconnected' : 00176 raise RuntimeError('DBus daemon has died...') 00177 elif e.get_dbus_name() == 'org.freedesktop.DBus.Error.ServiceUnknown' : 00178 error = RuntimeError('DBus Viewer service failed to start...') 00179 continue 00180 else: 00181 raise RuntimeError('Unexpected DBus problem: ' + e.get_dbus_name( ) + "(" + e.args[0] + ")") 00182 except Exception, e: 00183 error = e 00184 continue 00185 00186 if error is not None : 00187 raise error 00188 00189 def __invoke( self, dt, t, func, *args, **kwargs ): 00190 ## set maximum dbus timeout... 00191 kwargs['timeout'] = 0x7fffffff / 1000.0 00192 try: 00193 result = func(*args,**kwargs) 00194 except dbus.DBusException, e: 00195 if e.get_dbus_name() == 'org.freedesktop.DBus.Error.Disconnected' : 00196 raise RuntimeError('DBus daemon has died....') 00197 elif e.get_dbus_name() == 'org.freedesktop.DBus.Error.ServiceUnknown' : 00198 raise RuntimeError('DBus Viewer service has exited....') 00199 else: 00200 raise RuntimeError('Unexpected DBus problem: ' + e.get_dbus_name( ) + "(" + e.args[0] + ")") 00201 00202 if type(result) == dbus.Dictionary and result.has_key('*error*') : 00203 raise RuntimeError(str(result['*error*'])) 00204 elif type(result) != dt : 00205 raise RuntimeError(str(result)) 00206 00207 return t(result) 00208 00209 def panel( self, paneltype="viewer" ) : 00210 if type(paneltype) != str or (paneltype != "viewer" and paneltype != "clean"): 00211 raise Exception, "the only valid panel types are 'viewer' and 'clean'" 00212 if self.__state['proxy'] == None: 00213 self.__connect( ) 00214 00215 return self.__invoke( dbus.Int32, int, self.__state['proxy'].panel, paneltype ) 00216 00217 def load( self, path, displaytype="raster", panel=0, scaling=0 ): 00218 if type(path) != str or type(displaytype) != str or \ 00219 (type(scaling) != float and type(scaling) != int) : 00220 raise Exception, "load() takes two strings; only the first arg is required..." 00221 00222 if self.__state['proxy'] == None: 00223 self.__connect( ) 00224 00225 return self.__invoke( dbus.Int32, int, self.__state['proxy'].load, path, displaytype, panel, float(scaling) ) 00226 00227 def close( self, panel=0 ): 00228 if type(panel) != int : 00229 raise Exception, "close() takes one optional integer..." 00230 00231 if self.__state['proxy'] == None: 00232 self.__connect( ) 00233 00234 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].close, panel ) 00235 00236 def popup( self, what, panel=0 ): 00237 if type(what) != str or type(panel) != int : 00238 raise Exception, "popup() takes a string followed by one optional integer..." 00239 00240 if self.__state['proxy'] == None: 00241 self.__connect( ) 00242 00243 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].popup, what, panel ) 00244 00245 def freeze( self, panel=0 ): 00246 if type(panel) != int : 00247 raise Exception, "freeze() takes only a panel id..." 00248 00249 if self.__state['proxy'] == None: 00250 self.__connect( ) 00251 00252 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].freeze, panel ) 00253 00254 def unfreeze( self, panel=0 ): 00255 if type(panel) != int : 00256 raise Exception, "unfreeze() takes only a panel id..." 00257 00258 if self.__state['proxy'] == None: 00259 self.__connect( ) 00260 00261 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].unfreeze, panel ) 00262 00263 00264 def restore( self, path, panel=0 ): 00265 if type(path) != str or type(panel) != int: 00266 raise Exception, "restore() takes a string and an integer; only the first arg is required..." 00267 00268 if self.__state['proxy'] == None: 00269 self.__connect( ) 00270 00271 return self.__invoke( dbus.Int32, int, self.__state['proxy'].restore, path, panel ) 00272 00273 def cwd( self, new_path='' ): 00274 if type(new_path) != str: 00275 raise Exception, "cwd() takes a single (optional) string..." 00276 00277 if self.__state['proxy'] == None: 00278 self.__connect( ) 00279 00280 return self.__invoke( dbus.String, str, self.__state['proxy'].cwd, new_path ) 00281 00282 def output( self, device, devicetype='file', panel=0, scale=1.0, dpi=300, format="jpg", \ 00283 orientation="portrait", media="letter" ): 00284 if type(device) != str or type(panel) != int or type(scale) != float or \ 00285 type(dpi) != int or type(format) != str or type(orientation) != str or \ 00286 type( media ) != str: 00287 raise Exception, "output() takes (str,int,float,int,str,str,str); only the first is required..." 00288 00289 if self.__state['proxy'] == None: 00290 self.__connect( ) 00291 00292 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].output, device, devicetype, panel, scale, dpi, format, orientation, media ) 00293 00294 def axes( self, x='', y='', z='', panel=0 ): 00295 if type(x) != str or type(y) != str or type(z) != str or type(panel) != int : 00296 raise Exception, "axes() takes one to three strings and an optional panel id..." 00297 00298 if self.__state['proxy'] == None: 00299 self.__connect( ) 00300 00301 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].axes, x, y, z, panel ) 00302 00303 def datarange( self, range, data=0 ): 00304 if type(range) != list or type(data) != int or \ 00305 all( map( lambda x: type(x) == int or type(x) == float, range ) ) == False: 00306 raise Exception, "datarange() takes (numeric list,int)..." 00307 if len(range) != 2 or range[0] > range[1] : 00308 raise Exception, "range should be [ min, max ]..." 00309 00310 if self.__state['proxy'] == None: 00311 self.__connect( ) 00312 00313 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].datarange, map( lambda(x): float(x), range ), data ) 00314 00315 def contourlevels( self, levels=[], baselevel=2147483648.0, unitlevel=2147483648.0, data=0 ): 00316 if type(levels) != list or type(data) != int or \ 00317 all( map( lambda x: type(x) == int or type(x) == float, levels ) ) == False: 00318 raise Exception, "contorlevels() takes (numeric list,int)..." 00319 00320 if self.__state['proxy'] == None: 00321 self.__connect( ) 00322 00323 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].contourlevels, dbus.Array(map( lambda(x): float(x), levels),signature="d"), baselevel, unitlevel, data ) 00324 00325 def colormap( self, map, data_or_panel=0 ): 00326 if type(map) != str or type(data_or_panel) != int : 00327 raise Exception, "colormap() takes a colormap name and an optional panel or data id..." 00328 00329 if self.__state['proxy'] == None: 00330 self.__connect( ) 00331 00332 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].colormap, map, data_or_panel ) 00333 00334 00335 def colorwedge( self, show, data_or_panel=0 ): 00336 if type(show) != bool or type(data_or_panel) != int : 00337 raise Exception, "colorwedge() takes a boolean and an optional panel or data id..." 00338 00339 if self.__state['proxy'] == None: 00340 self.__connect( ) 00341 00342 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].colorwedge, show, data_or_panel ) 00343 00344 00345 def channel( self, num=-1, panel=0 ): 00346 if type(num) != int or type(panel) != int: 00347 raise Exception, "frame() takes (int,int); each argument is optional..." 00348 00349 if self.__state['proxy'] == None: 00350 self.__connect( ) 00351 00352 return self.__invoke( dbus.Int32, int, self.__state['proxy'].channel, num, panel ) 00353 00354 def zoom( self, level=None, blc=[], trc=[], coordinates="pixel", region="", panel=0 ): 00355 if (type(level) != int and level != None) or \ 00356 type(blc) != list or type(trc) != list or type(panel) != int or \ 00357 type(coordinates) != str or (type(region) != str and type(region) != dict) : 00358 raise Exception, "zoom() takes (int|None,list,list,str,int); each argument is optional..." 00359 00360 if self.__state['proxy'] == None: 00361 self.__connect( ) 00362 00363 if (type(region) == str and os.path.isfile( region )) or \ 00364 type(region) == dict : 00365 reg = region 00366 if type(region) == str : 00367 try: 00368 reg = self.__rgm.fromfiletorecord( region ) 00369 except: 00370 raise Exception, "region file (" + str(region) + ") exists but is not a valid region file" 00371 00372 if type(reg) != dict : 00373 raise Exception, "invalid regions or failed to load region" 00374 00375 ( _blc, _trc, _coord ) = self.__extract_region_box( reg ) 00376 00377 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].zoom, _blc, _trc, _coord, panel ) 00378 00379 elif len(blc) == 2 and len(trc) == 2 and \ 00380 all( map( lambda x,y: (type(x) == int or type(x) == float) and (type(y) == int or type(y) == float), blc, trc ) ) == True: 00381 if coordinates != "pixel" and coordinates != "world": 00382 raise Exception, "zoom() coordinates must be either world or pixel" 00383 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].zoom, map( lambda(x): float(x), blc ), map( lambda(x): float(x), trc ), coordinates, panel ) 00384 elif level != None: 00385 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].zoom, level, panel ) 00386 else: 00387 raise Exception, "must supply either blc and trc or a level for zoom" 00388 00389 00390 00391 def hide( self, panel=0 ): 00392 if type(panel) != int: 00393 raise Exception, "hide() takes a single (int) panel identifier ..." 00394 00395 if self.__state['proxy'] == None: 00396 self.__connect( ) 00397 00398 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].hide, panel ) 00399 00400 def show( self, panel=0 ): 00401 if type(panel) != int: 00402 raise Exception, "show() takes a single (int) panel identifier ..." 00403 00404 if self.__state['proxy'] == None: 00405 self.__connect( ) 00406 00407 return self.__invoke( dbus.Boolean, bool, self.__state['proxy'].show, panel ) 00408 00409 def fileinfo( self, path ): 00410 if type(path) != str: 00411 raise Exception, "fileinfo() takes a single path..." 00412 00413 if self.__state['proxy'] == None: 00414 self.__connect( ) 00415 00416 return self.__invoke( dbus.Dictionary, dict, self.__state['proxy'].fileinfo, path ) 00417 00418 def keyinfo( self, key ): 00419 if type(key) != int: 00420 raise Exception, "keyinfo() takes a single int..." 00421 00422 if self.__state['proxy'] == None: 00423 self.__connect( ) 00424 00425 return self.__invoke( dbus.Array, lambda x: map(str,x), self.__state['proxy'].keyinfo, key ) 00426 00427 def done( self ): 00428 00429 if self.__state['proxy'] == None: 00430 self.__connect( ) 00431 00432 result = self.__invoke( dbus.Boolean, bool, self.__state['proxy'].done ) 00433 self.__state['proxy'] = None 00434 self.__state['launched'] = False 00435 return result 00436 00437 def __extract_region_box( self, reg ): 00438 00439 if reg.has_key( 'regions' ) : 00440 if type(reg['regions']) != dict or not reg['regions'].has_key( '*1' ) : 00441 raise Exception, "invalid region, has 'regions' field but wrong format" 00442 reg=reg['regions']['*1'] 00443 00444 if not reg.has_key('trc') or not reg.has_key('blc'): 00445 raise Exception, "region must have a 'blc' and 'trc' field" 00446 00447 blc_r = reg['blc'] 00448 trc_r = reg['trc'] 00449 00450 if type(blc_r) != dict or type(trc_r) != dict : 00451 raise Exception, "region blc/trc of wrong type" 00452 00453 blc_k = blc_r.keys( ) 00454 trc_k = trc_r.keys( ) 00455 00456 if len(blc_k) < 2 or len(trc_k) < 2: 00457 raise Exception, "degenerate region" 00458 00459 blc_k.sort( ) 00460 trc_k.sort( ) 00461 00462 if type(blc_r[blc_k[0]]) != dict or type(blc_r[blc_k[1]]) != dict or \ 00463 type(trc_r[trc_k[0]]) != dict or type(trc_r[trc_k[1]]) != dict : 00464 raise Exception, "invalid blc/trc in region" 00465 00466 if not blc_r[blc_k[0]].has_key('value') or not blc_r[blc_k[1]].has_key('value') or \ 00467 not trc_r[trc_k[0]].has_key('value') or not trc_r[trc_k[1]].has_key('value'): 00468 raise Exception, "invalid shape for blc/trc in region" 00469 00470 if (type(blc_r[blc_k[0]]['value']) != float and type(blc_r[blc_k[0]]['value']) != int) or \ 00471 (type(blc_r[blc_k[1]]['value']) != float and type(blc_r[blc_k[1]]['value']) != int) or \ 00472 (type(trc_r[trc_k[0]]['value']) != float and type(trc_r[trc_k[0]]['value']) != int) or \ 00473 (type(trc_r[trc_k[0]]['value']) != float and type(trc_r[trc_k[0]]['value']) != int) : 00474 raise Exception, "invalid type for blc/trc value in region" 00475 00476 blc = [ float(blc_r[blc_k[0]]['value']), float(blc_r[blc_k[1]]['value']) ] 00477 trc = [ float(trc_r[trc_k[0]]['value']), float(trc_r[trc_k[1]]['value']) ] 00478 00479 coord = "pixel" 00480 if reg.has_key('name') and reg['name'] == "WCBox": 00481 coord = "world" 00482 00483 return ( blc, trc, coord )