1
2 """
3 IRAF GKI interpreter -- abstract implementation
4
5 The main classes here are GkiKernel and GkiController.
6
7 GkiKernel is the base class for graphics kernel implementations. Methods:
8
9 control() append()
10 Called by irafexecute to plot IRAF GKI metacode.
11 pushStdio() popStdio() getStdin/out/err()
12 Hooks to allow text I/O to special graphics devices, e.g. the
13 status line.
14 flush()
15 Flush graphics. May print or write a file for hardcopy devices.
16 clearReturnData()
17 Empty out return data buffer.
18 gcur()
19 Activate interactive graphics and return key pressed, position, etc.
20 redrawOriginal()
21 Redraw graphics without any annotations, overlays, etc.
22 undoN()
23 Allows annotations etc. to be removed.
24
25 Classes that implement a kernel provide methods named gki_* and control_*
26 which are called by the translate and control methods using dispatch
27 tables (functionTable and controlFunctionTable). The complete lists
28 of methods are in opcode2name and control2name. Python introspection
29 is used to determine which methods are implemented; it is OK for
30 unused methods to be omitted.
31
32 GkiProxy is a GkiKernel proxy class that implements the GkiKernel
33 interface and allows switching between GkiKernel objects (effectively
34 allowing the kernel type to change.)
35
36 GkiController is a GkiProxy that allows switching between different
37 graphics kernels as directed by commands embedded in the metacode stream.
38
39 $Id: gki.py 1463 2011-06-24 22:58:30Z stsci_embray $
40 """
41 from __future__ import division
42
43 import numpy
44 from types import *
45 import os, sys, string, re
46 import wutil, graphcap, iraf
47 import fontdata
48 from textattrib import *
49 import irafgwcs
50
51 nIrafColors = 16
52
53 BOI = -1
54 NOP = 0
55 GKI_MAX = 32767
56 GKI_MAX_FLOAT = float(GKI_MAX)
57 NDC_MAX = GKI_MAX_FLOAT/(GKI_MAX_FLOAT+1)
58 GKI_MAX_OP_CODE = 27
59 GKI_FLOAT_FACTOR = 100.
60 MAX_ERROR_COUNT = 7
61
62
63
64 GKI_EOF = 0
65 GKI_OPENWS = 1
66 GKI_CLOSEWS = 2
67 GKI_REACTIVATEWS = 3
68 GKI_DEACTIVATEWS = 4
69 GKI_MFTITLE = 5
70 GKI_CLEARWS = 6
71 GKI_CANCEL = 7
72 GKI_FLUSH = 8
73 GKI_POLYLINE = 9
74 GKI_POLYMARKER = 10
75 GKI_TEXT = 11
76 GKI_FILLAREA = 12
77 GKI_PUTCELLARRAY = 13
78 GKI_SETCURSOR = 14
79 GKI_PLSET = 15
80 GKI_PMSET = 16
81 GKI_TXSET = 17
82 GKI_FASET = 18
83 GKI_GETCURSOR = 19
84 GKI_GETCELLARRAY = 20
85 GKI_ESCAPE = 25
86 GKI_SETWCS = 26
87 GKI_GETWCS = 27
88
89 GKI_ILLEGAL_LIST = (21,22,23,24)
90
91 CONTROL_OPENWS = 1
92 CONTROL_CLOSEWS = 2
93 CONTROL_REACTIVATEWS = 3
94 CONTROL_DEACTIVATEWS = 4
95 CONTROL_CLEARWS = 6
96 CONTROL_SETWCS = 26
97 CONTROL_GETWCS = 27
98
99
100
101
102
103
104 opcode2name = {}
105 control2name = {}
106 for i in range(GKI_MAX_OP_CODE+1):
107 opcode2name[i] = 'gki_unknown'
108 control2name[i] = 'control_unknown'
109
110 opcode2name.update({
111 GKI_EOF: 'gki_eof',
112 GKI_OPENWS: 'gki_openws',
113 GKI_CLOSEWS: 'gki_closews',
114 GKI_REACTIVATEWS: 'gki_reactivatews',
115 GKI_DEACTIVATEWS: 'gki_deactivatews',
116 GKI_MFTITLE: 'gki_mftitle',
117 GKI_CLEARWS: 'gki_clearws',
118 GKI_CANCEL: 'gki_cancel',
119 GKI_FLUSH: 'gki_flush',
120 GKI_POLYLINE: 'gki_polyline',
121 GKI_POLYMARKER: 'gki_polymarker',
122 GKI_TEXT: 'gki_text',
123 GKI_FILLAREA: 'gki_fillarea',
124 GKI_PUTCELLARRAY: 'gki_putcellarray',
125 GKI_SETCURSOR: 'gki_setcursor',
126 GKI_PLSET: 'gki_plset',
127 GKI_PMSET: 'gki_pmset',
128 GKI_TXSET: 'gki_txset',
129 GKI_FASET: 'gki_faset',
130 GKI_GETCURSOR: 'gki_getcursor',
131 GKI_GETCELLARRAY: 'gki_getcellarray',
132 GKI_ESCAPE: 'gki_escape',
133 GKI_SETWCS: 'gki_setwcs',
134 GKI_GETWCS: 'gki_getwcs',
135 })
136
137
138
139 control2name.update({
140 CONTROL_OPENWS: 'control_openws',
141 CONTROL_CLOSEWS: 'control_closews',
142 CONTROL_REACTIVATEWS: 'control_reactivatews',
143 CONTROL_DEACTIVATEWS: 'control_deactivatews',
144 CONTROL_CLEARWS: 'control_clearws',
145 CONTROL_SETWCS: 'control_setwcs',
146 CONTROL_GETWCS: 'control_getwcs',
147 })
148
149 standardWarning = """
150 The graphics kernel for IRAF tasks has just received a metacode
151 instruction (%s) it never expected to see. Please inform the
152 STSDAS group of this occurrence."""
153
154 standardNotImplemented = \
155 """This IRAF task requires a graphics kernel facility not implemented
156 in the Pyraf graphics kernel (%s)."""
157
159 """Keeps track of where undoable appends are made so they can be
160 removed from the buffer on request. All it needs to know is
161 how much as been added to the metacode stream for each edit.
162 Since the process may add more gki, we distinguish specific edits
163 with a marker, and those are used when undoing."""
164
165 - def __init__(self):
167
168 - def add(self, size, undomarker=0):
169 self.editinfo.append((undomarker,size))
170
172 count = 0
173 for undomarker,size in self.editinfo:
174 if undomarker:
175 count = count+1
176 return count
177
178 - def popLastSize(self):
179 tsize = 0
180 while len(self.editinfo) > 0:
181 marker, size = self.editinfo.pop()
182 tsize = tsize + size
183 if marker: break
184 return tsize
185
187 """Split edit buffer at metacode length n. Modifies this buffer
188 to stop at n and returns a new EditHistory object with any
189 edits beyond n."""
190 newEditHistory = EditHistory()
191 tsize = 0
192 for i in xrange(len(self.editinfo)):
193 marker, size = self.editinfo[i]
194 tsize = tsize + size
195 if tsize >= n:
196 break
197 else:
198
199 return newEditHistory
200 newEditHistory.editinfo = self.editinfo[i+1:]
201 self.editinfo = self.editinfo[:i+1]
202 if tsize != n:
203
204 newEditHistory.editinfo.insert(0, (marker, tsize-n))
205 self.editinfo[i] = (marker, n-(tsize-size))
206 return newEditHistory
207
209 """Return copy of numpy array a"""
210 return numpy.array(a, copy=1)
211
212
213 _clearCodes = [
214 GKI_EOF,
215 GKI_OPENWS,
216 GKI_REACTIVATEWS,
217 GKI_CLEARWS,
218 GKI_CANCEL,
219 ]
220
221
222
224
225 """implement a buffer for gki which allocates memory in blocks so that
226 a new memory allocation is not needed everytime metacode is appended"""
227
228 INCREMENT = 50000
229
231
232 self.init(metacode)
233 self.redoBuffer = []
234
235 - def init(self, metacode=None):
236
237 """Initialize to empty buffer or to metacode"""
238
239 if metacode is not None:
240 self.buffer = metacode
241 self.bufferSize = len(metacode)
242 self.bufferEnd = len(metacode)
243 else:
244 self.buffer = numpy.zeros(0, numpy.int16)
245 self.bufferSize = 0
246 self.bufferEnd = 0
247 self.editHistory = EditHistory()
248 self.prepareToRedraw()
249
251
252 """Reset pointers in preparation for redraw"""
253
254
255
256
257
258 self.nextTranslate = 0
259
260
261 self.lastTranslate = 0
262 self.lastOpcode = None
263
264 - def reset(self, last=0):
265
266 """Discard everything up to end pointer
267
268 End is lastTranslate if last is true, else nextTranslate
269 """
270
271 if last:
272 end = self.lastTranslate
273 else:
274 end = self.nextTranslate
275 newEnd = self.bufferEnd - end
276 if newEnd > 0:
277 self.buffer[0:newEnd] = self.buffer[end:self.bufferEnd]
278 self.bufferEnd = newEnd
279 self.nextTranslate = self.nextTranslate-end
280 self.lastTranslate = 0
281 if not last:
282 self.lastOpcode = None
283 else:
284
285 self.init()
286
288
289 """Split this buffer at nextTranslate and return a new buffer
290 object with the rest of the metacode. lastOpcode may be
291 removed if it triggered the buffer split (so we can append
292 more metacode later if desired.)
293 """
294
295 tail = acopy(self.buffer[self.nextTranslate:self.bufferEnd])
296 if self.lastTranslate < self.nextTranslate and \
297 self.lastOpcode in _clearCodes:
298
299 self.bufferEnd = self.lastTranslate
300 self.nextTranslate = self.lastTranslate
301 else:
302
303 self.bufferEnd = self.nextTranslate
304
305 newbuffer = self.__class__(tail)
306 newbuffer.editHistory = self.editHistory.split(self.bufferEnd)
307 return newbuffer
308
309 - def append(self, metacode, isUndoable=0):
310
311 """Append metacode to buffer"""
312
313 if self.bufferSize < (self.bufferEnd + len(metacode)):
314
315 diff = self.bufferEnd + len(metacode) - self.bufferSize
316 nblocks = diff//self.INCREMENT + 1
317 self.bufferSize = self.bufferSize + nblocks * self.INCREMENT
318 newbuffer = numpy.zeros(self.bufferSize, numpy.int16)
319 if self.bufferEnd > 0:
320 newbuffer[0:self.bufferEnd] = self.buffer[0:self.bufferEnd]
321 self.buffer = newbuffer
322 self.buffer[self.bufferEnd:self.bufferEnd+len(metacode)] = metacode
323 self.bufferEnd = self.bufferEnd + len(metacode)
324 self.editHistory.add(len(metacode), isUndoable)
325
327
328 """Returns true if there is anything to undo on this plot"""
329
330 return (self.editHistory.NEdits() > 0)
331
332 - def undoN(self, nUndo=1):
333
334 """Undo last nUndo edits and replot. Returns true if plot changed."""
335
336 changed = 0
337 while nUndo>0:
338 size = self.editHistory.popLastSize()
339 if size == 0: break
340 self.bufferEnd = self.bufferEnd - size
341
342 self.redoBuffer.append(
343 acopy(self.buffer[self.bufferEnd:self.bufferEnd+size])
344 )
345 nUndo = nUndo-1
346 changed = 1
347 if changed:
348 if self.bufferEnd <= 0:
349 self.init()
350
351
352 self.nextTranslate = 0
353 self.lastTranslate = 0
354 return changed
355
357
358 """Returns true if there is anything to redo on this plot"""
359
360 return len(self.redoBuffer)>0
361
362 - def redoN(self, nRedo=1):
363
364 """Redo last nRedo edits and replot. Returns true if plot changed."""
365
366 changed = 0
367 while self.redoBuffer and nRedo>0:
368 code = self.redoBuffer.pop()
369 self.append(code, isUndoable=1)
370 nRedo = nRedo-1
371 changed = 1
372 return changed
373
375
376 """Return buffer contents (as numpy array, even if empty)"""
377
378 return self.buffer[0:self.bufferEnd]
379
381
382 """Return buffer up to end pointer, deleting those elements
383
384 End is lastTranslate if last is true, else nextTranslate
385 """
386
387 if last:
388 end = self.lastTranslate
389 else:
390 end = self.nextTranslate
391 b = acopy(self.buffer[:end])
392 self.reset(last)
393 return b
394
396
397 """Read next opcode and argument from buffer, returning a tuple
398 with (opcode, arg). Skips no-op codes and illegal codes.
399 Returns (None,None) on end of buffer or when opcode is truncated."""
400
401 ip = self.nextTranslate
402 lenMC = self.bufferEnd
403 buffer = self.buffer
404 while ip < lenMC:
405 if buffer[ip] == NOP:
406 ip = ip+1
407 elif buffer[ip] != BOI:
408 print "WARNING: missynched graphics data stream"
409
410 ip = ip + 1
411 while ip < lenMC:
412 if buffer[ip] == BOI: break
413 ip = ip + 1
414 else:
415
416 print "WARNING: unable to resynchronize in graphics data stream"
417 break
418 else:
419 if ip+2 >= lenMC: break
420 opcode = int(buffer[ip+1])
421 arglen = buffer[ip+2]
422 if (ip+arglen) > lenMC: break
423 self.lastTranslate = ip
424 self.lastOpcode = opcode
425 arg = buffer[ip+3:ip+arglen].astype(numpy.int)
426 ip = ip + arglen
427 if ((opcode < 0) or
428 (opcode > GKI_MAX_OP_CODE) or
429 (opcode in GKI_ILLEGAL_LIST)):
430 print "WARNING: Illegal graphics opcode = ",opcode
431 else:
432
433 self.nextTranslate = ip
434 return (opcode, arg)
435
436 self.nextTranslate = ip
437 return (None, None)
438
440 return self.bufferEnd
441
443 if i >= self.bufferEnd:
444 raise IndexError("buffer index out of range")
445 return self.buffer[i]
446
448 if j > self.bufferEnd: j = self.bufferEnd
449 return self.buffer[i:j]
450
451
452
454
455 """A fifo buffer used to queue up metacode to be returned to
456 the IRAF subprocess"""
457
458
459
463
467
468 - def put(self, metacode):
469
470 self.fifo[:0] = metacode
471
473
474 if len(self.fifo):
475 metacode = self.fifo.pop()
476 else:
477 raise Exception("Attempted read on empty gki input buffer")
478
479
480
481 tasknameStack = []
482
483
484
486
487 """Abstract class intended to be subclassed by implementations of GKI
488 kernels. This is to provide a standard interface to irafexecute"""
489
491
492
493 self.createFunctionTables()
494 self.returnData = None
495 self.errorMessageCount = 0
496 self.stdin = None
497 self.stdout = None
498 self.stderr = None
499 self._stdioStack = []
500 self.gkiPreferTtyIpc = None
501
502
503 self.gkibuffer = GkiBuffer()
504
506 """Getter. Return the attribute, set 1st if need be (lazy init)."""
507
508
509
510
511 if self.gkiPreferTtyIpc == None:
512 self.gkiPreferTtyIpc = iraf.envget('gkiprefertty','') == 'yes'
513 return self.gkiPreferTtyIpc
514
516
517 """Use Python introspection to create function tables"""
518
519 self.functionTable = [None]*(GKI_MAX_OP_CODE+1)
520 self.controlFunctionTable = [None]*(GKI_MAX_OP_CODE+1)
521
522
523 gkidict, classlist = {}, [self.__class__]
524 for c in classlist:
525 for b in c.__bases__:
526 classlist.append(b)
527 for name in c.__dict__.keys():
528 if name[:4] == "gki_" or name[:8] == "control_":
529 gkidict[name] = 0
530
531 for opcode, name in opcode2name.items():
532 if gkidict.has_key(name):
533 self.functionTable[opcode] = getattr(self, name)
534 gkidict[name] = 1
535
536 for opcode, name in control2name.items():
537 if gkidict.has_key(name):
538 self.controlFunctionTable[opcode] = getattr(self, name)
539 gkidict[name] = 1
540
541 badlist = []
542 for name, value in gkidict.items():
543 if not value:
544 badlist.append(name)
545 if badlist:
546 raise SyntaxError("Bug: error in definition of class %s\n"
547 "Special method name is incorrect: %s" %
548 (self.__class__.__name__, " ".join(badlist)))
549
551 gkiTranslate(gkiMetacode, self.controlFunctionTable)
552 return self.returnData
553
554 - def append(self, gkiMetacode, isUndoable=0):
561
563
564
565
566 gkiTranslate(gkiMetacode, self.functionTable)
567
569
570 if self.errorMessageCount < MAX_ERROR_COUNT:
571 print text
572 self.errorMessageCount = self.errorMessageCount + 1
573
575
576
577
578
579
580
581 return self.gkibuffer
582
585
587 self.gkibuffer.reset()
588
590 """Hook for stuff that needs to be done at start of task"""
591 pass
592
594 """Hook for stuff that needs to be done at completion of task"""
595 pass
596
598 """Hook for stuff that needs to be done right before imcur() call"""
599 pass
600
601 - def undoN(self, nUndo=1):
608
609 - def redoN(self, nRedo=1):
615
617 """Hook for things that need to be done before redraw from metacode"""
618 pass
619
631
633
634
635 self.returnData = None
636
638
639
640 raise EOFError("The specified graphics device is not interactive")
641
642
643
644 - def pushStdio(self, stdin=None, stdout=None, stderr=None):
645 """Push current stdio settings onto stack at set new values"""
646 self._stdioStack.append((self.stdin, self.stdout, self.stderr))
647 self.stdin = stdin
648 self.stdout = stdout
649 self.stderr = stderr
650
652 """Restore stdio settings from stack"""
653 if self._stdioStack:
654 self.stdin, self.stdout, self.stderr = self._stdioStack.pop()
655 else:
656 self.stdin, self.stdout, self.stderr = None, None, None
657
659
660
661 try:
662 if self.preferTtyIpc() and self.stdin and self.stdin.isatty():
663 return self.stdin
664 elif (not self.stdin) or \
665 (default and not default.isatty()):
666 return default
667 except AttributeError:
668 pass
669 return self.stdin
670
672
673
674 try:
675 if self.preferTtyIpc() and self.stdout and self.stdout.isatty():
676 return self.stdout
677 elif (not self.stdout) or \
678 (default and not default.isatty()):
679 return default
680 except AttributeError:
681 pass
682 return self.stdout
683
685
686
687 try:
688 if self.preferTtyIpc() and self.stderr and self.stderr.isatty():
689 return self.stderr
690 elif (not self.stderr) or \
691 (default and not default.isatty()):
692 return default
693 except AttributeError:
694 pass
695 return self.stderr
696
697
699
700 """General Function that can be used for decoding and interpreting
701 the GKI metacode stream. FunctionTable is a 28 element list containing
702 the functions to invoke for each opcode encountered. This table should
703 be different for each kernel that uses this function and the control
704 method.
705 This may be called with either a gkiBuffer or a simple numerical
706 array. If a gkiBuffer, it translates only the previously untranslated
707 part of the gkiBuffer and updates the nextTranslate pointer."""
708
709 if isinstance(metacode, GkiBuffer):
710 gkiBuffer = metacode
711 else:
712 gkiBuffer = GkiBuffer(metacode)
713
714 opcode, arg = gkiBuffer.getNextCode()
715 while opcode != None:
716 f = functionTable[opcode]
717 if f is not None:
718 apply(f,(arg,))
719
720 opcode, arg = gkiBuffer.getNextCode()
721
722
723
725
726 """implement a buffer for draw commands which allocates memory in blocks
727 so that a new memory allocation is not needed everytime functions are
728 appended"""
729
730 INCREMENT = 500
731
733
734 self.buffer = None
735 self.bufferSize = 0
736 self.bufferEnd = 0
737 self.nextTranslate = 0
738
740
741 return self.bufferEnd
742
744
745 """Discard everything up to nextTranslate pointer"""
746
747 newEnd = self.bufferEnd - self.nextTranslate
748 if newEnd > 0:
749 self.buffer[0:newEnd] = self.buffer[self.nextTranslate:self.bufferEnd]
750 self.bufferEnd = newEnd
751 else:
752 self.buffer = None
753 self.bufferSize = 0
754 self.bufferEnd = 0
755 self.nextTranslate = 0
756
758
759 """Append a single (function,args) tuple to the list"""
760
761 if self.bufferSize < self.bufferEnd + 1:
762
763 self.bufferSize = self.bufferSize + self.INCREMENT
764 newbuffer = self.bufferSize*[None]
765 if self.bufferEnd > 0:
766 newbuffer[0:self.bufferEnd] = self.buffer[0:self.bufferEnd]
767 self.buffer = newbuffer
768 self.buffer[self.bufferEnd] = funcargs
769 self.bufferEnd = self.bufferEnd + 1
770
772
773 """Get current contents of buffer
774
775 Note that this returns a view into the numpy array,
776 so if the return value is modified the buffer will change too.
777 """
778
779 if self.buffer:
780 return self.buffer[0:self.bufferEnd]
781 else:
782 return []
783
785
786 """Return tuples (function, args) with all new calls in buffer"""
787
788 ip = self.nextTranslate
789 if ip < self.bufferEnd:
790 self.nextTranslate = self.bufferEnd
791 return self.buffer[ip:self.bufferEnd]
792 else:
793 return []
794
795
796
797
799
800 """Base class for kernel proxy
801
802 stdgraph is an instance of a GkiKernel to which calls are deferred.
803 openKernel() method must be supplied to create a kernel and assign
804 it to stdgraph.
805 """
806
811
814
816 raise Exception("bug: do not use GkiProxy class directly")
817
818
819
820
824
828
829 - def undoN(self, nUndo=1):
832
836
840
844
848
852
853
854
855 - def pushStdio(self, stdin=None, stdout=None, stderr=None):
856 """Push current stdio settings onto stack at set new values"""
857 if self.stdgraph:
858 self.stdgraph.pushStdio(stdin,stdout,stderr)
859
860 self._stdioStack.append((self.stdin, self.stdout, self.stderr))
861 self.stdin = stdin
862 self.stdout = stdout
863 self.stderr = stderr
864
866 """Restore stdio settings from stack"""
867
868 if self.stdgraph:
869 self.stdgraph.popStdio()
870 if self._stdioStack:
871 self.stdin, self.stdout, self.stderr = self._stdioStack.pop()
872 else:
873 self.stdin, self.stdout, self.stderr = None, None, None
874
880
886
892
893 - def append(self, arg, isUndoable=0):
896
900
904
908
912
916
917
918
920
921 """Proxy that switches between interactive and other kernels
922
923 This can gracefully handle changes in kernels which can appear
924 in any open workstation instruction. It also uses lazy
925 instantiation of the real kernel (which can be expensive). In
926 one sense it is a factory class that will instantiate the
927 necessary kernels as they are requested.
928
929 Most external modules should access the gki functions through
930 an instance of this class, gki.kernel.
931 """
932
934
935 GkiProxy.__init__(self)
936 self.interactiveKernel = None
937 self.lastDevice = None
938 self.wcs = None
939
946
955
964
966
967 mode = arg[0]
968 device = arg[2:].astype(numpy.int8).tostring().strip()
969 self.openKernel(device)
970
1000
1002 """Starting with stdgraph, drill until a device is found in
1003 the graphcap or isn't"""
1004 if not device:
1005 device = iraf.envget("stdgraph","")
1006 graphcap = getGraphcap()
1007
1008 devstr = device
1009 tried = {devstr: None}
1010 while not graphcap.has_key(devstr):
1011 pdevstr = devstr
1012 devstr = iraf.envget(pdevstr,"")
1013 if not devstr:
1014 raise iraf.IrafError(
1015 "No entry found for specified stdgraph device `%s'" %
1016 device)
1017 elif tried.has_key(devstr):
1018
1019 s = [devstr]
1020 next = pdevstr
1021 while next and (next != devstr):
1022 s.append(next)
1023 next = tried[next]
1024 if next: s.append(next)
1025 s.reverse()
1026 raise iraf.IrafError(
1027 "Circular definition in graphcap for device\n%s"
1028 % ' -> '.join(s))
1029 else:
1030 tried[devstr] = pdevstr
1031 return devstr
1032
1033
1034
1036
1037 """A version of the graphics kernel that does nothing except warn the
1038 user that it does nothing. Used when graphics display isn't possible"""
1039
1041
1042 print "No graphics display available for this session."
1043 print "Graphics tasks that attempt to plot to an interactive " + \
1044 "screen will fail."
1045 GkiKernel.__init__(self)
1046 self.name = 'Null'
1047
1049 raise iraf.IrafError("Unable to plot graphics to screen")
1050
1052 raise iraf.IrafError("Attempt to access graphics when "
1053 "it isn't available")
1054
1056 raise iraf.IrafError("Attempt to access graphics when "
1057 "it isn't available")
1058
1059 - def translate(self, gkiMetacode, redraw=0):
1061
1062
1063
1065 """A graphics kernel whose only responsibility is to redirect
1066 metacode to a file-like object. Currently doesn't handle WCS
1067 get or set commands.
1068
1069 (This is needed for situations when you append to a graphics
1070 file - RIJ)"""
1071
1073
1074
1075 GkiKernel.__init__(self)
1076 self.filehandle = filehandle
1077 self.wcs = None
1078
1080
1081 self.filehandle.write(metacode.tostring())
1082
1083
1088
1090 if not self.wcs:
1091 self.wcs = irafgwcs.IrafGWcs()
1092 if self.returnData:
1093 self.returnData = self.returnData + self.wcs.pack()
1094 else:
1095 self.returnData = self.wcs.pack()
1096
1098
1100
1102
1103
1104
1106
1107 """Print metacode stream information"""
1108
1113
1121
1122 - def gki_eof(self, arg): print 'gki_eof'
1130 - def gki_flush(self, arg): print 'gki_flush'
1133 - def gki_text(self, arg): print 'gki_text'
1137 - def gki_plset(self, arg): print 'gki_plset'
1138 - def gki_pmset(self, arg): print 'gki_pmset'
1139 - def gki_txset(self, arg): print 'gki_txset'
1140 - def gki_faset(self, arg): print 'gki_faset'
1147
1148
1149
1150 graphcapDict = {}
1151
1159
1160
1161
1162
1188
1189
1190
1192
1193 """Holds configurable aspects of IRAF plotting behavior
1194
1195 This gets instantiated as a singleton instance so all windows
1196 can share the same configuration.
1197 """
1198
1200
1201
1202
1203
1204
1205
1206
1207 self.fontAspect = 42./27.
1208 self.fontMax2MinSizeRatio = 4.
1209
1210
1211 self.UnitFontHWindowFraction = 1./80
1212 self.UnitFontVWindowFraction = 1./45
1213
1214
1215 self.minUnitHFontSize = 5.
1216 self.minUnitVFontSize = self.minUnitHFontSize * self.fontAspect
1217
1218
1219 self.maxUnitHFontSize = \
1220 self.minUnitHFontSize * self.fontMax2MinSizeRatio
1221 self.maxUnitVFontSize = self.maxUnitHFontSize * self.fontAspect
1222
1223
1224
1225 self.vFontOffset = 0.0
1226 self.hFontOffset = 0.0
1227
1228
1229 self.isFixedAspectFont = 1
1230
1231
1232 self.defaultColors = [
1233 (0.,0.,0.),
1234 (1.,1.,1.),
1235 (1.,0.,0.),
1236 (0.,1.,0.),
1237 (0.,0.,1.),
1238 (0.,1.,1.),
1239 (1.,1.,0.),
1240 (1.,0.,1.),
1241 (1.,1.,1.),
1242
1243 (0.18,0.31,0.31),
1244 (1.,1.,1.),
1245 (1.,1.,1.),
1246 (1.,1.,1.),
1247 (1.,1.,1.),
1248 (1.,1.,1.),
1249 (1.,1.,1.),
1250 ]
1251 self.cursorColor = 2
1252 if len(self.defaultColors) != nIrafColors:
1253 raise ValueError("defaultColors should have %d elements (has %d)" % (nIrafColors, len(self.defaultColors)))
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1266 if not 0 <= color < len(self.defaultColors):
1267 raise ValueError("Bad cursor color (%d) should be >=0 and <%d" %
1268 (color, len(self.defaultColors)-1))
1269 self.cursorColor = color
1270
1272
1273 """Determine the unit font size for the given setup in pixels.
1274 The unit size refers to the horizonal size of fixed width characters
1275 (allow for proportionally sized fonts later?).
1276
1277 Basically, if font aspect is not fixed, the unit font size is
1278 proportional to the window dimension (for v and h independently),
1279 with the exception that if min or max pixel sizes are enabled,
1280 they are 'clipped' at the specified value. If font aspect is fixed,
1281 then the horizontal size is the driver if the window is higher than
1282 wide and vertical size for the converse.
1283 """
1284
1285 hwinsize = gwidget.winfo_width()
1286 vwinsize = gwidget.winfo_height()
1287 hsize = hwinsize * self.UnitFontHWindowFraction
1288 vsize = vwinsize * self.UnitFontVWindowFraction
1289 if self.minUnitHFontSize is not None:
1290 hsize = max(hsize,self.minUnitHFontSize)
1291 if self.minUnitVFontSize is not None:
1292 vsize = max(vsize,self.minUnitVFontSize)
1293 if self.maxUnitHFontSize is not None:
1294 hsize = min(hsize,self.maxUnitHFontSize)
1295 if self.maxUnitVFontSize is not None:
1296 vsize = min(vsize,self.maxUnitVFontSize)
1297 if not self.isFixedAspectFont:
1298 fontAspect = vsize/hsize
1299 else:
1300 hsize = min(hsize, vsize/self.fontAspect)
1301 vsize = hsize * self.fontAspect
1302 fontAspect = self.fontAspect
1303 return (hsize, fontAspect)
1304
1306
1307 return self.defaultColors
1308
1309
1310
1311 _irafGkiConfig = IrafGkiConfig()
1312
1313
1314
1316
1318
1319 self.patterns = [0x0000,0xFFFF,0x00FF,0x5555,0x33FF]
1320
1322
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334 self.patterns = [None]*7
1335
1336 p = numpy.zeros(128,numpy.int8)
1337 p[0:4] = [0x92,0x49,0x24,0x92]
1338 for i in xrange(31):
1339 p[(i+1)*4:(i+2)*4] = p[0:4]
1340 self.patterns[3] = p
1341
1342 p = numpy.zeros(128,numpy.int8)
1343 p[0:4] = [0xFF,0xFF,0xFF,0xFF]
1344 for i in xrange(10):
1345 p[(i+1)*12:(i+1)*12+4] = p[0:4]
1346 self.patterns[4] = p
1347
1348 p = numpy.zeros(128,numpy.int8)
1349 p[0:12] = [0x92,0x49,0x24,0x92,0x24,0x92,0x49,0x24,0x49,0x24,0x92,0x49]
1350 for i in xrange(9):
1351 p[(i+1)*12:(i+2)*12] = p[0:12]
1352 p[120:128] = p[0:8]
1353 self.patterns[5] = p
1354
1355 p = numpy.zeros(128,numpy.int8)
1356 p[0:12] = [0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24]
1357 for i in xrange(9):
1358 p[(i+1)*12:(i+2)*12] = p[0:12]
1359 p[120:128] = p[0:8]
1360 self.patterns[6] = p
1361
1363
1365
1366 self.linestyle = 1
1367 self.linewidth = 1.0
1368 self.color = 1
1369
1370 - def set(self, linestyle, linewidth, color):
1371
1372 self.linestyle = linestyle
1373 self.linewidth = linewidth
1374 self.color = color
1375
1377
1379
1380 self.fillstyle = 1
1381 self.color = 1
1382
1383 - def set(self, fillstyle, color):
1384
1385 self.fillstyle = fillstyle
1386 self.color = color
1387
1389
1391
1392
1393 self.color = 1
1394
1395 - def set(self, markertype, size, color):
1398
1399
1401
1402
1403
1404 - def __init__(self):
1405
1406 self.charUp = 90.
1407 self.charSize = 1.
1408 self.charSpace = 0.
1409 self.textPath = CHARPATH_RIGHT
1410 self.textHorizontalJust = JUSTIFIED_NORMAL
1411 self.textVerticalJust = JUSTIFIED_NORMAL
1412 self.textFont = FONT_ROMAN
1413 self.textQuality = FQUALITY_NORMAL
1414 self.textColor = 1
1415 self.font = fontdata.font1
1416
1417 self.hFontSize = None
1418 self.fontAspect = None
1419
1420 - def set(self,charUp=90., charSize=1.,charSpace=0.,
1421 textPath=CHARPATH_RIGHT, textHorizontalJust=JUSTIFIED_NORMAL,
1422 textVerticalJust=JUSTIFIED_NORMAL, textFont=FONT_ROMAN,
1423 textQuality=FQUALITY_NORMAL, textColor=1):
1424
1425 self.charUp = charUp
1426 self.charSize = charSize
1427 self.charSpace = charSpace
1428 self.textPath = textPath
1429 self.textHorizontalJust = textHorizontalJust
1430 self.textVerticalJust = textVerticalJust
1431 self.textFont = textFont
1432 self.textQuality = textQuality
1433 self.textColor = textColor
1434
1435
1436 - def setFontSize(self, win):
1437
1438 """Set the unit font size for a given window using the iraf
1439 configuration parameters contained in an attribute class"""
1440
1441 conf = win.irafGkiConfig
1442 self.hFontSize, self.fontAspect = conf.fontSize(win.gwidget)
1443
1444 - def getFontSize(self):
1445
1446 return self.hFontSize, self.fontAspect
1447
1448
1449
1451
1452 """Filter GUI messages out of stderr during plotting"""
1453
1454 pat = re.compile('\031[^\035]*\035\037')
1455
1457 self.fh = sys.stderr
1458
1463
1466
1469
1470
1471
1473
1477
1483
1484 - def read(self, n=0):
1485 """Return up to n bytes from status line
1486
1487 Reads only a single line. If n<=0, just returns the line.
1488 """
1489 s = self.readline()
1490 if n>0:
1491 return s[:n]
1492 else:
1493 return s
1494
1497
1499 self.status.update_idletasks()
1500
1504
1507
1508
1509
1510
1511
1514
1516 f = ndc(intarr)
1517 return f[0::2],f[1::2]
1518
1519
1520
1521
1522 import gwm, gkiiraf
1523
1524
1525
1526 kernel = GkiController()
1527
1528
1538