/usr/lib/python3/dist-packages/pyraf/GkiMpl.py is in python3-pyraf 2.1.14+dfsg-6.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 | """
matplotlib implementation of the gki kernel class
$Id$
"""
# confidence high
import math, sys, numpy, os
import tkinter as TKNTR
import matplotlib
# (done in mca file) matplotlib.use('TkAgg') # set backend
from matplotlib.lines import Line2D
from matplotlib.figure import Figure
from matplotlib.patches import Rectangle
from stsci.tools.for2to3 import ndarr2str
from . import gki, gkitkbase, textattrib
from . import gkigcur
from . import MplCanvasAdapter as mca
from .wutil import moveCursorTo
try:
import readline
except ImportError:
readline = None
# MPL version
MPL_MAJ_MIN = matplotlib.__version__.split('.') # tmp var
MPL_MAJ_MIN = float(MPL_MAJ_MIN[0]+'.'+MPL_MAJ_MIN[1])
# MPL linewidths seem to be thicker by default
GKI_TO_MPL_LINEWIDTH = 0.65
# GKI seems to use: 0: clear, 1: solid, 2: dash, 3: dot, 4: dot-dash, 5: ?
GKI_TO_MPL_LINESTYLE = ['None','-','--',':','-.','steps']
# Convert GKI alignment int values to MPL (idx 0 = default), 0 is invalid
GKI_TO_MPL_HALIGN = ['left','center','left','right',0,0,0,0]
GKI_TO_MPL_VALIGN = ['bottom','center',0,0,0,0,'top','bottom']
# "surface dev$pix" uses idx=5, though that's not allowed
GKI_TO_MPL_VALIGN[4]='top'
GKI_TO_MPL_VALIGN[5]='bottom'
# some text is coming out too high by about this much
GKI_TEXT_Y_OFFSET = 0.005
# marktype seems unused at present (most markers are polylines), but for
# future use, the GIO document lists:
# 'Acceptable choices are "point", "box", "plus", "cross", "circle" '
GKI_TO_MPL_MARKTYPE = ['.','s','+','x','o']
# Convert other GKI font attributes to MPL (cannot do bold italic?)
GKI_TO_MPL_FONTATTR = ['normal',1,2,3,4,5,6,7,'roman','greek','italic','bold',
'low','medium','high']
#-----------------------------------------------
class GkiMplKernel(gkitkbase.GkiInteractiveTkBase):
"""matplotlib graphics kernel implementation"""
def makeGWidget(self, width=600, height=420):
"""Make the graphics widget. Also perform some self init."""
self.__pf = TKNTR.Frame(self.top)
self.__pf.pack(side=TKNTR.TOP, fill=TKNTR.BOTH, expand=1)
self.__xsz = width
self.__ysz = height
ddd = 100
self.__fig = Figure(figsize=(self.__xsz/(1.*ddd),self.__ysz/(1.*ddd)),
dpi=ddd)
self.__fig.set_facecolor('k') # default to black
self.__mca = mca.MplCanvasAdapter(self, self.__fig, master=self.__pf)
self.__mca.pack(side=TKNTR.TOP, fill=TKNTR.BOTH, expand=1)
self.__mca.gwidgetize(width, height) # Add attrs to the gwidget
self.gwidget = self.__mca.get_tk_widget()
self.__normLines = [] # list of Line2D objs
self.__normPatches = [] # list of Patch objs
self.__extraHeightMax = 25
self.__firstPlotDone = 0
self.__skipPlotAppends = False # allow gki_ funcs to be reused
self.__allowDrawing = False # like taskStart, but can't rely on that
self._forceNextDraw = False
# self.__lastGkiCmds = (None, None, None) # unused for now
self.colorManager = tkColorManager(self.irafGkiConfig)
self.startNewPage()
self.__gcursorObject = gkigcur.Gcursor(self)
self.__mca.show() # or, could: self.gRedraw() with a .show()
# not currently using getAdjustedHeight because the background is drawn and
# it is not black (or the same color as the rest of the empty window)
def getAdjustedHeight(self):
""" Calculate an adjusted height to make the plot look better in the
widget's viewfield - otherwise the graphics are too close to
the top of the window. Use in place of self.__ysz"""
adjHt = self.__ysz - min(0.05*self.__ysz, self.__extraHeightMax)
return adjHt
def getTextPointSize(self, gkiTextScaleFactor, winWidth, winHeight):
""" Make a decision on the best font size (point) based on the
size of the graphics window and other factors """
# The default point size for the initial window size
dfltPtSz = 8.0
WIN_SZ_FACTOR = 300.0 # honestly just trying a number that looks good
# The contribution to the sizing from the window itself, could
# be taken from just the height, but that would leave odd font
# sizes in the very tall/thin windows. Another option is to average
# the w & h. We will try taking the minimum.
winSzContrib = min(winWidth, winHeight)
ptSz = dfltPtSz * (winSzContrib/WIN_SZ_FACTOR)
# The above gives us a proportionally sized font, but it can be larger
# than what we are used to with the standard gkitkplot, so trim down
# the large sizes.
if (ptSz > dfltPtSz): ptSz = (ptSz+dfltPtSz)/2.0
# Now that the best standard size for this window has been
# determined, apply the GKI text scale factor used to it (deflt: 1.0)
ptSz = ptSz*gkiTextScaleFactor
# leave as float (not N.float64), it'll get truncated by Text if needed
return float(ptSz)
def clearMplData(self):
""" Clear all lines, patches, text, etc. from the figure as well
as any of our own copies we may be keeping around to facilitate
redraws/resizes/etc. of the figure. """
self.__normLines = [] # clear our lines
self.__normPatches = [] # clear our patches
self.__fig.clear() # clear all from fig
def resizeGraphics(self, width, height):
""" It is time to set a magnitude to our currently normalized
lines, and send them to the figure. Here we assume that
__normLines & __normPatches are already fully populated. """
self.__fig.lines = [] # clear all old lines from figure
self.__fig.patches = [] # clear all old patches from figure
self.__xsz = width
self.__ysz = height
# scale each text item
for t in self.__fig.texts:
t.set_size(self.getTextPointSize(t.gkiTextSzFactor, width, height))
# scale each line, then apply it to the figure
for nrln in self.__normLines:
ll = Line2D([], [])
ll.update_from(nrln)
ll.set_data(nrln.get_xdata(True)*self.__xsz,
nrln.get_ydata(True)*self.__ysz)
self.__fig.lines.append(ll)
# scale each patch, then apply it to the figure
for nrpa in self.__normPatches:
rr = Rectangle((0,0),0,0)
rr.update_from(nrpa)
rr.set_x(nrpa.get_x()*self.__xsz)
rr.set_y(nrpa.get_y()*self.__ysz)
rr.set_width(nrpa.get_width()*self.__xsz)
rr.set_height(nrpa.get_height()*self.__ysz)
self.__fig.patches.append(rr)
# do not redraw here - we are called only to set the sizes
# done
def gcur(self):
"""Return cursor value after key is typed"""
# BUT, before we rush into sending that gcur obj back, this happens to
# be an excellent spot to redraw! If we are an interactive task (yes
# since they want a gcur) AND if we haven't been drawing because we
# are saving draws for performance-sake, then do so now.
if not self.__allowDrawing:
self.gki_flush(None, force=True)
# else, we have already been drawing, so no need to flush here
# Now, do what we came here for.
return self.__gcursorObject()
def gcurTerminate(self, msg='Window destroyed by user'):
"""Terminate active gcur and set EOF flag"""
if self.__gcursorObject.active:
self.__gcursorObject.eof = msg
# end the gcur mainloop -- this is what allows
# closing the window to act the same as EOF
self.top.quit()
def pre_imcur(self):
""" Override this so as to redraw if needed """
# Just like in gcur(), this may be an excellent time to redraw.
if not self.__allowDrawing:
self.gki_flush(None, force=True)
# else, we have already been drawing, so no need to flush here
def prePageSelect(self):
""" Override this so as to redraw when needed """
self._forceNextDraw = True # make sure to flush at end of redraw!
def forceNextDraw(self):
""" Override this so as to force a redraw at the next opportunity """
self._forceNextDraw = True
def taskStart(self, name):
""" Because of redirection, this is not always called on the first plot
but it should be called for every successive one. """
# We take this opportuninty to simply set our draw-saving flag.
self.__allowDrawing = False
# Note the task command given to be shown in the status widget
if readline != None:
self._toWriteAtNextClear = readline.get_history_item(
readline.get_current_history_length())
def taskDone(self, name):
"""Called when a task is finished"""
# This is the usual hack to prevent the double redraw after first
# Tk plot, but this version does not seem to suffer from the bug.
# self.doubleRedrawHack()
# No need to draw now, UNLESS we haven't yet
if not self.__allowDrawing:
# If we have not yet made our first draw, then we need to now.
# For interactive tasks, we should have drawn already (see gcur).
# For non-interactive tasks, we will not have drawn yet.
self.gki_flush(None, force=True)
# else, we have already been drawing, so no need to flush here
def update(self):
"""Update for all Tk events.
This should not be called unless necessary since it can
cause double redraws. It is used in the imcur task to
allow window resize (configure) events to be caught
while a task is running. Possibly it should be called
during long-running tasks too, but that will probably
lead to more extra redraws"""
# Hack to prevent the double redraw after first Tk plot
self.doubleRedrawHack()
self.top.update()
def doubleRedrawHack(self):
""" This is a hack to prevent the double redraw on first plots. """
# There is a mysterious Expose event that appears on the
# idle list, but not until the Tk loop actually becomes idle.
# The only approach that seems to work is to set this flag
# and to ignore the event.
# This is ugly but appears to work as far as I can tell.
if not self.__firstPlotDone:
self.__mca.ignoreNextRedraw = 1
self.__firstPlotDone = 1
def prepareToRedraw(self):
"""This is a hook for things that need to be done before the redraw
from metacode. We'll simply clear drawBuffer."""
self.drawBuffer.reset()
def getHistory(self):
"""Additional information for page history"""
return self.drawBuffer
def setHistory(self, info):
"""Restore using additional information from page history"""
self.drawBuffer = info
def startNewPage(self):
"""Setup for new page"""
self.drawBuffer = gki.DrawBuffer()
self.clearMplData()
def clearPage(self):
"""Clear buffer for new page"""
self.drawBuffer.reset()
self.clearMplData()
def isPageBlank(self):
"""Returns true if this page is blank"""
# or, could use: return len(self.drawBuffer) == 0
return len(self.__normLines) == 0 and \
len(self.__normPatches) == 0 and \
len(self.__fig.texts) == 0
# -----------------------------------------------
# Overrides of GkiInteractiveTkBase functions
def activate(self):
"""Not really needed for Tkplot widgets (used to set OpenGL win)"""
pass
# -----------------------------------------------
# GkiKernel implementation
def incrPlot(self):
"""Plot any new commands in the buffer"""
# This step slows us down but is needed, e.g. 'T' in implot.
# So, save it for ONLY after we have allowed drawing. This can
# seriously hinder performance in interactive tasks but at least
# we can avoid taking that hit until after gcur() is 1st called and
# we are waiting on a human. We *could* look into checking what the
# actual incr/change was and see if it is worth redrawing.
if self.gwidget and (self.__allowDrawing or self._forceNextDraw):
active = self.gwidget.isSWCursorActive()
if active:
self.gwidget.deactivateSWCursor()
# Render any new contents of draw buffer (this is costly).
self.__mca.draw() # in mpl v0.99, .draw() == .show()
# Do NOT add the logic here (as in redraw()) to loop through the
# drawBuffer func-arg pairs, calling apply(), using the
# __skipPlotAppends attr. Do not do so since the MPL kernel version
# keeps its own data cache and doesn't use the drawBuffer that way.
if active:
self.gwidget.activateSWCursor()
# reset this no matter what
self._forceNextDraw = False
# special methods that go into the function tables
def _plotAppend(self, plot_function, *args):
""" Append a 2-tuple (plot_function, args) to the draw buffer """
# Allow for this draw buffer append to be skipped at times
if not self.__skipPlotAppends:
self.drawBuffer.append((plot_function,args))
# Run _noteGkiCmd here as well, as almost all gki_* funcs call us
# self._noteGkiCmd(plot_function)
# def _noteGkiCmd(self, cmdFunc):
# """ Append the func to our history, but keep the len constant. """
# Track any and all gki commands - this is used for pattern watching
# in the gki stream, not for redraws. Track the func itself.
# Since we track everything, we can't just use the drawBuffer here.
# self.__lastGkiCmds = self.__lastGkiCmds[1:] + (cmdFunc,)
def gki_clearws(self, arg):
# don't put clearws command in the draw buffer, just clear the display
self.clear()
# clear the canvas
self.clearMplData()
self.__mca.draw()
# Note this explicitly (not for redrawing) since _plotAppend isn't used
# self._noteGkiCmd(self.gki_clearws)
def gki_cancel(self, arg):
self.gki_clearws(arg)
# Note this explicitly (not for redrawing) since _plotAppend isn't used
# self._noteGkiCmd(self.gki_cancel)
def gki_flush(self, arg, force=False):
""" Asked to render current plot immediately. Also used by redraw().
NOTE: This is called multiple times (~8) for a single prow
call. There is a performance improvement gained by skipping the
resize calculation between taskStart() and taskDone(). This class
adds the 'force' arg which forces it to redraw once whether we are
"saving draws" or not. """
# don't put flush command into the draw buffer
if self.__allowDrawing or force:
self.resizeGraphics(self.__xsz, self.__ysz) # do NOT use adjusted y!
self.__mca.draw()
self.__mca.flush()
# Note this explicitly (not for redrawing) since _plotAppend isn't used
# self._noteGkiCmd(self.gki_flush)
def gki_polyline(self, arg):
""" Instructed to draw a GKI polyline """
# record this operation as a tuple in the draw buffer
self._plotAppend(self.gki_polyline, arg)
# commit pending WCS changes when draw is found
self.wcs.commit()
# Reshape to get x's and y's
# arg[0] is the num pairs, so: len(arg)-1 == 2*arg[0]
verts = gki.ndc(arg[1:])
rshpd = verts.reshape(arg[0],2)
xs = rshpd[:,0]
ys = rshpd[:,1]
# Put the normalized data into a Line2D object, append to our list.
# Later we will scale it and append it to the fig. For the sake of
# performance, don't draw now, it slows things down.
# Note that for each object we make and store here (which is
# normalized), there will be a second (sized) copy of the same object
# created in resizeGraphics(). We could consider storing this data
# in some other way for speed, but perf. tests for #122 showed
# that this use of multiple object creation wasn't a big hit at all.
ll=Line2D(xs, ys,
linestyle=self.lineAttributes.linestyle,
linewidth=GKI_TO_MPL_LINEWIDTH*self.lineAttributes.linewidth,
color=self.lineAttributes.color)
self.__normLines.append(ll)
# While we are here and obviously getting drawing commands from the
# task, set our draw-saving flag. This covers the case of the
# interactive task during a redraw from a user-typed 'r'. That entire
# case goes: 1) no drawing during initial plot creation, 2) task is
# done giving us gki commands when gcur() seen, so drawing is allowed
# 3) user types 'r' or similar to cause a redraw so this turns off
# drawing again (to speed it up) until the next gcur() is seen.
self.__allowDrawing = False
def gki_polymarker(self, arg):
""" Instructed to draw a GKI polymarker. IRAF only implements
points for polymarker, so that makes it simple. """
# record this operation as a tuple in the draw buffer
self._plotAppend(self.gki_polymarker, arg)
# commit pending WCS changes when draw is found
self.wcs.commit()
# Reshape to get x's and y's
# arg[0] is the num pairs, so: len(arg)-1 == 2*arg[0]
verts = gki.ndc(arg[1:])
rshpd = verts.reshape(arg[0],2)
xs = rshpd[:,0]
ys = rshpd[:,1]
# put the normalized data into a Line2D object, append to our list
# later we will scale it and append it to the fig. See performance
# note in gki_polyline()
ll=Line2D(xs, ys, linestyle='', marker='.',
markersize=3.0, markeredgewidth=0.0,
markerfacecolor=self.markerAttributes.color,
color=self.markerAttributes.color)
self.__normLines.append(ll)
def calculateMplTextAngle(self, charUp, textPath):
""" From the given GKI charUp and textPath values, calculate the
rotation angle to be used for text. Oddly, it seems that textPath
and charUp both serve similar purposes, so we will have to look at
them both in order to figure the rotation angle. One might have
assumed that textPath could have meant "L to R" vs. "R to L", but
that does not seem to be the case - it seems to be rotation angle. """
# charUp range
if charUp < 0: charUp += 360.
charUp = math.fmod(charUp, 360.)
# get angle from textPath
angle = charUp+270. # deflt CHARPATH_RIGHT
if textPath == textattrib.CHARPATH_UP: angle = charUp
elif textPath == textattrib.CHARPATH_LEFT: angle = charUp+90.
elif textPath == textattrib.CHARPATH_DOWN: angle = charUp+180.
# return from 0-360
return math.fmod(angle,360.)
def gki_text(self, arg):
""" Instructed to draw some GKI text """
# record this operation as a tuple in the draw buffer
self._plotAppend(self.gki_text, arg)
# commit pending WCS changes
self.wcs.commit()
# add the text
x = gki.ndc(arg[0])
y = gki.ndc(arg[1])
text = ndarr2str(arg[3:].astype(numpy.int8))
ta = self.textAttributes
# For now, force this to be non-bold for decent looking plots. It
# seems (oddly) that IRAF/GKI tends to overuse boldness in graphics.
# A fix to mpl (near 0.91.2) makes bold text show as reeeeally bold.
# However, assume the user knows what they are doing with their
# settings if they have set a non-standard (1.0) charSize.
weight = 'normal'
if (MPL_MAJ_MIN < 0.91) or (abs(ta.charSize - 1.0) > .0001):
# only on these cases do we pay attention to 'bold' in textFont
if ta.textFont.find('bold') >= 0: weight = 'bold'
style = 'italic'
if ta.textFont.find('italic') < 0: style = 'normal'
# Calculate initial fontsize
fsz = self.getTextPointSize(ta.charSize, self.__xsz, self.__ysz)
# figure rotation angle
rot = self.calculateMplTextAngle(ta.charUp, ta.textPath)
# Kludge alert - only use the GKI_TEXT_Y_OFFSET in cases where
# we know the text is a simple level label (not in a contour, etc)
yOffset = 0.0
if abs(rot) < .0001 and ta.textHorizontalJust=='center':
yOffset = GKI_TEXT_Y_OFFSET
# Note that we add the text here in NDC (0.0-1.0) x,y and that
# the fig takes care of resizing for us.
t = self.__fig.text(x, y-yOffset, text, \
color=ta.textColor,
rotation=rot,
horizontalalignment=ta.textHorizontalJust,
verticalalignment=ta.textVerticalJust,
fontweight=weight, # [ 'normal' | 'bold' | ... ]
fontstyle=style, # [ 'normal' | 'italic' | 'oblique']
fontsize=fsz)
# To this Text object just created, we need to attach the GKI charSize
# scale factor, since we will need it later during a resize. Mpl
# knows nothing about this, but we support it for GKI.
t.gkiTextSzFactor = ta.charSize # add attribute
def gki_fillarea(self, arg):
""" Instructed to draw a GKI fillarea """
# record this operation as a tuple in the draw buffer
self._plotAppend(self.gki_fillarea, arg)
# commit pending WCS changes
self.wcs.commit()
# plot the fillarea
fa = self.fillAttributes
verts = gki.ndc(arg[1:])
# fillstyle 0=clear, 1=hollow, 2=solid, 3-6=hatch
# default case is 'solid' (fully filled solid color)
# 'hatch' case seems to be unused
ec = fa.color
fc = fa.color
fll = 1
if fa.fillstyle == 0: # 'clear' (fully filled with black)
ec = self.colorManager.setDrawingColor(0)
fc = ec
fll = 1
if fa.fillstyle == 1: # 'hollow' (just the rectangle line, empty)
ec = fa.color
fc = None
fll = 0
lowerleft = (verts[0], verts[1])
width = verts[4]-verts[0]
height = verts[5]-verts[1]
rr = Rectangle(lowerleft, width, height,
edgecolor=ec, facecolor=fc, fill=fll)
self.__normPatches.append(rr)
def gki_putcellarray(self, arg):
self.wcs.commit()
self.errorMessage(gki.standardNotImplemented % "GKI_PUTCELLARRAY")
def gki_setcursor(self, arg):
# record this operation as a tuple in the draw buffer
self._plotAppend(self.gki_setcursor, arg)
# get x and y
cursorNumber = arg[0]
x = gki.ndc(arg[1])
y = gki.ndc(arg[2])
# Update the sw cursor object (A clear example of why this update
# is needed is how 'apall' re-centers the cursor w/out changing y, when
# the user types 'r'; without this update, the two cursors separate.)
swCurObj = self.__mca.getSWCursor()
if swCurObj: swCurObj.moveTo(x, y, SWmove=1)
# wutil.moveCursorTo uses 0,0 <--> upper left, need to convert
sx = int( x * self.gwidget.winfo_width())
sy = int((1-y) * self.gwidget.winfo_height())
rx = self.gwidget.winfo_rootx()
ry = self.gwidget.winfo_rooty()
# call the wutil version to move the cursor
moveCursorTo(self.gwidget.winfo_id(), rx, ry, sx, sy)
def gki_plset(self, arg):
# record this operation as a tuple in the draw buffer
self._plotAppend(self.gki_plset, arg)
# Handle case where some terms (eg. xgterm) allow higher values,
# by looping over the possible visible patterns. (ticket #172)
arg0 = arg[0]
if arg0 >= len(GKI_TO_MPL_LINESTYLE):
num_visible = len(GKI_TO_MPL_LINESTYLE)-1
arg0 = 1 + (arg0 % num_visible)
# Note that GkiTkplotKernel saves color (arg[2]) in numeric format,
# but we keep it in the rgb strng form which mpl can readily use.
# Same note for linestyle, changed from number to mpl symbol.
self.lineAttributes.set(GKI_TO_MPL_LINESTYLE[arg0], # linestyle
arg[1]/gki.GKI_FLOAT_FACTOR, # linewidth
self.colorManager.setDrawingColor(arg[2]))
def gki_pmset(self, arg):
# record this operation as a tuple in the draw buffer
self._plotAppend(self.gki_pmset, arg)
# set attrs. See notes about GKI_TO_MPL_MARKTYPE
self.markerAttributes.set(0, #GKI_TO_MPL_MARKTYPE[arg[0]] ! type unused
0, #gki.ndc(arg[1]) ! size unused
self.colorManager.setDrawingColor(arg[2]))
def gki_txset(self, arg):
# record this operation as a tuple in the draw buffer
self._plotAppend(self.gki_txset, arg)
# Set text attrs
# charSize: To quote from tkplottext.py:
# "We draw the line at fontSizes less than 1/2! Get real."
# without the 0.5 floor, "contour dev$pix" ticklabels are too small
charUp = float(arg[0]) # default: 90.0
charSize = max(0.5, arg[1]/gki.GKI_FLOAT_FACTOR) # default: 1.0
charSpace = arg[2]/gki.GKI_FLOAT_FACTOR # unused yet (0.0)
textPath = arg[3] # leave as GKI code
# btw, in unit testsing never saw a case where textPath != 3
textHorizontalJust = GKI_TO_MPL_HALIGN[arg[4]]
textVerticalJust = GKI_TO_MPL_VALIGN[arg[5]]
textFont = GKI_TO_MPL_FONTATTR[arg[6]]
textQuality = GKI_TO_MPL_FONTATTR[arg[7]] # unused ? (lo,md,hi)
textColor = self.colorManager.setDrawingColor(arg[8])
self.textAttributes.set(charUp, charSize, charSpace,
textPath, textHorizontalJust, textVerticalJust, textFont,
textQuality, textColor)
def gki_faset(self, arg):
# record this operation as a tuple in the draw buffer
self._plotAppend(self.gki_faset, arg)
# set the fill attrs
self.fillAttributes.set(arg[0], # fillstyle
self.colorManager.setDrawingColor(arg[1]))
def gki_getcursor(self, arg):
raise NotImplementedError(gki.standardNotImplemented % "GKI_GETCURSOR")
def gki_getcellarray(self, arg):
raise NotImplementedError(gki.standardNotImplemented % "GKI_GETCELLARRAY")
def gki_unknown(self, arg):
self.errorMessage(gki.standardWarning % "GKI_UNKNOWN")
# Note this explicitly (not for redrawing) since _plotAppend isn't used
# self._noteGkiCmd(self.gki_unknown)
def gRedraw(self):
if self.gwidget: self.gwidget.tkRedraw()
def redraw(self, o=None):
"""Redraw for expose or resize events, also called when page menu is
used. This method generally should not be called directly -- call
gwidget.tkRedraw() instead since it does some other
preparations.
"""
# Argument o is not needed because we only get redraw
# events for our own gwidget.
#
# DESIGN NOTE: Make sure this is not getting called for window
# resizes! Using the drawBuffer is too slow and unnecessary. Resizes
# should only be hooking into resizeGraphics() for performance sake.
# See if we need to force the flush (of course wait until the end)
frc = self._forceNextDraw
self._forceNextDraw = False
# Clear the screen
self.clearMplData()
# Plot the current buffer
self.__skipPlotAppends = True
for (function, args) in self.drawBuffer.get():
function(*args)
self.__skipPlotAppends = False
self.gki_flush(None, force=frc) # does: resize-calc's; draw; flush
#-----------------------------------------------
class tkColorManager:
"""Encapsulates the details of setting the graphic's windows colors.
Needed since we may be using rgba mode or color index mode and we
do not want any of the graphics programs to have to deal with the
mode being used. The current design applies the same colors to all
graphics windows for color index mode (but it isn't required).
An 8-bit display depth results in color index mode, otherwise rgba
mode is used. If no new colors are available, we take what we can
get. We do not attempt to get a private colormap.
"""
def __init__(self, config):
self.config = config
self.rgbamode = 0
self.indexmap = len(self.config.defaultColors)*[None]
# call setColors to allocate colors after widget is created
def setColors(self, widget):
"""Not needed for Tkplot, a noop"""
pass
def setCursorColor(self, irafColorIndex=None):
"""Set crosshair cursor color to given index.
Only has an effect in index color mode."""
if irafColorIndex is not None:
self.config.setCursorColor(irafColorIndex)
def setDrawingColor(self, irafColorIndex):
"""Return the specified iraf color usable by tkinter"""
color = self.config.defaultColors[irafColorIndex]
red = int(255*color[0])
green = int(255*color[1])
blue = int(255*color[2])
return "#%02x%02x%02x" % (red,green,blue)
|