PLplot
5.10.0
|
00001 from Tkinter import * 00002 #from pl import * 00003 from plplot import * 00004 from TclSup import * 00005 00006 from string import * 00007 00008 from math import * 00009 00010 CMD='cmd' 00011 00012 # Physically imported this function from Tkinter.py, b/c it evidently 00013 # isn't made available via the above import statement. 00014 00015 def _flatten(tuple): 00016 res = () 00017 for item in tuple: 00018 if type(item) in (TupleType, ListType): 00019 res = res + _flatten(item) 00020 elif item is not None: 00021 res = res + (item,) 00022 return res 00023 00024 #=============================================================================# 00025 # class Plframe 00026 #=============================================================================# 00027 00028 class Plframe(Widget): 00029 00030 def __init__( self, master=None, cnf={}, **kw ): 00031 """Constructor. 00032 00033 Initialize the Tk base class, and squirel away the stream id, 00034 so that we will be able to ensure that future draw requests 00035 show up in the right place.""" 00036 00037 Widget.__init__( self, master, 'plframe', cnf, kw ) 00038 00039 self.strm = plgstrm() 00040 00041 def cmd( s, *args ): 00042 "Invoke a subcommand on the plframe widget." 00043 apply( s.tk.call, (s._w, 'cmd',) + _flatten(args) ) 00044 00045 def info(s, what): 00046 return s.tk.call( s._w, 'info', what ) 00047 00048 ## Now implement the PLplot API. For simple functions, can call 00049 ## straight to Tk, which is probably the most straightforward way, in 00050 ## the sense of making the Python/Tk widget look and act most like 00051 ## it's older brother the Tcl version. However, functions which rely 00052 ## on the Numeric extensions cannot work this way, so for those we 00053 ## call straight to the Python compiled interface to the PLplot API. 00054 00055 def plenv( s, xmin, xmax, ymin, ymax, i, j ): 00056 s.cmd( 'plenv', xmin, xmax, ymin, ymax, i, j ) 00057 00058 def pleop(s): 00059 s.cmd( 'pleop' ) 00060 00061 def pllab( s, xlab, ylab, tlab ): 00062 s.cmd( 'pllab', xlab, ylab, tlab ) 00063 00064 def plline( s, x, y ): 00065 plsstrm( s.strm ) 00066 plline( x, y ) 00067 00068 def plpoin( s, xs, ys, mark ): 00069 plsstrm( s.strm ) 00070 plpoin( xs, ys, mark ) 00071 00072 #=============================================================================# 00073 # class PlXframe 00074 #=============================================================================# 00075 00076 class PlXframe(Frame): 00077 00078 """This class provides the same facilities as the Plframe, plus 00079 several additional facilities for advanced plot interaction, which 00080 are provided through menus and bindings.""" 00081 00082 def __init__( s, master=None, cnf={}, **kw ): 00083 """Constructor. 00084 00085 Configure the widget database, and then set up the internal 00086 members. Namely, a menubar and a contained Plframe.""" 00087 00088 Frame.__init__( s, master ) 00089 00090 s.setup_defaults() 00091 00092 # We will be using the grid geometry manager to pack a plframe 00093 # widget along with some additional addornments. The top row 00094 # (row 0) will contain the menubar. The next row (row 1) will 00095 # hold the actual plframe, as well as the vertical scrollbar 00096 # when present.. And the last row (row 2) will hold the 00097 # horizontal scrollbar when present. We want the menubar and 00098 # the horizontal scrollbar to use only the required space, and 00099 # we want the plframe to expand to fill any remaining space. 00100 # To accomplish this we set the weights for the top and bottom 00101 # row to zero, thus preventing them from expanding. 00102 00103 s.rowconfigure( 0, weight=0, minsize=0 ) 00104 s.rowconfigure( 1, weight=1, minsize=0 ) 00105 s.rowconfigure( 2, weight=0, minsize=0 ) 00106 00107 # Similarly for the columns, we will use the left column (col 00108 # 0) for the plframe and the horizontal scrollbar, and the 00109 # right column (col 1) for the vertical scrollbar. Moreover, the 00110 # menubar in the top row will span columns 0 and 1. Again, we 00111 # want the rightmost column (col 0) to stay at whatever is the 00112 # minimum width to display its contents, and want the plframe 00113 # in the leftmost column to grow to fill all space. 00114 00115 s.columnconfigure( 0, weight=1, minsize=0 ) 00116 s.columnconfigure( 1, weight=0, minsize=0 ) 00117 00118 # Okay, now get the actual plframe widget so we can display 00119 # it. And we pack it into its grid cell with all four sides 00120 # of the plframe sticking to the expandable size of the 00121 # enclosing grid cell, as arranged by the row,col weighting 00122 # scheme described above. 00123 00124 s.plf = Plframe( s, kw ) 00125 s.plf.grid( row=1, column=0, sticky=NSEW ) 00126 00127 s.build_menu_bar() 00128 00129 s.strm = plgstrm() 00130 00131 s.setup_bindings() 00132 00133 s.hscroll_exists = 0 00134 s.vscroll_exists = 0 00135 00136 ## All this stuff is being based heavily on the Pltkwin.tcl thing by 00137 ## Vince, for itcl/itk (which in turn was based heavily on the 00138 ## plwidgets.tcl stuff by Maurice). 00139 00140 def setup_defaults(s): 00141 #s.saveopt_dev = StringVar() 00142 s.saveopt_dev = "psc" 00143 #s.saveopt_dev.set( "psc" ) 00144 00145 #s.saveopt_file = IntVar() 00146 s.saveopt_file = 0 00147 #s.saveopt_file.set( 0 ) 00148 00149 # Now do the zoom support stuff. 00150 00151 s.zidx = 0 00152 s.zxl = [ 0. ]; s.zyl = [ 0. ] 00153 s.zxr = [ 1. ]; s.zyr = [ 1. ] 00154 00155 def setup_bindings(s): 00156 s.plf.bind( "<Any-KeyPress>", s.key_filter ) 00157 s.plf.bind( "<Any-ButtonPress>", s.user_mouse ) 00158 s.plf.bind( "<Any-Enter>", s.set_focus ) 00159 00160 def set_focus(s,e): 00161 # print "in set_focus" 00162 s.plf.focus() 00163 00164 def build_menu_bar(s): 00165 "Create the menubar at the top of the PlXframe" 00166 00167 s.ftop = Frame( s ) 00168 s.ftop.grid( row=0, columnspan=2, sticky='ew' ) 00169 00170 s.ftop.plot = Menubutton( s.ftop, text="Plot", underline=0, 00171 relief=RAISED ) 00172 s.ftop.plot.m = Menu( s.ftop.plot ) 00173 s.ftop.plot["menu"] = s.ftop.plot.m 00174 00175 s.ftop.plot.pack( side=LEFT ) 00176 00177 s.create_menu_print( s.ftop.plot.m ) 00178 s.create_menu_save( s.ftop.plot.m ) 00179 s.create_menu_orient( s.ftop.plot.m ) 00180 s.create_menu_zoom( s.ftop.plot.m ) 00181 s.create_menu_page( s.ftop.plot.m ) 00182 s.create_menu_options( s.ftop.plot.m ) 00183 s.create_menu_debug ( s.ftop.plot.m ) 00184 00185 s.ftop.eop = Button( s.ftop, text="Clear", 00186 command=s.clearpage ) 00187 s.ftop.eop.pack( side=RIGHT ) 00188 00189 s.ftop.lstat = Label( s.ftop, anchor=W, relief=RAISED ) 00190 s.ftop.lstat.pack( side=RIGHT, expand=1, fill=BOTH ) 00191 00192 def create_menu_print( s, m ): 00193 m.add( "command", label="Print", command=s.cmd_print ) 00194 00195 def create_menu_save( s, pmenu ): 00196 """Create the menu which lets us control the whole business of 00197 saving plots to disk.""" 00198 00199 m = Menu( pmenu ) 00200 pmenu.add( "cascade", label='Save', menu=m ) 00201 00202 # Save - As 00203 00204 m.add( "command", label="As", command=s.save_as ) 00205 00206 # Save - Again 00207 00208 m.add( "command", label="Again", command=s.save_again, 00209 state=DISABLED ) 00210 00211 # Save - Close 00212 00213 m.add( "command", label="Close", command=s.save_close, 00214 state=DISABLED ) 00215 00216 m.add( "separator" ) 00217 00218 # Save - Set device.. (another cascade) 00219 00220 m.sdev = Menu( m ) 00221 m.add( "cascade", label="Set device", menu=m.sdev ) 00222 00223 # Generate the device list in the "Save/Set device" widget 00224 # menu, by querying the plframe widget for the available 00225 # output devices (which are listed). 00226 00227 devnames = s.plf.info( "devnames" ) 00228 devkeys = s.plf.info( "devkeys" ) 00229 00230 # Oh no, these came back as Tcl lists. Have to convert them 00231 # to Python lists. 00232 00233 devnamlst = TclList2Py( devnames ) 00234 devkeylst = TclList2Py( devkeys ) 00235 00236 print "devnamlst = ", devnamlst 00237 print "devkeylst = ", devkeylst 00238 print "len(devnamlst) = ", len(devnamlst) 00239 00240 ## for i in range( len(devnamlst) ): 00241 ## devnam = devnamlst[i] 00242 ## devkey = devkeylst[i] 00243 ## 00244 ## m.sdev.add( "radio", label=devnam, variable=s.saveopt_dev, 00245 ## value=devkey ) 00246 00247 # Now get the ball rolling by preinvoking one of these. 00248 # Default to the first one, whatever it is, but use psc 00249 # (Postscript color), if we can find it in the list of 00250 # available devices. 00251 00252 ## ivk = 1 00253 ## for i in range( len(devnamlst) ): 00254 ## if devkeylst[i] == "psc": 00255 ## ivk = i+1 00256 ## break 00257 ## 00258 ## m.sdev.invoke( ivk ) 00259 00260 # Save - Set file type.. (another cascade) 00261 00262 m.sfile = Menu( m ) 00263 m.add( "cascade", label="Set file type", menu=m.sfile ) 00264 00265 m.sfile.add( "radio", label="Single file (one plot/file)", 00266 variable=s.saveopt_file, value=0 ) 00267 m.sfile.add( "radio", label="Archive file( many plots/file)", 00268 variable=s.saveopt_file, value=1 ) 00269 00270 m.sfile.invoke( 1 ) 00271 00272 def create_menu_orient( s, pmenu ): 00273 m = Menu( pmenu ) 00274 pmenu.add( "cascade", label='Orient', menu=m ) 00275 00276 m.config( postcommand=lambda o=s, x=m: o.update_orient(x) ) 00277 00278 # Orient - 0 degrees 00279 00280 m.add( 'radio', label="0 degrees", 00281 command=lambda o=s: o.orient(0) ) 00282 00283 # Orient - 90 degrees 00284 00285 m.add( 'radio', label="90 degrees", 00286 command=lambda o=s: o.orient(1) ) 00287 00288 # Orient - 180 degrees 00289 00290 m.add( 'radio', label="180 degrees", 00291 command=lambda o=s: o.orient(2) ) 00292 00293 # Orient - 270 degrees 00294 00295 m.add( 'radio', label="270 degrees", 00296 command=lambda o=s: o.orient(3) ) 00297 00298 def create_menu_zoom( s, pmenu ): 00299 m = Menu( pmenu ) 00300 pmenu.add( "cascade", label='Zoom', menu=m ) 00301 00302 m.config( postcommand=lambda o=s, x=m: o.update_zoom(x) ) 00303 00304 # Zoom - select (by mouse) 00305 00306 m.add( 'command', label="Select", 00307 command=lambda o=s: o.zoom_select() ) 00308 00309 # Zoom - back (go back 1 zoom level) 00310 00311 m.add( 'command', label="Back", 00312 command=lambda o=s: o.zoom_back(), 00313 state='disabled' ) 00314 00315 # Zoom - forward (go forward 1 zoom level) 00316 00317 m.add( 'command', label="Forward", 00318 command=lambda o=s: o.zoom_forward(), 00319 state='disabled' ) 00320 00321 # Zoom - enter bounds 00322 00323 m.add( 'command', label="Enter bounds..", 00324 command=lambda o=s: o.zoom_enter() ) 00325 00326 # Zoom - reset 00327 00328 m.add( 'command', label="Reset", 00329 command=lambda o=s: o.zoom_reset() ) 00330 00331 # Zoom - options (another cascade) 00332 00333 zom = Menu( m ) 00334 m.add( 'cascade', label="Options", menu=zom ) 00335 00336 s.zoomopts_asp = IntVar() 00337 s.zoomopts_sel = IntVar() 00338 00339 zom.add( 'check', label="Preserve aspect ratio", 00340 variable=s.zoomopts_asp ) 00341 00342 zom.add( 'separator' ) 00343 00344 zom.add( 'radio', label="Start from corner", 00345 variable=s.zoomopts_sel, value=0 ) 00346 00347 zom.add( 'radio', label="Start from center", 00348 variable=s.zoomopts_sel, value=1 ) 00349 00350 s.zoomopts_sel = 1 00351 s.zoomopts_asp = 1 00352 00353 zom.invoke(1) 00354 zom.invoke(4) 00355 00356 def create_menu_page( s, pmenu ): 00357 m = Menu( pmenu ) 00358 pmenu.add( "cascade", label='Page', menu=m ) 00359 00360 # Page - enter bounds 00361 00362 m.add( 'command', label="Setup..", command=s.page_enter ) 00363 00364 # Page - reset 00365 00366 m.add( 'command', label="Reset", command=s.page_reset ) 00367 00368 def create_menu_options( s, pmenu ): 00369 m = Menu( pmenu ) 00370 pmenu.add( 'cascade', label="Options", menu=m ) 00371 00372 m.add( 'command', label="Palette 0", command=s.plcmap0_edit ) 00373 m.add( 'command', label="Palette 1", command=s.plcmap1_edit ) 00374 00375 def create_menu_debug( s, pmenu ): 00376 pmenu.add( "command", label="Debug PlXframe", command=s.debug ) 00377 00378 ## Now the commands needed to implement the menus. 00379 00380 def key_filter( s, e ): 00381 """Process keystroke events, and parcell out to various control 00382 functions.""" 00383 00384 kn = e.keysym 00385 00386 if kn == 'z': 00387 s.zoom_select() 00388 elif kn == 'b': 00389 s.zoom_back() 00390 elif kn == 'f': 00391 s.zoom_forward() 00392 elif kn == 'r': 00393 s.zoom_reset() 00394 else: 00395 pass 00396 #print "Unknown keyname ", kn 00397 00398 def user_mouse( s, e ): 00399 print "in user_mouse" 00400 00401 ## flash 00402 00403 def cmd_print(s): 00404 s.label_set( "Printing plot..." ) 00405 s.tk.call( s.plf._w, 'print' ) 00406 # Should see about error trapping, like the itk widget does. 00407 00408 def sucky_save(s): 00409 """A sucky save menu thing. Needs to be enhanced to work like 00410 the one in Tcl/Itcl.""" 00411 00412 s.tk.call( s.plf._w, 'save', 'as', 'ps', 'xx.ps' ) 00413 s.tk.call( s.plf._w, 'save', 'close' ) 00414 print "Plot saved to xx.ps" 00415 00416 def save_as(s): pass 00417 def save_again(s): pass 00418 def save_close(s): pass 00419 00420 def update_zoom(s,m): 00421 """Configure zoom menu. 00422 00423 Responsible for making sure zoom menu entries are normal or 00424 disabled as appropriate. In particular, that 'Back' or 'Forward' 00425 are only displayed if it is possible to traverse the zoom windows 00426 list in that direction.""" 00427 00428 zframes = len( s.zxl ) 00429 00430 if s.zidx == 0: 00431 #print "disable back" 00432 m.entryconfig( 2, state=DISABLED ) 00433 else: 00434 #print "enable back" 00435 m.entryconfig( 2, state=ACTIVE ) 00436 00437 if s.zidx == zframes-1: 00438 #print "disable forward" 00439 m.entryconfig( 3, state=DISABLED ) 00440 else: 00441 #print "enable forward" 00442 m.entryconfig( 3, state=ACTIVE ) 00443 00444 def zoom_select(s): 00445 "Zooms plot in response to mouse selection." 00446 00447 # In itk we save the existing binding so it can be restored. 00448 00449 # Don't know how to save a binding for future use in Python/Tk. 00450 ## s.def_button_cmd = s.plf.bind( "<ButtonPress>" ) 00451 00452 ## global def_button_cmd zoomopts 00453 ## 00454 ## set def_button_cmd [bind [plwin] <ButtonPress>] 00455 00456 if s.zoomopts_sel == 0: 00457 s.label_set( "Click on one corner of zoom region." ) 00458 elif s.zoomopts_sel == 1: 00459 s.label_set( "Click on center of zoom region." ) 00460 00461 s.plf.bind( "<ButtonPress>", s.zoom_start ) 00462 00463 def zoom_enter(s): 00464 print "zoom_enter" 00465 00466 ##---------------------------------------------------------------------------- 00467 ## zoom_reset 00468 ## 00469 ## Resets after zoom. 00470 ## Note that an explicit redraw is not necessary since the packer 00471 ## issues a resize after the scrollbars are unmapped. 00472 ##---------------------------------------------------------------------------- 00473 00474 def zoom_reset(s): 00475 00476 ## global def_button_cmd 00477 00478 s.label_reset() 00479 ## bind [plwin] <ButtonPress> $def_button_cmd 00480 00481 s.tk.call( s.plf._w, 'view', 'reset' ) 00482 00483 if s.hscroll_exists and atoi( s.tk.call( 'winfo', 'ismapped', 00484 s.hscroll._w ) ): 00485 s.hscroll.grid_forget() 00486 00487 if s.vscroll_exists and atoi( s.tk.call( 'winfo', 'ismapped', 00488 s.vscroll._w ) ): 00489 s.vscroll.grid_forget() 00490 00491 ## Reset zoom windows list 00492 00493 s.zidx = 0 00494 s.zxl = [ 0. ]; s.zyl = [ 0. ] 00495 s.zxr = [ 1. ]; s.zyr = [ 1. ] 00496 00497 def update_orient(s,m): 00498 00499 r = s.tk.call( s.plf._w, 'orient' ) 00500 00501 # Grr, this is a floating point string. Must stand on our 00502 # heads to get an actual integer out of it. 00503 00504 f = atof( r ) 00505 fsi = "%.0f" % f 00506 i = atoi( fsi ) 00507 00508 n = i / 90 00509 n = i % 4 00510 00511 m.invoke( n+1 ) 00512 00513 def orient(s, n): 00514 """Set the orientation of the plframe, but check to make sure 00515 we only do this if the new orientation is different from the 00516 old one.""" 00517 00518 oldori = s.tk.call( s.plf._w, 'orient' ) 00519 oldf = atof( oldori ) 00520 oldn = atoi( "%.0f" % oldf ) % 4 00521 00522 if n != oldn: 00523 rots = "%d" % n 00524 s.tk.call( s.plf._w, 'orient', rots ) 00525 00526 def page_enter(s): 00527 print "in page_enter" 00528 00529 def page_reset(s): 00530 print "in page_reset" 00531 00532 def zoom_start( s, e ): 00533 "Starts plot zoom." 00534 00535 s.wx = e.x 00536 s.wy = e.y 00537 00538 ## Restore previous binding, but don't know how to do this in Python/Tk. 00539 ## s.plf.bind( "<ButtonPress>", s.def_button_cmd ) 00540 00541 ## global def_button_cmd 00542 ## 00543 ## bind [plwin] <ButtonPress> $def_button_cmd 00544 00545 ## Maybe what I should do for now is just remove the one we instlaled, 00546 ## but punt on restoring the prexisting binding. 00547 00548 s.plf.bind( "<ButtonPress>", None ) 00549 00550 ## Hmpffff. That didn't work... Grrrrrr. 00551 00552 s.label_set( "Select zoom region by dragging mouse, then release." ) 00553 00554 s.tk.call( s.plf._w, 'draw', 'init' ) 00555 s.plf.bind( "<B1-Motion>", s.zoom_mouse_draw ) 00556 s.plf.bind( "<B1-ButtonRelease>", s.zoom_mouse_end ) 00557 00558 ##---------------------------------------------------------------------------- 00559 ## zoom_coords 00560 ## 00561 ## Transforms the initial and final mouse coordinates to either: 00562 ## 00563 ## opt = 0 device coordinates 00564 ## opt = 1 normalized device coordinates 00565 ## 00566 ## The global variable "zoomopts" is used to determine zoom behavior: 00567 ## 00568 ## zoomopts($this,0): 00569 ## 0 box follows mouse movements exactly 00570 ## 1 box follows mouse movements so that aspect ratio is preserved (default) 00571 ## 00572 ## zoomopts($this,1): 00573 ## 0 first and last points specified determine opposite corners 00574 ## of zoom box. 00575 ## 1 box is centered about the first point clicked on, 00576 ## perimeter follows mouse (default) 00577 ## 00578 ##---------------------------------------------------------------------------- 00579 00580 def zoom_coords( s, x0, y0, x1, y1, opt ): 00581 00582 # Convert the integer input to float, prevents problems with 00583 # division. 00584 00585 x0 = float(x0) 00586 y0 = float(y0) 00587 x1 = float(x1) 00588 y1 = float(y1) 00589 00590 Lx = s.plf.winfo_width() 00591 Ly = s.plf.winfo_height() 00592 00593 # Enforce boundaries in device coordinate space 00594 00595 bounds = split( s.tk.call( s.plf._w, 'view', 'bounds' ) ) 00596 xmin = Lx * atof( bounds[0] ) 00597 ymin = Ly * atof( bounds[1] ) 00598 xmax = Lx * atof( bounds[2] ) 00599 ymax = Ly * atof( bounds[3] ) 00600 00601 x1 = max( xmin, min( xmax, x1 ) ) 00602 y1 = max( ymin, min( ymax, y1 ) ) 00603 00604 # Two-corners zoom. 00605 00606 if s.zoomopts_sel == 0: 00607 pass 00608 00609 ## if { $zoomopts($this,1) == 0 } then { 00610 00611 # Get box lengths 00612 00613 dx = x1 - x0 00614 dy = y1 - y0 00615 ## set dx [expr $x1 - $x0] 00616 ## set dy [expr $y1 - $y0] 00617 00618 sign_dx = sign(dx) 00619 sign_dy = sign(dy) 00620 ## set sign_dx [expr ($dx > 0) ? 1 : -1] 00621 ## set sign_dy [expr ($dy > 0) ? 1 : -1] 00622 00623 xl = x0 00624 yl = y0 00625 ## set xl $x0 00626 ## set yl $y0 00627 00628 # Constant aspect ratio 00629 00630 if s.zoomopts_asp == 1: 00631 pass 00632 ## if { $zoomopts($this,0) == 1 } then { 00633 ## 00634 ## # Scale factors used to maintain plot aspect ratio 00635 ## 00636 ## set xscale [expr $xmax - $xmin] 00637 ## set yscale [expr $ymax - $ymin] 00638 ## 00639 ## # Adjust box size for proper aspect ratio 00640 ## 00641 ## set rx [expr double(abs($dx)) / $xscale] 00642 ## set ry [expr double(abs($dy)) / $yscale] 00643 ## 00644 ## if { $rx > $ry } then { 00645 ## set dy [expr $yscale * $rx * $sign_dy] 00646 ## } else { 00647 ## set dx [expr $xscale * $ry * $sign_dx] 00648 ## } 00649 ## 00650 ## set xr [expr $xl + $dx] 00651 ## set yr [expr $yl + $dy] 00652 ## 00653 ## # Now check again to see if in bounds, and adjust if not 00654 ## 00655 ## if { $xr < $xmin || $xr > $xmax } then { 00656 ## if { $xr < $xmin } then { 00657 ## set dx [expr $xmin - $x0] 00658 ## } else { 00659 ## set dx [expr $xmax - $x0] 00660 ## } 00661 ## set rx [expr double(abs($dx)) / $xscale] 00662 ## set dy [expr $yscale * $rx * $sign_dy] 00663 ## } 00664 ## 00665 ## if { $yr < $ymin || $yr > $ymax } then { 00666 ## if { $yr < $ymin } then { 00667 ## set dy [expr $ymin - $y0] 00668 ## } else { 00669 ## set dy [expr $ymax - $y0] 00670 ## } 00671 ## set ry [expr double(abs($dy)) / $yscale] 00672 ## set dx [expr $xscale * $ry * $sign_dx] 00673 ## } 00674 ## } 00675 00676 # Final box coordinates 00677 00678 xr = xl + dx 00679 yr = yl + dy 00680 ## set xr [expr $xl + $dx] 00681 ## set yr [expr $yl + $dy] 00682 00683 ### zoom from center out, preserving aspect ratio 00684 else: 00685 00686 # Get box lengths, adjusting downward if necessary to keep 00687 # in bounds 00688 00689 dx = abs( x1 - x0 ) 00690 dy = abs( y1 - y0 ) 00691 00692 xr = x0 + dx; 00693 xl = x0 - dx 00694 yr = y0 + dy 00695 yl = y0 - dy 00696 00697 if xl < xmin: dx = x0 - xmin 00698 if xr > xmax: dx = xmax - x0 00699 if yl < ymin: dy = y0 - ymin 00700 if yr > ymax: dy = ymax - y0 00701 00702 # Constant aspect ratio 00703 00704 if s.zoomopts_asp == 1: 00705 00706 # Scale factors used to maintain plot aspect ratio 00707 00708 xscale = xmax - xmin 00709 yscale = ymax - ymin 00710 00711 # Adjust box size for proper aspect ratio 00712 00713 rx = dx / xscale 00714 ry = dy / yscale 00715 00716 if rx > ry: 00717 dy = yscale * rx 00718 else: 00719 dx = xscale * ry 00720 00721 xr = x0 + dx 00722 xl = x0 - dx 00723 yr = y0 + dy 00724 yl = y0 - dy 00725 00726 # Now check again to see if in bounds, and adjust 00727 # downward if not 00728 00729 if xl < xmin: 00730 dx = x0 - xmin 00731 rx = dx / xscale 00732 dy = yscale * rx 00733 if yr > ymax: 00734 dx = xmax - x0 00735 rx = dx / xscale 00736 dy = yscale * rx 00737 if yl < ymin: 00738 dy = y0 - ymin 00739 ry = dy / yscale 00740 dx = xscale * ry 00741 if yr > ymax: 00742 dy = ymax - y0 00743 ry = dy / yscale 00744 dx = xscale * ry 00745 00746 # Final box coordinates 00747 00748 xr = x0 + dx 00749 xl = x0 - dx 00750 yr = y0 + dy 00751 yl = y0 - dy 00752 00753 ## Optional translation to relative device coordinates. 00754 00755 if opt == 1: 00756 wxl = xl / Lx 00757 wxr = xr / Lx 00758 wyl = 1.0 - float(yr) / Ly 00759 wyr = 1.0 - float(yl) / Ly 00760 else: 00761 wxr = xl 00762 wxl = xr 00763 wyr = yl 00764 wyl = yr 00765 00766 return wxl, wyl, wxr, wyr 00767 00768 def zoom_mouse_draw(s,e): 00769 "Draws zoom box in response to mouse motion (with button held down)." 00770 00771 coords = s.zoom_coords( s.wx, s.wy, e.x, e.y, 0 ) 00772 00773 s.tk.call( s.plf._w, 'draw', 'rect', 00774 coords[0], coords[1], coords[2], coords[3] ) 00775 00776 def zoom_mouse_end( s, e ): 00777 "Performs actual zoom, invoked when user releases mouse button." 00778 00779 # Finish rubber band draw 00780 00781 s.plf.bind( "<B1-ButtonRelease>" ) 00782 s.plf.bind( "<B1-Motion>" ) 00783 ## bind [plwin] <B1-ButtonRelease> {} 00784 ## bind [plwin] <B1-Motion> {} 00785 s.label_reset() 00786 s.tk.call( s.plf._w, 'draw', 'end' ) 00787 00788 # Select new plot region 00789 00790 coords = s.zoom_coords( s.wx, s.wy, e.x, e.y, 1 ) 00791 00792 s.view_zoom( coords[0], coords[1], coords[2], coords[3] ) 00793 00794 ### Hmm, view_select is only called by update_view, which isn't called 00795 ### by anything... 00796 ## def view_select( s, x0, y0, x1, y1 ): 00797 ## """Handles change of view into plot. 00798 ## Given in relative plot window coordinates.""" 00799 ## 00800 ## print "in view_select" 00801 ####body Pltkwin::view_select {x0 y0 x1 y1} { 00802 ## 00803 #### Adjust arguments to be in bounds and properly ordered (xl < xr, etc) 00804 ## 00805 #### set xl [min $x0 $x1] 00806 #### set yl [min $y0 $y1] 00807 #### set xr [max $x0 $x1] 00808 #### set yr [max $y0 $y1] 00809 ## 00810 ## xl = min( x0, x1 ); yl = min( y0, y1 ) 00811 ## xr = max( x0, x1 ); yr = max( y0, y1 ) 00812 ## 00813 #### set xmin 0. 00814 #### set ymin 0. 00815 #### set xmax 1. 00816 #### set ymax 1. 00817 ## 00818 ## xmin = 0.; ymin = 0. 00819 ## xmax = 1.; ymax = 1. 00820 ## 00821 #### set xl [max $xmin [min $xmax $xl]] 00822 #### set yl [max $ymin [min $ymax $yl]] 00823 #### set xr [max $xmin [min $xmax $xr]] 00824 #### set yr [max $ymin [min $ymax $yr]] 00825 ## 00826 ## xl = max( xmin, min( xmax, xl ) ) 00827 ## yl = max( ymin, min( ymax, yl ) ) 00828 ## xr = max( xmin, min( xmax, xr ) ) 00829 ## yr = max( ymin, min( ymax, yr ) ) 00830 ## 00831 #### Only create scrollbars if really needed. 00832 ## 00833 #### if {($xl == $xmin) && ($xr == $xmax)} \ 00834 #### then {set hscroll 0} else {set hscroll 1} 00835 #### 00836 #### if {($yl == $xmin) && ($yr == $xmax)} \ 00837 #### then {set vscroll 0} else {set vscroll 1} 00838 #### 00839 #### if { ! ($hscroll || $vscroll)} {return} 00840 ## 00841 ## if xl == xmin and xr == xmax: 00842 ## hscroll = 0 00843 ## else: 00844 ## hscroll = 1 00845 ## 00846 ## if yl == ymin and yr == ymax: 00847 ## vscroll = 0 00848 ## else: 00849 ## vscroll = 1 00850 ## 00851 ## if not (hscroll or vscroll): 00852 ## return 00853 ## 00854 #### Select plot region 00855 ## 00856 #### [plwin] view select $xl $yl $xr $yr 00857 ## 00858 ## s.tk.call( s.plf._w, 'view', 'select', xl, yl, xr, yr ) 00859 ## 00860 #### Fix up view 00861 ## 00862 #### fixview $hscroll $vscroll 00863 ## s.fixview( hscroll, vscroll ) 00864 00865 def view_zoom( s, x0, y0, x1, y1 ): 00866 "Handles zoom. Given in relative device coordinates." 00867 00868 ## Adjust arguments to be properly ordered (xl < xr, etc) 00869 00870 xl = min( x0, x1 ) 00871 yl = min( y0, y1 ) 00872 xr = max( x0, x1 ) 00873 yr = max( y0, y1 ) 00874 00875 ## Check for double-click (specified zoom region less than a few 00876 ## pixels wide). In this case, magnification is 2X in each direction, 00877 ## centered at the mouse location. At the boundary, the magnification 00878 ## is determined by the distance to the boundary. 00879 00880 stdzoom = .5 00881 if (xr - xl) < .02 and (yr - yl) < .02: 00882 nxl = xl - .5 * stdzoom 00883 nxr = xl + .5 * stdzoom 00884 if nxl < 0.: 00885 nxl = 0. 00886 nxr = 2. * xl 00887 if nxr > 1.: 00888 nxr = 1. 00889 nxl = 2. * xl - 1. 00890 xl = nxl 00891 xr = nxr 00892 00893 nyl = yl - .5 * stdzoom 00894 nyr = yl + .5 * stdzoom 00895 if nyl < 0.: 00896 nyl = 0. 00897 nyr = 2. * yl 00898 if nyr > 1.: 00899 nyr = 1. 00900 nyl = 2. * yl - 1. 00901 yl = nyl 00902 yr = nyr 00903 00904 ## Adjust arguments to be in bounds (in case margins are in effect). 00905 00906 bounds = split( s.tk.call( s.plf._w, 'view', 'bounds' ) ) 00907 xmin = atof( bounds[0] ) 00908 ymin = atof( bounds[1] ) 00909 xmax = atof( bounds[2] ) 00910 ymax = atof( bounds[3] ) 00911 00912 xl = max( xmin, min( xmax, xl ) ) 00913 yl = max( ymin, min( ymax, yl ) ) 00914 xr = max( xmin, min( xmax, xr ) ) 00915 yr = max( ymin, min( ymax, yr ) ) 00916 00917 ## Only create scrollbars if really needed. 00918 00919 hscroll, vscroll = 0, 0 00920 if xl != xmin or xr != xmax: hscroll = 1 00921 if yl != ymin or yr != ymax: vscroll = 1 00922 00923 if not (hscroll or yscroll): 00924 s.tk.call( s.plf._w, 'redraw' ) 00925 return 00926 00927 ## Select plot region 00928 00929 s.tk.call( s.plf._w, 'view', 'zoom', xl, yl, xr, yr ) 00930 00931 ## Fix up view 00932 00933 s.fixview( hscroll, vscroll ) 00934 00935 ## Add window to zoom windows list 00936 00937 coords = split( s.tk.call( s.plf._w, 'view' ) ) 00938 00939 s.zidx = s.zidx + 1 00940 00941 if s.zidx == len( s.zxl ): 00942 # Adding onto the end, no big deal 00943 s.zxl.append( atof( coords[0] ) ) 00944 s.zyl.append( atof( coords[1] ) ) 00945 s.zxr.append( atof( coords[2] ) ) 00946 s.zyr.append( atof( coords[3] ) ) 00947 else: 00948 # Adding into the middle... 00949 s.zxl[ s.zidx ] = atof( coords[0] ) 00950 s.zyl[ s.zidx ] = atof( coords[1] ) 00951 s.zxr[ s.zidx ] = atof( coords[2] ) 00952 s.zyr[ s.zidx ] = atof( coords[3] ) 00953 00954 if s.zidx < len( s.zxl ) - 1: 00955 # Now chop off the end. 00956 s.zxl = s.zxl[0:s.zidx+1] 00957 s.zyl = s.zyl[0:s.zidx+1] 00958 s.zxr = s.zxr[0:s.zidx+1] 00959 s.zyr = s.zyr[0:s.zidx+1] 00960 00961 def zoom_back(s): 00962 "Traverse the zoom windows list backward." 00963 00964 if s.zidx > 0: 00965 s.zidx = s.zidx - 1 00966 00967 xl = s.zxl[ s.zidx ]; yl = s.zyl[ s.zidx ] 00968 xr = s.zxr[ s.zidx ]; yr = s.zyr[ s.zidx ] 00969 00970 # Select plot region 00971 00972 s.tk.call( s.plf._w, 'view', 'select', xl, yl, xr, yr ) 00973 00974 def zoom_forward(s): 00975 "Traverse the zoom windows list forward." 00976 00977 zframes = len( s.zxl ) 00978 00979 if zframes == 1 or s.zidx == zframes-1: 00980 return 00981 00982 s.zidx = s.zidx + 1 00983 00984 xl = s.zxl[ s.zidx ]; yl = s.zyl[ s.zidx ] 00985 xr = s.zxr[ s.zidx ]; yr = s.zyr[ s.zidx ] 00986 00987 # Select plot region 00988 00989 s.tk.call( s.plf._w, 'view', 'select', xl, yl, xr, yr ) 00990 00991 def view_scroll(s): 00992 print "in view_scroll" 00993 00994 def fixview( s, hscroll, vscroll ): 00995 "Handles updates of scrollbars & plot after view change." 00996 00997 ## Create scrollbars if they don't already exist. 00998 00999 created_sb = 0 01000 if hscroll and not s.hscroll_exists: 01001 s.hscroll = Scrollbar( s, relief=SUNKEN, orient=HORIZONTAL ) 01002 s.hscroll['command'] = ( s.plf._w, 'xscroll' ) 01003 s.plf.config( xscroll=( s.hscroll, 'set' ) ) 01004 s.hscroll_exists = 1 01005 created_sb = 1 01006 01007 if vscroll and not s.vscroll_exists: 01008 s.vscroll = Scrollbar( s, relief=SUNKEN, orient=VERTICAL ) 01009 s.vscroll['command'] = ( s.plf._w, 'yscroll' ) 01010 s.plf.config( yscroll=( s.vscroll, 'set' ) ) 01011 s.vscroll_exists = 1 01012 created_sb = 1 01013 01014 ## When scrollbars are first created, it may be necessary to unmap 01015 ## then map the plframe widget so that it has a chance to initialize 01016 ## the scrollbars before they are mapped. 01017 01018 if created_sb: 01019 s.plf.grid_forget() 01020 s.plf.grid( row=1, column=0, sticky='nsew' ) 01021 01022 ## Map scrollbars if not already mapped. To get packing right, need 01023 ## to unmap then remap plot widget. Otherwise need to do explicit 01024 ## redraw. 01025 01026 if hscroll and not atoi( s.tk.call( 'winfo', 'ismapped', 01027 s.hscroll._w ) ) \ 01028 or vscroll and not atoi( s.tk.call( 'winfo', 'ismapped', 01029 s.vscroll._w ) ) : 01030 s.update() 01031 s.plf.grid_forget() 01032 if hscroll: 01033 s.hscroll.grid( row=2, column=0, sticky='ew' ) 01034 if vscroll: 01035 s.vscroll.grid( row=1, column=1, sticky='ns' ) 01036 s.plf.grid( row=1, column=0, sticky='nsew' ) 01037 else: 01038 s.tk.call( s.plf._w, 'redraw' ) 01039 01040 ## Hmmm. Actually, "update_view" doesn't seem to be used by anything... 01041 ## def update_view(s): 01042 ## """Updates view. 01043 ## Results in scrollbars being added if they are appropriate. 01044 ## Does nothing if the plot window is unchanged from the default.""" 01045 ## 01046 ## print "in update_view" 01047 #### set coords [[plwin] view] 01048 #### 01049 #### set xl [lindex "$coords" 0] 01050 #### set yl [lindex "$coords" 1] 01051 #### set xr [lindex "$coords" 2] 01052 #### set yr [lindex "$coords" 3] 01053 #### 01054 #### view_select $xl $yl $xr $yr 01055 01056 def status_msg(s,msg): 01057 s.label_set(msg) 01058 # schedule removal of the message with Tk "after" 01059 s.after( 2500, s.label_reset ) 01060 01061 def label_reset(s): 01062 s.ftop.lstat.config( text='' ) 01063 01064 def label_set(s,msg): 01065 s.ftop.lstat.config( text=msg ) 01066 01067 def plcmap0_edit(s): 01068 print "in plcmap0_edit" 01069 01070 def plcmap1_edit(s): 01071 print "in plcmap1_edit" 01072 01073 ## Now do the PLplot API. Just vector these off to the contained 01074 ## Plframe widget. 01075 01076 def cmd( s, *args ): 01077 "Invoke a subcommand on the plframe widget." 01078 apply( s.tk.call, (s.plf._w, 'cmd',) + _flatten(args) ) 01079 01080 def pladv( s, page ): 01081 s.cmd( 'pladv', page ) 01082 01083 def plaxes( s, x0, y0, xopt, xtick, nxsub, yopt, ytick, nysub ): 01084 s.cmd( 'plaxes', x0, y0, xopt, xtick, nxsub, yopt, ytick, nysub ) 01085 01086 def plbin(s): pass 01087 def plbop(s): 01088 s.cmd( 'plbop' ) 01089 01090 def plbox( s, xopt, xtick, nxsub, yopt, ytick, nysub ): 01091 s.cmd( 'plbox', xopt, xtick, nxsub, yopt, ytick, nysub ) 01092 01093 def plbox3( s, xopt, xlabel, xtick, nsubx, 01094 yopt, ylabel, ytick, nsuby, 01095 zopt, zlabel, ztick, nsubz ): 01096 s.cmd( 'plbox3', 01097 xopt, xlabel, xtick, nsubx, 01098 yopt, ylabel, ytick, nsuby, 01099 zopt, zlabel, ztick, nsubz ) 01100 01101 def plcol0( s, col0 ): 01102 s.cmd( 'plcol0', col0 ) 01103 01104 def plcol1( s, col1 ): 01105 s.cmd( 'plcol1', col1 ) 01106 01107 # def plcont( s ): pass 01108 01109 ## def plcontxxx( s, z, kx, lx, ky, ly, clev, pltr, xg, yg, wrap ): 01110 ## plsstrm( s.strm ) 01111 ## plcont( z, kx, lx, ky, ly, clev, pltr, xg, yg, wrap ) 01112 01113 def plcont( s, *args ): 01114 plsstrm( s.strm ) 01115 apply( plcont, args ) 01116 01117 def plfcont( s ): pass 01118 def plcpstream( s ): pass 01119 01120 def plenv( s, xmin, xmax, ymin, ymax, i, j ): 01121 s.cmd( 'plenv', xmin, xmax, ymin, ymax, i, j ) 01122 01123 def pleop(s): 01124 s.cmd( 'pleop' ) 01125 #print "should've waited here, but it didn't." 01126 s.plf.setvar( 'wv', '0' ) 01127 s.label_set( "Plotting paused ... (Hit Clear to continue)" ) 01128 #print "preparing to wait for wv to change" 01129 s.plf.waitvar( 'wv' ) 01130 #print "it changed." 01131 s.label_reset() 01132 s.update() 01133 01134 def clearpage(s): 01135 s.plf.setvar( 'wv', 1 ) 01136 01137 def plfill( s, x, y ): 01138 plsstrm( s.strm ) 01139 plfill( x, y ) 01140 01141 def plfont( s, ifnt ): 01142 s.cmd( 'plfont', ifnt ) 01143 01144 def plfontld( s, fnt ): 01145 s.cmd( 'plfontld', fnt ) 01146 01147 def plhist( s, data, datmin, datmax, nbin, oldwin ): 01148 plsstrm( s.strm ) 01149 plhist( data, datmin, datmax, nbin, oldwin ) 01150 01151 def plhls( s, h, l, sat ): 01152 s.cmd( 'plhls', h, l, sat ) 01153 01154 def pljoin( s, x1, y1, x2, y2 ): 01155 s.cmd( 'pljoin', x1, y1, x2, y2 ) 01156 01157 def pllab( s, xlab, ylab, tlab ): 01158 s.cmd( 'pllab', xlab, ylab, tlab ) 01159 01160 def plline( s, x, y ): 01161 plsstrm( s.strm ) 01162 plline( x, y ) 01163 01164 def plline3( s, x, y, z ): 01165 plsstrm( s.strm ) 01166 plline3( x, y, z ) 01167 01168 def pllsty( s, lin ): 01169 s.cmd( 'pllsty', lin ) 01170 01171 # map and merridians ommitted. 01172 01173 def plmesh( s, x, y, z, opt ): 01174 plsstrm( s.strm ) 01175 plmesh( x, y, z, opt ) 01176 01177 def plmtex( s, side, disp, pos, just, text ): 01178 s.cmd( 'plmtex', side, disp, pos, just, text ) 01179 01180 def plot3d( s, x, y, z, opt, side ): 01181 plsstrm( s.strm ) 01182 plplot3d( x, y, z, opt, side ) 01183 01184 def plplot3d( s, x, y, z, opt, side ): 01185 plsstrm( s.strm ) 01186 plplot3d( x, y, z, opt, side ) 01187 01188 def plpoin( s, xs, ys, mark ): 01189 plsstrm( s.strm ) 01190 plpoin( xs, ys, mark ) 01191 01192 def plpoin3( s, x, y, z, code ): 01193 plsstrm( s.strm ) 01194 plpoin3( x, y, z, code ) 01195 01196 def plpoly3( s, x, y, z, draw ): 01197 plsstrm( s.strm ) 01198 plpoly3( x, y, z, draw ) 01199 01200 def plprec( s, setp, prec ): 01201 s.cmd( 'plprec', setp, prec ) 01202 01203 def plpsty( s, patt ): 01204 s.cmd( 'plpsty', patt ) 01205 01206 def plptex( s, x, y, dx, dy, just, text ): 01207 s.cmd( 'plptex', x, y, dx, dy, just, text ) 01208 01209 def plreplot( s ): 01210 s.cmd( 'plreplot' ) 01211 01212 def plrgb( s, r, g, b ): 01213 s.cmd( 'plrgb', r, g, b ) 01214 01215 def plrgb1( s, r, g, b ): 01216 s.cmd( 'plrgb1', r, g, b ) 01217 01218 def plschr( s, dflt, scale ): 01219 s.cmd( 'plschr', dflt, scale ) 01220 01221 def plshade( s, z, xmin, xmax, ymin, ymax, 01222 sh_min, sh_max, sh_cmap, sh_color, sh_width, 01223 min_col, min_wid, max_col, max_wid, rect, 01224 pltr='pltr0', xg=None, yg=None, wrap=0 ): 01225 "Color filled shad plot." 01226 01227 plsstrm( s.strm ); 01228 plshade( z, xmin, xmax, ymin, ymax, 01229 sh_min, sh_max, sh_cmap, sh_color, sh_width, 01230 min_col, min_wid, max_col, max_wid, rect, 01231 pltr, xg, yg, wrap ) 01232 01233 ## def plshade2( s, z, xmin, xmax, ymin, ymax, 01234 ## sh_min, sh_max, sh_cmap, sh_color, sh_width, 01235 ## min_col, min_wid, max_col, max_wid, rect, 01236 ## pltr, xg, yg, wrap ): 01237 ## "Was unable to fix plshade, must make new plshade2, grrr." 01238 ## 01239 ## print "in plshade2" 01240 ## plsstrm( s.strm ); 01241 ## plshade( z, xmin, xmax, ymin, ymax, 01242 ## sh_min, sh_max, sh_cmap, sh_color, sh_width, 01243 ## min_col, min_wid, max_col, max_wid, rect, 01244 ## pltr, xg, yg, wrap ) 01245 01246 # Only works for special conditions so comment it out for now. 01247 # def plsmem( ny, ny, plotmem ): 01248 # s.cmd('plsmem', nx, ny plotmem ) 01249 01250 def plssub( s, nx, ny ): 01251 s.cmd( 'plssub', nx, ny ) 01252 01253 def plssym( s, dflt, scale ): 01254 s.cmd( 'plssym', dflt, scale ) 01255 01256 # plstar and plstart not relevant 01257 01258 #def plstyl( s, ... 01259 01260 def plsvpa( s, xmin, xmax, ymin, ymax ): 01261 s.cmd( 'plsvpa', xmin, xmax, ymin, ymax ) 01262 01263 def plsxax( s, digmax, digits ): 01264 s.cmd( 'plsxax', digmax, digits ) 01265 01266 def plsyax( s, digmax, digits ): 01267 s.cmd( 'plsyax', digmax, digits ) 01268 01269 def plsym( s, x, y, code ): 01270 plsstrm( s.strm ) 01271 plsym( x, y, code ) 01272 01273 def plszax( s, digmax, digits ): 01274 s.cmd( 'plszax', digmax, digits ) 01275 01276 def plvasp( s, aspect ): 01277 s.cmd( 'plvasp', aspect ) 01278 01279 def plvpas( s, xmin, xmax, ymin, ymax, aspect ): 01280 s.cmd( 'plvpas', xmin, xmax, ymin, ymax, aspect ) 01281 01282 def plvpor( s, xmin, xmax, ymin, ymax ): 01283 s.cmd( 'plvpor', xmin, xmax, ymin, ymax ) 01284 01285 def plvsta(s): 01286 s.cmd( 'plvsta' ) 01287 01288 def plw3d( s, basex, basey, height, xmin0, 01289 xmax0, ymin0, ymax0, zmin0, zmax0, alt, az): 01290 s.cmd( 'plw3d', 01291 basex, basey, height, xmin0, 01292 xmax0, ymin0, ymax0, zmin0, zmax0, alt, az) 01293 01294 def plwid( s, width ): 01295 s.cmd( 'plwid', width ) 01296 01297 def plwind( s, xmin, xmax, ymin, ymax ): 01298 s.cmd( 'plwind', xmin, xmax, ymin, ymax ) 01299 01300 def debug(s): 01301 print "Debugging dump for PlXframe:" 01302 print "s.saveopt_dev = ", s.saveopt_dev 01303 01304 ## End of Plframe.py