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

Source Code for Module pyraf.gki

   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  # beginning of instruction sentinel 
  54  NOP = 0   # no op value 
  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  # gki opcode constants 
  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  # Names of methods in GkiKernel that handle the various opcodes 
 100  # This also can be useful for debug prints of opcode values. 
 101   
 102  # Initial dictionaries with all opcodes unknown 
 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  # control channel opcodes 
 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   
158 -class EditHistory:
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):
166 self.editinfo = []
167
168 - def add(self, size, undomarker=0):
169 self.editinfo.append((undomarker,size))
170
171 - def NEdits(self):
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
186 - def split(self,n):
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 # looks like all edits stay here 199 return newEditHistory 200 newEditHistory.editinfo = self.editinfo[i+1:] 201 self.editinfo = self.editinfo[:i+1] 202 if tsize != n: 203 # split last edit 204 newEditHistory.editinfo.insert(0, (marker, tsize-n)) 205 self.editinfo[i] = (marker, n-(tsize-size)) 206 return newEditHistory
207
208 -def acopy(a):
209 """Return copy of numpy array a""" 210 return numpy.array(a, copy=1)
211 212 # GKI opcodes that clear the buffer 213 _clearCodes = [ 214 GKI_EOF, 215 GKI_OPENWS, 216 GKI_REACTIVATEWS, 217 GKI_CLEARWS, 218 GKI_CANCEL, 219 ] 220 221 #********************************************************************** 222
223 -class GkiBuffer:
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
230 - def __init__(self, metacode=None):
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
250 - def prepareToRedraw(self):
251 252 """Reset pointers in preparation for redraw""" 253 254 # nextTranslate is pointer to next element in buffer to be 255 # translated. It is needed because we may get truncated 256 # messages, leaving some metacode to be prepended to next 257 # message. 258 self.nextTranslate = 0 259 # lastTranslate points to beginning of last metacode translated, 260 # which may need to be removed if buffer is split 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 # complete reset so buffer can shrink sometimes 285 self.init()
286
287 - def split(self):
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 # discard last opcode, it cleared the page 299 self.bufferEnd = self.lastTranslate 300 self.nextTranslate = self.lastTranslate 301 else: 302 # retain last opcode 303 self.bufferEnd = self.nextTranslate 304 # return object of same class as this 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 # increment buffer size and copy into new array 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
326 - def isUndoable(self):
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 # add this chunk to end of buffer (use copy, not view) 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 # reset translation pointer to beginning so plot gets redone 351 # entirely 352 self.nextTranslate = 0 353 self.lastTranslate = 0 354 return changed
355
356 - def isRedoable(self):
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
374 - def get(self):
375 376 """Return buffer contents (as numpy array, even if empty)""" 377 378 return self.buffer[0:self.bufferEnd]
379
380 - def delget(self, last=0):
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
395 - def getNextCode(self):
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 # find next possible beginning of instruction 410 ip = ip + 1 411 while ip < lenMC: 412 if buffer[ip] == BOI: break 413 ip = ip + 1 414 else: 415 # Unable to resync 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 # normal return 433 self.nextTranslate = ip 434 return (opcode, arg) 435 # end-of-buffer return 436 self.nextTranslate = ip 437 return (None, None)
438
439 - def __len__(self):
440 return self.bufferEnd
441
442 - def __getitem__(self,i):
443 if i >= self.bufferEnd: 444 raise IndexError("buffer index out of range") 445 return self.buffer[i]
446
447 - def __getslice__(self,i,j):
448 if j > self.bufferEnd: j = self.bufferEnd 449 return self.buffer[i:j]
450 451 #********************************************************************** 452
453 -class GkiReturnBuffer:
454 455 """A fifo buffer used to queue up metacode to be returned to 456 the IRAF subprocess""" 457 458 # Only needed for getcursor and getcellarray, neither of which are 459 # currently implemented.
460 - def __init__(self):
461 462 self.fifo = []
463
464 - def reset(self):
465 466 self.fifo = []
467
468 - def put(self, metacode):
469 470 self.fifo[:0] = metacode
471
472 - def get(self):
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 # stack of active IRAF tasks, used to identify source of plot 481 tasknameStack = [] 482 483 #********************************************************************** 484
485 -class GkiKernel:
486 487 """Abstract class intended to be subclassed by implementations of GKI 488 kernels. This is to provide a standard interface to irafexecute""" 489
490 - def __init__(self):
491 492 # Basics needed for all instances 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 # see notes in the getter 501 # no harm in allocating gkibuffer, doesn't actually allocate 502 # space unless appended to. 503 self.gkibuffer = GkiBuffer()
504
505 - def preferTtyIpc(self):
506 """Getter. Return the attribute, set 1st if need be (lazy init).""" 507 # Allow users to set the behavior of redirection choices 508 # for special uses of PyRAF (e.g. embedded in other GUI's). Do not 509 # set this without knowing what you are doing - it breaks some commonly 510 # used command-line redirection within PyRAF. (thus default = False) 511 if self.gkiPreferTtyIpc == None: 512 self.gkiPreferTtyIpc = iraf.envget('gkiprefertty','') == 'yes' 513 return self.gkiPreferTtyIpc
514
515 - def createFunctionTables(self):
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 # to protect against typos, make list of all gki_ & control_ methods 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 # now loop over all methods that might be present 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 # do same for control methods 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 # did we use all the gkidict methods? 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
550 - def control(self, gkiMetacode):
551 gkiTranslate(gkiMetacode, self.controlFunctionTable) 552 return self.returnData
553
554 - def append(self, gkiMetacode, isUndoable=0):
555 556 # append metacode to the buffer 557 buffer = self.getBuffer() 558 buffer.append(gkiMetacode, isUndoable) 559 # translate and display the metacode 560 self.translate(buffer,0)
561
562 - def translate(self, gkiMetacode, redraw=0):
563 # Note, during the perf. testing of #122 it was noticed that this 564 # doesn't seem to get called; should be by self.append/undoN/redoN 565 # (looks to be hidden in subclasses, by GkiInteractiveTkBase.translate) 566 gkiTranslate(gkiMetacode, self.functionTable)
567
568 - def errorMessage(self, text):
569 570 if self.errorMessageCount < MAX_ERROR_COUNT: 571 print text 572 self.errorMessageCount = self.errorMessageCount + 1
573
574 - def getBuffer(self):
575 576 # Normally, the buffer will be an attribute of the kernel, but 577 # in some cases some kernels need more than one instance (interactive 578 # graphics for example). In those cases, this method may be 579 # overridden and the buffer will actually reside elsewhere 580 581 return self.gkibuffer
582
583 - def flush(self):
584 pass
585
586 - def clear(self):
587 self.gkibuffer.reset()
588
589 - def taskStart(self, name):
590 """Hook for stuff that needs to be done at start of task""" 591 pass
592
593 - def taskDone(self, name):
594 """Hook for stuff that needs to be done at completion of task""" 595 pass
596
597 - def pre_imcur(self):
598 """Hook for stuff that needs to be done right before imcur() call""" 599 pass
600
601 - def undoN(self, nUndo=1):
602 603 # Remove the last nUndo interactive appends to the metacode buffer 604 buffer = self.getBuffer() 605 if buffer.undoN(nUndo): 606 self.prepareToRedraw() 607 self.translate(buffer,1)
608
609 - def redoN(self, nRedo=1):
610 611 # Redo the last nRedo edits to the metacode buffer 612 buffer = self.getBuffer() 613 if buffer.redoN(nRedo): 614 self.translate(buffer,1)
615
616 - def prepareToRedraw(self):
617 """Hook for things that need to be done before redraw from metacode""" 618 pass
619
620 - def redrawOriginal(self):
621 622 buffer = self.getBuffer() 623 nUndo = buffer.editHistory.NEdits() 624 if nUndo: 625 self.undoN(nUndo) 626 else: 627 # just redraw it 628 buffer.prepareToRedraw() 629 self.prepareToRedraw() 630 self.translate(buffer,1)
631
632 - def clearReturnData(self):
633 634 # intended to be called after return data is used by the client 635 self.returnData = None
636
637 - def gcur(self):
638 # a default gcur routine to handle all the kernels that aren't 639 # interactive 640 raise EOFError("The specified graphics device is not interactive")
641 642 # some special routines for getting and setting stdin/out/err attributes 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
651 - def popStdio(self):
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
658 - def getStdin(self, default=None):
659 # return our own or the default, depending on what is defined 660 # and what is a tty 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 # OK if isatty is missing 669 return self.stdin
670
671 - def getStdout(self, default=None):
672 # return our own or the default, depending on what is defined 673 # and what is a tty 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 # OK if isatty is missing 682 return self.stdout
683
684 - def getStderr(self, default=None):
685 # return our own or the default, depending on what is defined 686 # and what is a tty 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 # OK if isatty is missing 695 return self.stderr
696 697 #**********************************************************************
698 -def gkiTranslate(metacode, functionTable):
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 # ! DEBUG ! timer("in gkiTranslate, for: "+opcode2name[opcode]) # good dbg spot 720 opcode, arg = gkiBuffer.getNextCode()
721 722 #********************************************************************** 723
724 -class DrawBuffer:
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
732 - def __init__(self):
733 734 self.buffer = None 735 self.bufferSize = 0 736 self.bufferEnd = 0 737 self.nextTranslate = 0
738
739 - def __len__(self):
740 741 return self.bufferEnd
742
743 - def reset(self):
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
757 - def append(self, funcargs):
758 759 """Append a single (function,args) tuple to the list""" 760 761 if self.bufferSize < self.bufferEnd + 1: 762 # increment buffer size and copy into new array 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
771 - def get(self):
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
784 - def getNewCalls(self):
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
798 -class GkiProxy(GkiKernel):
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
807 - def __init__(self):
808 809 GkiKernel.__init__(self) 810 self.stdgraph = None
811
812 - def __del__(self):
813 self.flush()
814
815 - def openKernel(self):
816 raise Exception("bug: do not use GkiProxy class directly")
817 818 # methods simply defer to stdgraph 819 # some create kernel and some simply return if no kernel is defined 820
821 - def errorMessage(self, text):
822 if not self.stdgraph: self.openKernel() 823 return self.stdgraph.errorMessage(text)
824
825 - def getBuffer(self):
826 if not self.stdgraph: self.openKernel() 827 return self.stdgraph.getBuffer()
828
829 - def undoN(self, nUndo=1):
830 if not self.stdgraph: self.openKernel() 831 return self.stdgraph.undoN(nUndo)
832
833 - def prepareToRedraw(self):
834 if self.stdgraph: 835 return self.stdgraph.prepareToRedraw()
836
837 - def redrawOriginal(self):
838 if not self.stdgraph: self.openKernel() 839 return self.stdgraph.redrawOriginal()
840
841 - def translate(self, gkiMetacode, redraw=0):
842 if not self.stdgraph: self.openKernel() 843 return self.stdgraph.translate(gkiMetacode,redraw)
844
845 - def clearReturnData(self):
846 if not self.stdgraph: self.openKernel() 847 return self.stdgraph.clearReturnData()
848
849 - def gcur(self):
850 if not self.stdgraph: self.openKernel() 851 return self.stdgraph.gcur()
852 853 # keep both local and stdgraph stdin/out/err up-to-date 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 #XXX still need some work here? 860 self._stdioStack.append((self.stdin, self.stdout, self.stderr)) 861 self.stdin = stdin 862 self.stdout = stdout 863 self.stderr = stderr
864
865 - def popStdio(self):
866 """Restore stdio settings from stack""" 867 #XXX still need some work here? 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
875 - def getStdin(self, default=None):
876 if self.stdgraph: 877 return self.stdgraph.getStdin(default) 878 else: 879 return GkiKernel.getStdin(self, default)
880
881 - def getStdout(self, default=None):
882 if self.stdgraph: 883 return self.stdgraph.getStdout(default) 884 else: 885 return GkiKernel.getStdout(self, default)
886
887 - def getStderr(self, default=None):
888 if self.stdgraph: 889 return self.stdgraph.getStderr(default) 890 else: 891 return GkiKernel.getStderr(self, default)
892
893 - def append(self, arg, isUndoable=0):
894 if self.stdgraph: 895 self.stdgraph.append(arg,isUndoable)
896
897 - def control(self, gkiMetacode):
898 if not self.stdgraph: self.openKernel() 899 return self.stdgraph.control(gkiMetacode)
900
901 - def flush(self):
902 if self.stdgraph: 903 self.stdgraph.flush()
904
905 - def clear(self):
906 if self.stdgraph: 907 self.stdgraph.clear()
908
909 - def taskStart(self, name):
910 if self.stdgraph: 911 self.stdgraph.taskStart(name)
912
913 - def taskDone(self, name):
914 if self.stdgraph: 915 self.stdgraph.taskDone(name)
916 917 #********************************************************************** 918
919 -class GkiController(GkiProxy):
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
933 - def __init__(self):
934 935 GkiProxy.__init__(self) 936 self.interactiveKernel = None 937 self.lastDevice = None 938 self.wcs = None
939
940 - def taskStart(self, name):
941 942 # GkiController manages the tasknameStack 943 tasknameStack.append(name) 944 if self.stdgraph: 945 self.stdgraph.taskStart(name)
946
947 - def taskDone(self, name):
948 949 # delete name from stack; pop until we find it if necessary 950 while tasknameStack: 951 lastname = tasknameStack.pop() 952 if lastname == name: break 953 if self.stdgraph: 954 self.stdgraph.taskDone(name)
955
956 - def control(self, gkiMetacode):
957 958 # some control functions get executed here because they can 959 # change the kernel 960 gkiTranslate(gkiMetacode, self.controlFunctionTable) 961 # rest of control is handled by the kernel 962 if not self.stdgraph: self.openKernel() 963 return self.stdgraph.control(gkiMetacode)
964
965 - def control_openws(self, arg):
966 967 mode = arg[0] 968 device = arg[2:].astype(numpy.int8).tostring().strip() 969 self.openKernel(device)
970
971 - def openKernel(self, device=None):
972 973 """Open kernel specified by device or by current value of stdgraph""" 974 device = self.getDevice(device) 975 graphcap = getGraphcap() 976 977 # In either of these 3 cases we want to create a new kernel. The last 978 # is the most complex, and it needs to be revisited (when the Device 979 # class is refactored) but suffice it to say we only want to compare 980 # the dict for the device, not the "master dict". 981 if None == self.lastDevice or \ 982 device != self.lastDevice or \ 983 graphcap[device].dict[device] != graphcap.get(self.lastDevice)[self.lastDevice]: 984 self.flush() 985 executable = graphcap[device]['kf'] 986 if executable == 'cl': 987 # open (persistent) interactive kernel 988 if not self.interactiveKernel: 989 if wutil.hasGraphics: 990 self.interactiveKernel = gwm.getGraphicsWindowManager() 991 else: 992 self.interactiveKernel = GkiNull() 993 self.stdgraph = self.interactiveKernel 994 else: 995 self.stdgraph = gkiiraf.GkiIrafKernel(device) 996 self.stdin = self.stdgraph.stdin 997 self.stdout = self.stdgraph.stdout 998 self.stderr = self.stdgraph.stderr 999 self.lastDevice = device
1000
1001 - def getDevice(self, device=None):
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 # protect against circular definitions 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 # track back through circular definition 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
1035 -class GkiNull(GkiKernel):
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
1040 - def __init__(self):
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
1048 - def control_openws(self, arg):
1049 raise iraf.IrafError("Unable to plot graphics to screen")
1050
1051 - def control_reactivatews(self, arg):
1052 raise iraf.IrafError("Attempt to access graphics when " 1053 "it isn't available")
1054
1055 - def control_getwcs(self, arg):
1056 raise iraf.IrafError("Attempt to access graphics when " 1057 "it isn't available")
1058
1059 - def translate(self, gkiMetacode, redraw=0):
1060 pass
1061 1062 #********************************************************************** 1063
1064 -class GkiRedirection(GkiKernel):
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
1072 - def __init__(self, filehandle):
1073 # Differs from all other constructors in that it takes a 1074 # file-like object as an argument. 1075 GkiKernel.__init__(self) 1076 self.filehandle = filehandle 1077 self.wcs = None
1078
1079 - def append(self, metacode):
1080 # Overloads the baseclass implementation. 1081 self.filehandle.write(metacode.tostring())
1082 1083 # control needs to get and set WCS data
1084 - def control_setwcs(self, arg):
1085 self.wcs = irafgwcs.IrafGWcs(arg) 1086 # Need to store this in the (persistent) kernel 1087 kernel.wcs = self.wcs
1088
1089 - def control_getwcs(self, arg):
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
1097 - def getStdin(self, default=None): return default
1098
1099 - def getStdout(self, default=None): return default
1100
1101 - def getStderr(self, default=None): return default
1102 1103 #********************************************************************** 1104
1105 -class GkiNoisy(GkiKernel):
1106 1107 """Print metacode stream information""" 1108
1109 - def __init__(self):
1110 1111 GkiKernel.__init__(self) 1112 self.name = 'Noisy'
1113
1114 - def control_openws(self, arg): print 'control_openws'
1115 - def control_closews(self, arg): print 'control_closews'
1116 - def control_reactivatews(self, arg): print 'control_reactivatews'
1117 - def control_deactivatews(self, arg): print 'control_deactivatews'
1118 - def control_clearws(self, arg): print 'control_clearws'
1119 - def control_setwcs(self, arg): print 'control_setwcs'
1120 - def control_getwcs(self, arg): print 'control_getwcs'
1121
1122 - def gki_eof(self, arg): print 'gki_eof'
1123 - def gki_openws(self, arg): print 'gki_openws'
1124 - def gki_closews(self, arg): print 'gki_closews'
1125 - def gki_reactivatews(self, arg): print 'gki_reactivatews'
1126 - def gki_deactivatews(self, arg): print 'gki_deactivatews'
1127 - def gki_mftitle(self, arg): print 'gki_mftitle'
1128 - def gki_clearws(self, arg): print 'gki_clearws'
1129 - def gki_cancel(self, arg): print 'gki_cancel'
1130 - def gki_flush(self, arg): print 'gki_flush'
1131 - def gki_polyline(self, arg): print 'gki_polyline'
1132 - def gki_polymarker(self, arg): print 'gki_polymarker'
1133 - def gki_text(self, arg): print 'gki_text'
1134 - def gki_fillarea(self, arg): print 'gki_fillarea'
1135 - def gki_putcellarray(self, arg): print 'gki_putcellarray'
1136 - def gki_setcursor(self, arg): print 'gki_setcursor'
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'
1141 - def gki_getcursor(self, arg): print 'gki_getcursor'
1142 - def gki_getcellarray(self, arg): print 'gki_getcellarray'
1143 - def gki_unknown(self, arg): print 'gki_unknown'
1144 - def gki_escape(self, arg): print 'gki_escape'
1145 - def gki_setwcs(self, arg): print 'gki_setwcs'
1146 - def gki_getwcs(self, arg): print 'gki_getwcs'
1147 1148 # Dictionary of all graphcap files known so far 1149 1150 graphcapDict = {} 1151
1152 -def getGraphcap(filename=None):
1153 """Get graphcap file from filename (or cached version if possible)""" 1154 if filename is None: 1155 filename = iraf.osfn(iraf.envget('graphcap','dev$graphcap')) 1156 if not graphcapDict.has_key(filename): 1157 graphcapDict[filename] = graphcap.GraphCap(filename) 1158 return graphcapDict[filename]
1159 1160 #XXX printPlot belongs in gwm, not gki? 1161 #XXX or maybe should be a method of gwm window manager 1162
1163 -def printPlot(window=None):
1164 1165 """Print contents of window (default active window) to stdplot 1166 1167 window must be a GkiKernel object (with a gkibuffer attribute.) 1168 """ 1169 1170 if window is None: 1171 window = gwm.getActiveGraphicsWindow() 1172 if window is None: return 1173 gkibuff = window.gkibuffer.get() 1174 if len(gkibuff): 1175 graphcap = getGraphcap() 1176 stdplot = iraf.envget('stdplot','') 1177 if not stdplot: 1178 msg = "No hardcopy device defined in stdplot" 1179 elif not graphcap.has_key(stdplot): 1180 msg = "Unknown hardcopy device stdplot=`%s'" % stdplot 1181 else: 1182 printer = gkiiraf.GkiIrafKernel(stdplot) 1183 printer.append(gkibuff) 1184 printer.flush() 1185 msg = "snap completed" 1186 stdout = kernel.getStdout(default=sys.stdout) 1187 stdout.write("%s\n" % msg)
1188 1189 #********************************************************************** 1190
1191 -class IrafGkiConfig:
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
1199 - def __init__(self):
1200 1201 # All set to constants for now, eventually allow setting other 1202 # values 1203 1204 # h = horizontal font dimension, v = vertical font dimension 1205 1206 # ratio of font height to width 1207 self.fontAspect = 42./27. 1208 self.fontMax2MinSizeRatio = 4. 1209 1210 # Empirical constants for font sizes 1211 self.UnitFontHWindowFraction = 1./80 1212 self.UnitFontVWindowFraction = 1./45 1213 1214 # minimum unit font size in pixels (set to None if not relevant) 1215 self.minUnitHFontSize = 5. 1216 self.minUnitVFontSize = self.minUnitHFontSize * self.fontAspect 1217 1218 # maximum unit font size in pixels (set to None if not relevant) 1219 self.maxUnitHFontSize = \ 1220 self.minUnitHFontSize * self.fontMax2MinSizeRatio 1221 self.maxUnitVFontSize = self.maxUnitHFontSize * self.fontAspect 1222 1223 # offset constants to match iraf's notion of where 0,0 is relative 1224 # to the coordinates of a character 1225 self.vFontOffset = 0.0 1226 self.hFontOffset = 0.0 1227 1228 # font sizing switch 1229 self.isFixedAspectFont = 1 1230 1231 # List of rgb tuples (0.0-1.0 range) for the default IRAF set of colors 1232 self.defaultColors = [ 1233 (0.,0.,0.), # black 1234 (1.,1.,1.), # white 1235 (1.,0.,0.), # red 1236 (0.,1.,0.), # green 1237 (0.,0.,1.), # blue 1238 (0.,1.,1.), # cyan 1239 (1.,1.,0.), # yellow 1240 (1.,0.,1.), # magenta 1241 (1.,1.,1.), # white 1242 # (0.32,0.32,0.32), # gray32 1243 (0.18,0.31,0.31), # IRAF blue-green 1244 (1.,1.,1.), # white 1245 (1.,1.,1.), # white 1246 (1.,1.,1.), # white 1247 (1.,1.,1.), # white 1248 (1.,1.,1.), # white 1249 (1.,1.,1.), # white 1250 ] 1251 self.cursorColor = 2 # red 1252 if len(self.defaultColors) != nIrafColors: 1253 raise ValueError("defaultColors should have %d elements (has %d)" % (nIrafColors, len(self.defaultColors)))
1254 1255 # old colors 1256 # (1.,0.5,0.), # coral 1257 # (0.7,0.19,0.38), # maroon 1258 # (1.,0.65,0.), # orange 1259 # (0.94,0.9,0.55), # khaki 1260 # (0.85,0.45,0.83), # orchid 1261 # (0.25,0.88,0.82), # turquoise 1262 # (0.91,0.53,0.92), # violet 1263 # (0.96,0.87,0.72) # wheat 1264
1265 - def setCursorColor(self, color):
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
1271 - def fontSize(self, gwidget):
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
1305 - def getIrafColors(self):
1306 1307 return self.defaultColors
1308 1309 # create the singleton instance 1310 1311 _irafGkiConfig = IrafGkiConfig() 1312 1313 #----------------------------------------------- 1314
1315 -class IrafLineStyles:
1316
1317 - def __init__(self):
1318 1319 self.patterns = [0x0000,0xFFFF,0x00FF,0x5555,0x33FF]
1320
1321 -class IrafHatchFills:
1322
1323 - def __init__(self):
1324 1325 # Each fill pattern is a 32x4 ubyte array (represented as 1-d). 1326 # These are computed on initialization rather than using a 1327 # 'data' type initialization since they are such simple patterns. 1328 # these arrays are stored in a pattern list. Pattern entries 1329 # 0-2 should never be used since they are not hatch patterns. 1330 1331 # so much for these, currently PyOpenGL does not support 1332 # glPolygonStipple()! But adding it probably is not too hard. 1333 1334 self.patterns = [None]*7 1335 # pattern 3, vertical stripes 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 # pattern 4, horizontal stripes 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 # pattern 5, close diagonal striping 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 # pattern 6, diagonal stripes the other way 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
1362 -class LineAttributes:
1363
1364 - def __init__(self):
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
1376 -class FillAttributes:
1377
1378 - def __init__(self):
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
1388 -class MarkerAttributes:
1389
1390 - def __init__(self):
1391 1392 # the first two attributes are not currently used in IRAF, so ditch'em 1393 self.color = 1
1394
1395 - def set(self, markertype, size, color):
1396 1397 self.color = color
1398 1399
1400 -class TextAttributes:
1401 1402 # Used as a structure definition basically, perhaps it should be made 1403 # more sophisticated.
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 # Place to keep font size and aspect for current window dimensions 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 # Place to keep font size and aspect for current window dimensions 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
1450 -class FilterStderr:
1451 1452 """Filter GUI messages out of stderr during plotting""" 1453 1454 pat = re.compile('\031[^\035]*\035\037') 1455
1456 - def __init__(self):
1457 self.fh = sys.stderr
1458
1459 - def write(self, text):
1460 # remove GUI junk 1461 edit = self.pat.sub('',text) 1462 if edit: self.fh.write(edit)
1463
1464 - def flush(self):
1465 self.fh.flush()
1466
1467 - def close(self):
1468 pass
1469 1470 #----------------------------------------------- 1471
1472 -class StatusLine:
1473
1474 - def __init__(self, status, name):
1475 self.status = status 1476 self.windowName = name
1477
1478 - def readline(self):
1479 """Shift focus to graphics, read line from status, restore focus""" 1480 wutil.focusController.setFocusTo(self.windowName) 1481 rv = self.status.readline() 1482 return rv
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
1495 - def write(self, text):
1496 self.status.updateIO(text=text.strip())
1497
1498 - def flush(self):
1499 self.status.update_idletasks()
1500
1501 - def close(self):
1502 # clear status line 1503 self.status.updateIO(text="")
1504
1505 - def isatty(self):
1506 return 1
1507 1508 #----------------------------------------------- 1509 1510 #******************************** 1511
1512 -def ndc(intarr):
1513 return intarr/(GKI_MAX_FLOAT+1)
1514
1515 -def ndcpairs(intarr):
1516 f = ndc(intarr) 1517 return f[0::2],f[1::2]
1518 1519 1520 # import these last so everything in this module is defined 1521 1522 import gwm, gkiiraf 1523 1524 # This is the proxy for the current graphics kernel 1525 1526 kernel = GkiController() 1527 1528 # Beware! This is highly experimental and was made only for a test case.
1529 -def _resetGraphicsKernel():
1530 global kernel 1531 if kernel: 1532 kernel.clearReturnData() 1533 kernel.flush() 1534 gwm.delete() 1535 kernel = None 1536 gwm._resetGraphicsWindowManager() 1537 kernel = GkiController()
1538