Package pyraf :: Module irafdisplay
[hide private]
[frames] | no frames]

Source Code for Module pyraf.irafdisplay

  1  """irafdisplay.py: Interact with IRAF-compatible image display 
  2   
  3  Modeled after the NOAO Client Display Library (CDL) 
  4   
  5  Public functions: 
  6   
  7  readCursor(sample=0) 
  8          Read image cursor position 
  9   
 10  open(imtdev=None) 
 11          Open a connection to the display server.  This is called 
 12          automatically by readCursor if the display has not already been 
 13          opened, so it is not generally necessary for users to call it. 
 14   
 15          See the open doc string for info on the imtdev argument, which 
 16          allows various forms of socket and network connections. 
 17   
 18  close() 
 19          Close the active display server.  Called automatically on exit. 
 20   
 21  Various classes are defined for the different connections (ImageDisplay, 
 22  ImageDisplayProxy, UnixImageDisplay, InetImageDisplay, FifoImageDisplay). 
 23  They should generally be created using the _open factory function. 
 24  This could be used to maintain references to multiple display servers. 
 25   
 26  Ultimately more functionality may be added to make this a complete 
 27  replacement for CDL. 
 28   
 29  $Id: irafdisplay.py 1463 2011-06-24 22:58:30Z stsci_embray $ 
 30  """ 
 31  from __future__ import division # confidence high 
 32   
 33  import os, numpy, socket, sys 
 34  from stsci.tools import irafutils 
 35  try: 
 36      import fcntl 
 37  except: 
 38      if 0==sys.platform.find('win'): # not on win*, but IS on darwin & cygwin 
 39          fcntl = None 
 40      else: 
 41          raise 
 42   
 43  # FCNTL is deprecated in Python 2.2 
 44  if hasattr(fcntl,'F_SETFL') or fcntl==None: 
 45      FCNTL = fcntl 
 46  else: 
 47      import FCNTL 
 48   
 49  _default_imtdev = ("unix:/tmp/.IMT%d", "fifo:/dev/imt1i:/dev/imt1o") 
 50   
51 -def _open(imtdev=None):
52 53 """Open connection to the image display server 54 55 This is a factory function that returns an instance of the ImageDisplay 56 class for the specified imtdev. The default connection if no imtdev is 57 specified is given in the environment variable IMTDEV (if defined) or 58 is "unix:/tmp/.IMT%d". Failing that, a connection is attempted on the 59 /dev/imt1[io] named fifo pipes. 60 61 The syntax for the imtdev argument is <domain>:<address> where <domain> 62 is one of "inet" (internet tcp/ip socket), "unix" (unix domain socket) 63 or "fifo" (named pipe). The form of the address depends upon the 64 domain, as illustrated in the examples below. 65 66 inet:5137 Server connection to port 5137 on the local 67 host. For a client, a connection to the 68 given port on the local host. 69 70 inet:5137:foo.bar.edu Client connection to port 5137 on internet 71 host foo.bar.edu. The dotted form of address 72 may also be used. 73 74 unix:/tmp/.IMT212 Unix domain socket with the given pathname 75 IPC method, local host only. 76 77 fifo:/dev/imt1i:/dev/imt1o FIFO or named pipe with the given pathname. 78 IPC method, local host only. Two pathnames 79 are required, one for input and one for 80 output, since FIFOs are not bidirectional. 81 For a client the first fifo listed will be 82 the client's input fifo; for a server the 83 first fifo will be the server's output fifo. 84 This allows the same address to be used for 85 both the client and the server, as for the 86 other domains. 87 88 The address field may contain one or more "%d" fields. If present, the 89 user's UID will be substituted (e.g. "unix:/tmp/.IMT%d"). 90 """ 91 92 if not imtdev: 93 # try defaults 94 defaults = list(_default_imtdev) 95 if os.environ.has_key('IMTDEV'): 96 defaults.insert(0, os.environ['IMTDEV']) 97 for imtdev in defaults: 98 try: 99 return _open(imtdev) 100 except IOError, error: 101 pass 102 raise IOError("Cannot open image display") 103 # substitute user id in name (multiple times) if necessary 104 nd = len(imtdev.split("%d")) 105 dev = imtdev % ((os.getuid(),)*(nd-1)) 106 fields = dev.split(":") 107 domain = fields[0] 108 if domain == "unix" and len(fields) == 2: 109 return UnixImageDisplay(fields[1]) 110 elif domain == "fifo" and len(fields) == 3: 111 return FifoImageDisplay(fields[1],fields[2]) 112 elif domain == "inet" and (2 <= len(fields) <= 3): 113 try: 114 port = int(fields[1]) 115 if len(fields) == 3: 116 hostname = fields[2] 117 else: 118 hostname = None 119 return InetImageDisplay(port, hostname) 120 except ValueError: 121 pass 122 raise ValueError("Illegal image device specification `%s'" 123 % imtdev)
124 125
126 -class ImageDisplay:
127 128 """Interface to IRAF-compatible image display""" 129 130 # constants for cursor read 131 _IIS_READ = 0100000 132 _IMC_SAMPLE = 0040000 133 _IMCURSOR = 020 134 _SZ_IMCURVAL = 160 135
136 - def __init__(self):
137 # Flag indicating that readCursor request is active. 138 # This is used to handle interruption of readCursor before 139 # read is complete. Without this kluge, ^C interrupts 140 # leave image display in a bad state. 141 self._inCursorMode = 0
142
143 - def readCursor(self,sample=0):
144 145 """Read image cursor value for this image display 146 147 Return immediately if sample is true, or wait for keystroke 148 if sample is false (default). Returns a string with 149 x, y, frame, and key. 150 """ 151 152 if not self._inCursorMode: 153 opcode = self._IIS_READ 154 if sample: 155 opcode |= self._IMC_SAMPLE 156 self._writeHeader(opcode, self._IMCURSOR, 0, 0, 0, 0, 0) 157 self._inCursorMode = 1 158 s = self._read(self._SZ_IMCURVAL) 159 self._inCursorMode = 0 160 # only part up to newline is real data 161 return s.split("\n")[0]
162
163 - def _writeHeader(self,tid,subunit,thingct,x,y,z,t):
164 165 """Write request to image display""" 166 167 a = numpy.array([tid,thingct,subunit,0,x,y,z,t], numpy.int16) 168 # Compute the checksum 169 sum = numpy.add.reduce(a) 170 sum = 0xffff - (sum & 0xffff) 171 a[3] = sum 172 self._write(a.tostring())
173
174 - def close(self, os_close=os.close):
175 176 """Close image display connection""" 177 178 try: 179 os_close(self._fdin) 180 except (OSError, AttributeError): 181 pass 182 try: 183 os_close(self._fdout) 184 except (OSError, AttributeError): 185 pass
186
187 - def _read(self, n):
188 """Read n bytes from image display and return as string 189 190 Raises IOError on failure. If a Tkinter widget exists, runs 191 a Tk mainloop while waiting for data so that the Tk widgets 192 remain responsive. 193 """ 194 try: 195 return irafutils.tkread(self._fdin, n) 196 except (EOFError, IOError): 197 raise IOError("Error reading from image display")
198
199 - def _write(self, s):
200 """Write string s to image display 201 202 Raises IOError on failure 203 """ 204 try: 205 n = len(s) 206 while n>0: 207 nwritten = os.write(self._fdout, s[-n:]) 208 n -= nwritten 209 if nwritten <= 0: 210 raise IOError("Error writing to image display") 211 except OSError: 212 raise IOError("Error writing to image display")
213 214
215 -class FifoImageDisplay(ImageDisplay):
216 217 """FIFO version of image display""" 218
219 - def __init__(self, infile, outfile):
220 ImageDisplay.__init__(self) 221 try: 222 self._fdin = os.open(infile, os.O_RDONLY | os.O_NDELAY) 223 fcntl.fcntl(self._fdin, FCNTL.F_SETFL, os.O_RDONLY) 224 self._fdout = os.open(outfile, os.O_WRONLY | os.O_NDELAY) 225 fcntl.fcntl(self._fdout, FCNTL.F_SETFL, os.O_WRONLY) 226 except OSError, error: 227 raise IOError("Cannot open image display (%s)" % (error,)) 228 except AttributeError: 229 raise RuntimeError("Image fcntl is not supported on this platform")
230
231 - def __del__(self):
232 self.close()
233
234 -class UnixImageDisplay(ImageDisplay):
235 236 """Unix socket version of image display""" 237
238 - def __init__(self, filename, family=None, type=socket.SOCK_STREAM):
239 ImageDisplay.__init__(self) 240 try: 241 if family==None: # set in func, not in decl so it works on win 242 family = socket.AF_UNIX 243 self._socket = socket.socket(family, type) 244 self._socket.connect(filename) 245 self._fdin = self._fdout = self._socket.fileno() 246 except socket.error, error: 247 raise IOError("Cannot open image display")
248
249 - def close(self):
250 251 """Close image display connection""" 252 253 self._socket.close()
254 255
256 -class InetImageDisplay(UnixImageDisplay):
257 258 """INET socket version of image display""" 259
260 - def __init__(self, port, hostname=None):
261 hostname = hostname or "localhost" 262 UnixImageDisplay.__init__(self, (hostname, port), family=socket.AF_INET)
263 264
265 -class ImageDisplayProxy(ImageDisplay):
266 267 """Interface to IRAF-compatible image display 268 269 This is a proxy to the actual display that allows retries 270 on failures and can switch between display connections. 271 """ 272
273 - def __init__(self, imtdev=None):
274 # if imtdev is specified, it becomes the default for the 275 # life of this instance 276 self._display = None 277 self.imtdev = imtdev 278 if imtdev: 279 self.open()
280
281 - def open(self, imtdev=None):
282 283 """Open image display connection, closing any active connection""" 284 285 self.close() 286 self._display = _open(imtdev or self.imtdev)
287
288 - def close(self):
289 290 """Close active image display connection""" 291 292 if self._display: 293 self._display.close() 294 self._display = None
295
296 - def readCursor(self,sample=0):
297 298 """Read image cursor value for the active image display 299 300 Return immediately if sample is true, or wait for keystroke 301 if sample is false (default). Returns a string with 302 x, y, frame, and key. Opens image display if necessary. 303 """ 304 305 if not self._display: 306 self.open() 307 try: 308 value = self._display.readCursor(sample) 309 # Null value indicates display was probably closed 310 if value: 311 return value 312 except IOError, error: 313 pass 314 # This error can occur if image display was closed. 315 # If a new display has been started then closing and 316 # reopening the connection will fix it. If that 317 # fails then give up. 318 self.open() 319 return self._display.readCursor(sample)
320 321 322 _display = ImageDisplayProxy() 323 324 # create aliases for _display methods 325 326 readCursor = _display.readCursor 327 open = _display.open 328 close = _display.close 329