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
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'):
39 fcntl = None
40 else:
41 raise
42
43
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
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
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
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
127
128 """Interface to IRAF-compatible image display"""
129
130
131 _IIS_READ = 0100000
132 _IMC_SAMPLE = 0040000
133 _IMCURSOR = 020
134 _SZ_IMCURVAL = 160
135
137
138
139
140
141 self._inCursorMode = 0
142
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
161 return s.split("\n")[0]
162
164
165 """Write request to image display"""
166
167 a = numpy.array([tid,thingct,subunit,0,x,y,z,t], numpy.int16)
168
169 sum = numpy.add.reduce(a)
170 sum = 0xffff - (sum & 0xffff)
171 a[3] = sum
172 self._write(a.tostring())
173
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
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
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
216
217 """FIFO version of image display"""
218
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
233
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:
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
250
251 """Close image display connection"""
252
253 self._socket.close()
254
255
257
258 """INET socket version of image display"""
259
260 - def __init__(self, port, hostname=None):
263
264
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
274
275
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
295
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
310 if value:
311 return value
312 except IOError, error:
313 pass
314
315
316
317
318 self.open()
319 return self._display.readCursor(sample)
320
321
322 _display = ImageDisplayProxy()
323
324
325
326 readCursor = _display.readCursor
327 open = _display.open
328 close = _display.close
329