PLplot: Plframe.py Source File

PLplot 5.10.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
Plframe.py
Go to the documentation of this file.
1 from Tkinter import *
2 #from pl import *
3 from plplot import *
4 from TclSup import *
5
6 from string import *
7
8 from math import *
9
10 CMD='cmd'
11
12 # Physically imported this function from Tkinter.py, b/c it evidently
13 # isn't made available via the above import statement.
14
15 def _flatten(tuple):
16 res = ()
17 for item in tuple:
18 if type(item) in (TupleType, ListType):
19 res = res + _flatten(item)
20 elif item is not None:
21 res = res + (item,)
22 return res
23
24 #=============================================================================#
25 # class Plframe
26 #=============================================================================#
27
28 class Plframe(Widget):
29
30 def __init__( self, master=None, cnf={}, **kw ):
31 """Constructor.
32
33 Initialize the Tk base class, and squirel away the stream id,
34 so that we will be able to ensure that future draw requests
35 show up in the right place."""
36
37 Widget.__init__( self, master, 'plframe', cnf, kw )
38
39 self.strm = plgstrm()
40
41 def cmd( s, *args ):
42 "Invoke a subcommand on the plframe widget."
43 apply( s.tk.call, (s._w, 'cmd',) + _flatten(args) )
44
45 def info(s, what):
46 return s.tk.call( s._w, 'info', what )
47
48 ## Now implement the PLplot API. For simple functions, can call
49 ## straight to Tk, which is probably the most straightforward way, in
50 ## the sense of making the Python/Tk widget look and act most like
51 ## it's older brother the Tcl version. However, functions which rely
52 ## on the Numeric extensions cannot work this way, so for those we
53 ## call straight to the Python compiled interface to the PLplot API.
54
55 def plenv( s, xmin, xmax, ymin, ymax, i, j ):
56 s.cmd( 'plenv', xmin, xmax, ymin, ymax, i, j )
57
58 def pleop(s):
59 s.cmd( 'pleop' )
60
61 def pllab( s, xlab, ylab, tlab ):
62 s.cmd( 'pllab', xlab, ylab, tlab )
63
64 def plline( s, x, y ):
65 plsstrm( s.strm )
66 plline( x, y )
67
68 def plpoin( s, xs, ys, mark ):
69 plsstrm( s.strm )
70 plpoin( xs, ys, mark )
71
72 #=============================================================================#
73 # class PlXframe
74 #=============================================================================#
75
76 class PlXframe(Frame):
77
78 """This class provides the same facilities as the Plframe, plus
79 several additional facilities for advanced plot interaction, which
80 are provided through menus and bindings."""
81
82 def __init__( s, master=None, cnf={}, **kw ):
83 """Constructor.
84
85 Configure the widget database, and then set up the internal
86 members. Namely, a menubar and a contained Plframe."""
87
88 Frame.__init__( s, master )
89
90 s.setup_defaults()
91
92 # We will be using the grid geometry manager to pack a plframe
93 # widget along with some additional addornments. The top row
94 # (row 0) will contain the menubar. The next row (row 1) will
95 # hold the actual plframe, as well as the vertical scrollbar
96 # when present.. And the last row (row 2) will hold the
97 # horizontal scrollbar when present. We want the menubar and
98 # the horizontal scrollbar to use only the required space, and
99 # we want the plframe to expand to fill any remaining space.
100 # To accomplish this we set the weights for the top and bottom
101 # row to zero, thus preventing them from expanding.
102
103 s.rowconfigure( 0, weight=0, minsize=0 )
104 s.rowconfigure( 1, weight=1, minsize=0 )
105 s.rowconfigure( 2, weight=0, minsize=0 )
106
107 # Similarly for the columns, we will use the left column (col
108 # 0) for the plframe and the horizontal scrollbar, and the
109 # right column (col 1) for the vertical scrollbar. Moreover, the
110 # menubar in the top row will span columns 0 and 1. Again, we
111 # want the rightmost column (col 0) to stay at whatever is the
112 # minimum width to display its contents, and want the plframe
113 # in the leftmost column to grow to fill all space.
114
115 s.columnconfigure( 0, weight=1, minsize=0 )
116 s.columnconfigure( 1, weight=0, minsize=0 )
117
118 # Okay, now get the actual plframe widget so we can display
119 # it. And we pack it into its grid cell with all four sides
120 # of the plframe sticking to the expandable size of the
121 # enclosing grid cell, as arranged by the row,col weighting
122 # scheme described above.
123
124 s.plf = Plframe( s, kw )
125 s.plf.grid( row=1, column=0, sticky=NSEW )
126
127 s.build_menu_bar()
128
129 s.strm = plgstrm()
130
131 s.setup_bindings()
132
133 s.hscroll_exists = 0
134 s.vscroll_exists = 0
135
136 ## All this stuff is being based heavily on the Pltkwin.tcl thing by
137 ## Vince, for itcl/itk (which in turn was based heavily on the
138 ## plwidgets.tcl stuff by Maurice).
139
140 def setup_defaults(s):
141 #s.saveopt_dev = StringVar()
142 s.saveopt_dev = "psc"
143 #s.saveopt_dev.set( "psc" )
144
145 #s.saveopt_file = IntVar()
146 s.saveopt_file = 0
147 #s.saveopt_file.set( 0 )
148
149 # Now do the zoom support stuff.
150
151 s.zidx = 0
152 s.zxl = [ 0. ]; s.zyl = [ 0. ]
153 s.zxr = [ 1. ]; s.zyr = [ 1. ]
154
155 def setup_bindings(s):
156 s.plf.bind( "<Any-KeyPress>", s.key_filter )
157 s.plf.bind( "<Any-ButtonPress>", s.user_mouse )
158 s.plf.bind( "<Any-Enter>", s.set_focus )
159
160 def set_focus(s,e):
161 # print "in set_focus"
162 s.plf.focus()
163
164 def build_menu_bar(s):
165 "Create the menubar at the top of the PlXframe"
166
167 s.ftop = Frame( s )
168 s.ftop.grid( row=0, columnspan=2, sticky='ew' )
169
170 s.ftop.plot = Menubutton( s.ftop, text="Plot", underline=0,
171 relief=RAISED )
172 s.ftop.plot.m = Menu( s.ftop.plot )
173 s.ftop.plot["menu"] = s.ftop.plot.m
174
175 s.ftop.plot.pack( side=LEFT )
176
177 s.create_menu_print( s.ftop.plot.m )
178 s.create_menu_save( s.ftop.plot.m )
179 s.create_menu_orient( s.ftop.plot.m )
180 s.create_menu_zoom( s.ftop.plot.m )
181 s.create_menu_page( s.ftop.plot.m )
182 s.create_menu_options( s.ftop.plot.m )
183 s.create_menu_debug ( s.ftop.plot.m )
184
185 s.ftop.eop = Button( s.ftop, text="Clear",
186 command=s.clearpage )
187 s.ftop.eop.pack( side=RIGHT )
188
189 s.ftop.lstat = Label( s.ftop, anchor=W, relief=RAISED )
190 s.ftop.lstat.pack( side=RIGHT, expand=1, fill=BOTH )
191
192 def create_menu_print( s, m ):
193 m.add( "command", label="Print", command=s.cmd_print )
194
195 def create_menu_save( s, pmenu ):
196 """Create the menu which lets us control the whole business of
197 saving plots to disk."""
198
199 m = Menu( pmenu )
200 pmenu.add( "cascade", label='Save', menu=m )
201
202 # Save - As
203
204 m.add( "command", label="As", command=s.save_as )
205
206 # Save - Again
207
208 m.add( "command", label="Again", command=s.save_again,
209 state=DISABLED )
210
211 # Save - Close
212
213 m.add( "command", label="Close", command=s.save_close,
214 state=DISABLED )
215
216 m.add( "separator" )
217
218 # Save - Set device.. (another cascade)
219
220 m.sdev = Menu( m )
221 m.add( "cascade", label="Set device", menu=m.sdev )
222
223 # Generate the device list in the "Save/Set device" widget
224 # menu, by querying the plframe widget for the available
225 # output devices (which are listed).
226
227 devnames = s.plf.info( "devnames" )
228 devkeys = s.plf.info( "devkeys" )
229
230 # Oh no, these came back as Tcl lists. Have to convert them
231 # to Python lists.
232
233 devnamlst = TclList2Py( devnames )
234 devkeylst = TclList2Py( devkeys )
235
236 print "devnamlst = ", devnamlst
237 print "devkeylst = ", devkeylst
238 print "len(devnamlst) = ", len(devnamlst)
239
240 ## for i in range( len(devnamlst) ):
241 ## devnam = devnamlst[i]
242 ## devkey = devkeylst[i]
243 ##
244 ## m.sdev.add( "radio", label=devnam, variable=s.saveopt_dev,
245 ## value=devkey )
246
247 # Now get the ball rolling by preinvoking one of these.
248 # Default to the first one, whatever it is, but use psc
249 # (Postscript color), if we can find it in the list of
250 # available devices.
251
252 ## ivk = 1
253 ## for i in range( len(devnamlst) ):
254 ## if devkeylst[i] == "psc":
255 ## ivk = i+1
256 ## break
257 ##
258 ## m.sdev.invoke( ivk )
259
260 # Save - Set file type.. (another cascade)
261
262 m.sfile = Menu( m )
263 m.add( "cascade", label="Set file type", menu=m.sfile )
264
265 m.sfile.add( "radio", label="Single file (one plot/file)",
266 variable=s.saveopt_file, value=0 )
267 m.sfile.add( "radio", label="Archive file( many plots/file)",
268 variable=s.saveopt_file, value=1 )
269
270 m.sfile.invoke( 1 )
271
272 def create_menu_orient( s, pmenu ):
273 m = Menu( pmenu )
274 pmenu.add( "cascade", label='Orient', menu=m )
275
276 m.config( postcommand=lambda o=s, x=m: o.update_orient(x) )
277
278 # Orient - 0 degrees
279
280 m.add( 'radio', label="0 degrees",
281 command=lambda o=s: o.orient(0) )
282
283 # Orient - 90 degrees
284
285 m.add( 'radio', label="90 degrees",
286 command=lambda o=s: o.orient(1) )
287
288 # Orient - 180 degrees
289
290 m.add( 'radio', label="180 degrees",
291 command=lambda o=s: o.orient(2) )
292
293 # Orient - 270 degrees
294
295 m.add( 'radio', label="270 degrees",
296 command=lambda o=s: o.orient(3) )
297
298 def create_menu_zoom( s, pmenu ):
299 m = Menu( pmenu )
300 pmenu.add( "cascade", label='Zoom', menu=m )
301
302 m.config( postcommand=lambda o=s, x=m: o.update_zoom(x) )
303
304 # Zoom - select (by mouse)
305
306 m.add( 'command', label="Select",
307 command=lambda o=s: o.zoom_select() )
308
309 # Zoom - back (go back 1 zoom level)
310
311 m.add( 'command', label="Back",
312 command=lambda o=s: o.zoom_back(),
313 state='disabled' )
314
315 # Zoom - forward (go forward 1 zoom level)
316
317 m.add( 'command', label="Forward",
318 command=lambda o=s: o.zoom_forward(),
319 state='disabled' )
320
321 # Zoom - enter bounds
322
323 m.add( 'command', label="Enter bounds..",
324 command=lambda o=s: o.zoom_enter() )
325
326 # Zoom - reset
327
328 m.add( 'command', label="Reset",
329 command=lambda o=s: o.zoom_reset() )
330
331 # Zoom - options (another cascade)
332
333 zom = Menu( m )
334 m.add( 'cascade', label="Options", menu=zom )
335
336 s.zoomopts_asp = IntVar()
337 s.zoomopts_sel = IntVar()
338
339 zom.add( 'check', label="Preserve aspect ratio",
340 variable=s.zoomopts_asp )
341
342 zom.add( 'separator' )
343
344 zom.add( 'radio', label="Start from corner",
345 variable=s.zoomopts_sel, value=0 )
346
347 zom.add( 'radio', label="Start from center",
348 variable=s.zoomopts_sel, value=1 )
349
350 s.zoomopts_sel = 1
351 s.zoomopts_asp = 1
352
353 zom.invoke(1)
354 zom.invoke(4)
355
356 def create_menu_page( s, pmenu ):
357 m = Menu( pmenu )
358 pmenu.add( "cascade", label='Page', menu=m )
359
360 # Page - enter bounds
361
362 m.add( 'command', label="Setup..", command=s.page_enter )
363
364 # Page - reset
365
366 m.add( 'command', label="Reset", command=s.page_reset )
367
368 def create_menu_options( s, pmenu ):
369 m = Menu( pmenu )
370 pmenu.add( 'cascade', label="Options", menu=m )
371
372 m.add( 'command', label="Palette 0", command=s.plcmap0_edit )
373 m.add( 'command', label="Palette 1", command=s.plcmap1_edit )
374
375 def create_menu_debug( s, pmenu ):
376 pmenu.add( "command", label="Debug PlXframe", command=s.debug )
377
378 ## Now the commands needed to implement the menus.
379
380 def key_filter( s, e ):
381 """Process keystroke events, and parcell out to various control
382 functions."""
383
384 kn = e.keysym
385
386 if kn == 'z':
387 s.zoom_select()
388 elif kn == 'b':
389 s.zoom_back()
390 elif kn == 'f':
391 s.zoom_forward()
392 elif kn == 'r':
393 s.zoom_reset()
394 else:
395 pass
396 #print "Unknown keyname ", kn
397
398 def user_mouse( s, e ):
399 print "in user_mouse"
400
401 ## flash
402
403 def cmd_print(s):
404 s.label_set( "Printing plot..." )
405 s.tk.call( s.plf._w, 'print' )
406 # Should see about error trapping, like the itk widget does.
407
408 def sucky_save(s):
409 """A sucky save menu thing. Needs to be enhanced to work like
410 the one in Tcl/Itcl."""
411
412 s.tk.call( s.plf._w, 'save', 'as', 'ps', 'xx.ps' )
413 s.tk.call( s.plf._w, 'save', 'close' )
414 print "Plot saved to xx.ps"
415
416 def save_as(s): pass
417 def save_again(s): pass
418 def save_close(s): pass
419
420 def update_zoom(s,m):
421 """Configure zoom menu.
422
423 Responsible for making sure zoom menu entries are normal or
424 disabled as appropriate. In particular, that 'Back' or 'Forward'
425 are only displayed if it is possible to traverse the zoom windows
426 list in that direction."""
427
428 zframes = len( s.zxl )
429
430 if s.zidx == 0:
431 #print "disable back"
432 m.entryconfig( 2, state=DISABLED )
433 else:
434 #print "enable back"
435 m.entryconfig( 2, state=ACTIVE )
436
437 if s.zidx == zframes-1:
438 #print "disable forward"
439 m.entryconfig( 3, state=DISABLED )
440 else:
441 #print "enable forward"
442 m.entryconfig( 3, state=ACTIVE )
443
444 def zoom_select(s):
445 "Zooms plot in response to mouse selection."
446
447 # In itk we save the existing binding so it can be restored.
448
449 # Don't know how to save a binding for future use in Python/Tk.
450 ## s.def_button_cmd = s.plf.bind( "<ButtonPress>" )
451
452 ## global def_button_cmd zoomopts
453 ##
454 ## set def_button_cmd [bind [plwin] <ButtonPress>]
455
456 if s.zoomopts_sel == 0:
457 s.label_set( "Click on one corner of zoom region." )
458 elif s.zoomopts_sel == 1:
459 s.label_set( "Click on center of zoom region." )
460
461 s.plf.bind( "<ButtonPress>", s.zoom_start )
462
463 def zoom_enter(s):
464 print "zoom_enter"
465
466 ##----------------------------------------------------------------------------
467 ## zoom_reset
468 ##
469 ## Resets after zoom.
470 ## Note that an explicit redraw is not necessary since the packer
471 ## issues a resize after the scrollbars are unmapped.
472 ##----------------------------------------------------------------------------
473
474 def zoom_reset(s):
475
476 ## global def_button_cmd
477
478 s.label_reset()
479 ## bind [plwin] <ButtonPress> $def_button_cmd
480
481 s.tk.call( s.plf._w, 'view', 'reset' )
482
483 if s.hscroll_exists and atoi( s.tk.call( 'winfo', 'ismapped',
484 s.hscroll._w ) ):
485 s.hscroll.grid_forget()
486
487 if s.vscroll_exists and atoi( s.tk.call( 'winfo', 'ismapped',
488 s.vscroll._w ) ):
489 s.vscroll.grid_forget()
490
491 ## Reset zoom windows list
492
493 s.zidx = 0
494 s.zxl = [ 0. ]; s.zyl = [ 0. ]
495 s.zxr = [ 1. ]; s.zyr = [ 1. ]
496
497 def update_orient(s,m):
498
499 r = s.tk.call( s.plf._w, 'orient' )
500
501 # Grr, this is a floating point string. Must stand on our
502 # heads to get an actual integer out of it.
503
504 f = atof( r )
505 fsi = "%.0f" % f
506 i = atoi( fsi )
507
508 n = i / 90
509 n = i % 4
510
511 m.invoke( n+1 )
512
513 def orient(s, n):
514 """Set the orientation of the plframe, but check to make sure
515 we only do this if the new orientation is different from the
516 old one."""
517
518 oldori = s.tk.call( s.plf._w, 'orient' )
519 oldf = atof( oldori )
520 oldn = atoi( "%.0f" % oldf ) % 4
521
522 if n != oldn:
523 rots = "%d" % n
524 s.tk.call( s.plf._w, 'orient', rots )
525
526 def page_enter(s):
527 print "in page_enter"
528
529 def page_reset(s):
530 print "in page_reset"
531
532 def zoom_start( s, e ):
533 "Starts plot zoom."
534
535 s.wx = e.x
536 s.wy = e.y
537
538 ## Restore previous binding, but don't know how to do this in Python/Tk.
539 ## s.plf.bind( "<ButtonPress>", s.def_button_cmd )
540
541 ## global def_button_cmd
542 ##
543 ## bind [plwin] <ButtonPress> $def_button_cmd
544
545 ## Maybe what I should do for now is just remove the one we instlaled,
546 ## but punt on restoring the prexisting binding.
547
548 s.plf.bind( "<ButtonPress>", None )
549
550 ## Hmpffff. That didn't work... Grrrrrr.
551
552 s.label_set( "Select zoom region by dragging mouse, then release." )
553
554 s.tk.call( s.plf._w, 'draw', 'init' )
555 s.plf.bind( "<B1-Motion>", s.zoom_mouse_draw )
556 s.plf.bind( "<B1-ButtonRelease>", s.zoom_mouse_end )
557
558 ##----------------------------------------------------------------------------
559 ## zoom_coords
560 ##
561 ## Transforms the initial and final mouse coordinates to either:
562 ##
563 ## opt = 0 device coordinates
564 ## opt = 1 normalized device coordinates
565 ##
566 ## The global variable "zoomopts" is used to determine zoom behavior:
567 ##
568 ## zoomopts($this,0):
569 ## 0 box follows mouse movements exactly
570 ## 1 box follows mouse movements so that aspect ratio is preserved (default)
571 ##
572 ## zoomopts($this,1):
573 ## 0 first and last points specified determine opposite corners
574 ## of zoom box.
575 ## 1 box is centered about the first point clicked on,
576 ## perimeter follows mouse (default)
577 ##
578 ##----------------------------------------------------------------------------
579
580 def zoom_coords( s, x0, y0, x1, y1, opt ):
581
582 # Convert the integer input to float, prevents problems with
583 # division.
584
585 x0 = float(x0)
586 y0 = float(y0)
587 x1 = float(x1)
588 y1 = float(y1)
589
590 Lx = s.plf.winfo_width()
591 Ly = s.plf.winfo_height()
592
593 # Enforce boundaries in device coordinate space
594
595 bounds = split( s.tk.call( s.plf._w, 'view', 'bounds' ) )
596 xmin = Lx * atof( bounds[0] )
597 ymin = Ly * atof( bounds[1] )
598 xmax = Lx * atof( bounds[2] )
599 ymax = Ly * atof( bounds[3] )
600
601 x1 = max( xmin, min( xmax, x1 ) )
602 y1 = max( ymin, min( ymax, y1 ) )
603
604 # Two-corners zoom.
605
606 if s.zoomopts_sel == 0:
607 pass
608
609 ## if { $zoomopts($this,1) == 0 } then {
610
611 # Get box lengths
612
613 dx = x1 - x0
614 dy = y1 - y0
615 ## set dx [expr $x1 - $x0]
616 ## set dy [expr $y1 - $y0]
617
618 sign_dx = sign(dx)
619 sign_dy = sign(dy)
620 ## set sign_dx [expr ($dx > 0) ? 1 : -1]
621 ## set sign_dy [expr ($dy > 0) ? 1 : -1]
622
623 xl = x0
624 yl = y0
625 ## set xl $x0
626 ## set yl $y0
627
628 # Constant aspect ratio
629
630 if s.zoomopts_asp == 1:
631 pass
632 ## if { $zoomopts($this,0) == 1 } then {
633 ##
634 ## # Scale factors used to maintain plot aspect ratio
635 ##
636 ## set xscale [expr $xmax - $xmin]
637 ## set yscale [expr $ymax - $ymin]
638 ##
639 ## # Adjust box size for proper aspect ratio
640 ##
641 ## set rx [expr double(abs($dx)) / $xscale]
642 ## set ry [expr double(abs($dy)) / $yscale]
643 ##
644 ## if { $rx > $ry } then {
645 ## set dy [expr $yscale * $rx * $sign_dy]
646 ## } else {
647 ## set dx [expr $xscale * $ry * $sign_dx]
648 ## }
649 ##
650 ## set xr [expr $xl + $dx]
651 ## set yr [expr $yl + $dy]
652 ##
653 ## # Now check again to see if in bounds, and adjust if not
654 ##
655 ## if { $xr < $xmin || $xr > $xmax } then {
656 ## if { $xr < $xmin } then {
657 ## set dx [expr $xmin - $x0]
658 ## } else {
659 ## set dx [expr $xmax - $x0]
660 ## }
661 ## set rx [expr double(abs($dx)) / $xscale]
662 ## set dy [expr $yscale * $rx * $sign_dy]
663 ## }
664 ##
665 ## if { $yr < $ymin || $yr > $ymax } then {
666 ## if { $yr < $ymin } then {
667 ## set dy [expr $ymin - $y0]
668 ## } else {
669 ## set dy [expr $ymax - $y0]
670 ## }
671 ## set ry [expr double(abs($dy)) / $yscale]
672 ## set dx [expr $xscale * $ry * $sign_dx]
673 ## }
674 ## }
675
676 # Final box coordinates
677
678 xr = xl + dx
679 yr = yl + dy
680 ## set xr [expr $xl + $dx]
681 ## set yr [expr $yl + $dy]
682
683 ### zoom from center out, preserving aspect ratio
684 else:
685
686 # Get box lengths, adjusting downward if necessary to keep
687 # in bounds
688
689 dx = abs( x1 - x0 )
690 dy = abs( y1 - y0 )
691
692 xr = x0 + dx;
693 xl = x0 - dx
694 yr = y0 + dy
695 yl = y0 - dy
696
697 if xl < xmin: dx = x0 - xmin
698 if xr > xmax: dx = xmax - x0
699 if yl < ymin: dy = y0 - ymin
700 if yr > ymax: dy = ymax - y0
701
702 # Constant aspect ratio
703
704 if s.zoomopts_asp == 1:
705
706 # Scale factors used to maintain plot aspect ratio
707
708 xscale = xmax - xmin
709 yscale = ymax - ymin
710
711 # Adjust box size for proper aspect ratio
712
713 rx = dx / xscale
714 ry = dy / yscale
715
716 if rx > ry:
717 dy = yscale * rx
718 else:
719 dx = xscale * ry
720
721 xr = x0 + dx
722 xl = x0 - dx
723 yr = y0 + dy
724 yl = y0 - dy
725
726 # Now check again to see if in bounds, and adjust
727 # downward if not
728
729 if xl < xmin:
730 dx = x0 - xmin
731 rx = dx / xscale
732 dy = yscale * rx
733 if yr > ymax:
734 dx = xmax - x0
735 rx = dx / xscale
736 dy = yscale * rx
737 if yl < ymin:
738 dy = y0 - ymin
739 ry = dy / yscale
740 dx = xscale * ry
741 if yr > ymax:
742 dy = ymax - y0
743 ry = dy / yscale
744 dx = xscale * ry
745
746 # Final box coordinates
747
748 xr = x0 + dx
749 xl = x0 - dx
750 yr = y0 + dy
751 yl = y0 - dy
752
753 ## Optional translation to relative device coordinates.
754
755 if opt == 1:
756 wxl = xl / Lx
757 wxr = xr / Lx
758 wyl = 1.0 - float(yr) / Ly
759 wyr = 1.0 - float(yl) / Ly
760 else:
761 wxr = xl
762 wxl = xr
763 wyr = yl
764 wyl = yr
765
766 return wxl, wyl, wxr, wyr
767
768 def zoom_mouse_draw(s,e):
769 "Draws zoom box in response to mouse motion (with button held down)."
770
771 coords = s.zoom_coords( s.wx, s.wy, e.x, e.y, 0 )
772
773 s.tk.call( s.plf._w, 'draw', 'rect',
774 coords[0], coords[1], coords[2], coords[3] )
775
776 def zoom_mouse_end( s, e ):
777 "Performs actual zoom, invoked when user releases mouse button."
778
779 # Finish rubber band draw
780
781 s.plf.bind( "<B1-ButtonRelease>" )
782 s.plf.bind( "<B1-Motion>" )
783 ## bind [plwin] <B1-ButtonRelease> {}
784 ## bind [plwin] <B1-Motion> {}
785 s.label_reset()
786 s.tk.call( s.plf._w, 'draw', 'end' )
787
788 # Select new plot region
789
790 coords = s.zoom_coords( s.wx, s.wy, e.x, e.y, 1 )
791
792 s.view_zoom( coords[0], coords[1], coords[2], coords[3] )
793
794 ### Hmm, view_select is only called by update_view, which isn't called
795 ### by anything...
796 ## def view_select( s, x0, y0, x1, y1 ):
797 ## """Handles change of view into plot.
798 ## Given in relative plot window coordinates."""
799 ##
800 ## print "in view_select"
801 ####body Pltkwin::view_select {x0 y0 x1 y1} {
802 ##
803 #### Adjust arguments to be in bounds and properly ordered (xl < xr, etc)
804 ##
805 #### set xl [min $x0 $x1]
806 #### set yl [min $y0 $y1]
807 #### set xr [max $x0 $x1]
808 #### set yr [max $y0 $y1]
809 ##
810 ## xl = min( x0, x1 ); yl = min( y0, y1 )
811 ## xr = max( x0, x1 ); yr = max( y0, y1 )
812 ##
813 #### set xmin 0.
814 #### set ymin 0.
815 #### set xmax 1.
816 #### set ymax 1.
817 ##
818 ## xmin = 0.; ymin = 0.
819 ## xmax = 1.; ymax = 1.
820 ##
821 #### set xl [max $xmin [min $xmax $xl]]
822 #### set yl [max $ymin [min $ymax $yl]]
823 #### set xr [max $xmin [min $xmax $xr]]
824 #### set yr [max $ymin [min $ymax $yr]]
825 ##
826 ## xl = max( xmin, min( xmax, xl ) )
827 ## yl = max( ymin, min( ymax, yl ) )
828 ## xr = max( xmin, min( xmax, xr ) )
829 ## yr = max( ymin, min( ymax, yr ) )
830 ##
831 #### Only create scrollbars if really needed.
832 ##
833 #### if {($xl == $xmin) && ($xr == $xmax)} \
834 #### then {set hscroll 0} else {set hscroll 1}
835 ####
836 #### if {($yl == $xmin) && ($yr == $xmax)} \
837 #### then {set vscroll 0} else {set vscroll 1}
838 ####
839 #### if { ! ($hscroll || $vscroll)} {return}
840 ##
841 ## if xl == xmin and xr == xmax:
842 ## hscroll = 0
843 ## else:
844 ## hscroll = 1
845 ##
846 ## if yl == ymin and yr == ymax:
847 ## vscroll = 0
848 ## else:
849 ## vscroll = 1
850 ##
851 ## if not (hscroll or vscroll):
852 ## return
853 ##
854 #### Select plot region
855 ##
856 #### [plwin] view select $xl $yl $xr $yr
857 ##
858 ## s.tk.call( s.plf._w, 'view', 'select', xl, yl, xr, yr )
859 ##
860 #### Fix up view
861 ##
862 #### fixview $hscroll $vscroll
863 ## s.fixview( hscroll, vscroll )
864
865 def view_zoom( s, x0, y0, x1, y1 ):
866 "Handles zoom. Given in relative device coordinates."
867
868 ## Adjust arguments to be properly ordered (xl < xr, etc)
869
870 xl = min( x0, x1 )
871 yl = min( y0, y1 )
872 xr = max( x0, x1 )
873 yr = max( y0, y1 )
874
875 ## Check for double-click (specified zoom region less than a few
876 ## pixels wide). In this case, magnification is 2X in each direction,
877 ## centered at the mouse location. At the boundary, the magnification
878 ## is determined by the distance to the boundary.
879
880 stdzoom = .5
881 if (xr - xl) < .02 and (yr - yl) < .02:
882 nxl = xl - .5 * stdzoom
883 nxr = xl + .5 * stdzoom
884 if nxl < 0.:
885 nxl = 0.
886 nxr = 2. * xl
887 if nxr > 1.:
888 nxr = 1.
889 nxl = 2. * xl - 1.
890 xl = nxl
891 xr = nxr
892
893 nyl = yl - .5 * stdzoom
894 nyr = yl + .5 * stdzoom
895 if nyl < 0.:
896 nyl = 0.
897 nyr = 2. * yl
898 if nyr > 1.:
899 nyr = 1.
900 nyl = 2. * yl - 1.
901 yl = nyl
902 yr = nyr
903
904 ## Adjust arguments to be in bounds (in case margins are in effect).
905
906 bounds = split( s.tk.call( s.plf._w, 'view', 'bounds' ) )
907 xmin = atof( bounds[0] )
908 ymin = atof( bounds[1] )
909 xmax = atof( bounds[2] )
910 ymax = atof( bounds[3] )
911
912 xl = max( xmin, min( xmax, xl ) )
913 yl = max( ymin, min( ymax, yl ) )
914 xr = max( xmin, min( xmax, xr ) )
915 yr = max( ymin, min( ymax, yr ) )
916
917 ## Only create scrollbars if really needed.
918
919 hscroll, vscroll = 0, 0
920 if xl != xmin or xr != xmax: hscroll = 1
921 if yl != ymin or yr != ymax: vscroll = 1
922
923 if not (hscroll or yscroll):
924 s.tk.call( s.plf._w, 'redraw' )
925 return
926
927 ## Select plot region
928
929 s.tk.call( s.plf._w, 'view', 'zoom', xl, yl, xr, yr )
930
931 ## Fix up view
932
933 s.fixview( hscroll, vscroll )
934
935 ## Add window to zoom windows list
936
937 coords = split( s.tk.call( s.plf._w, 'view' ) )
938
939 s.zidx = s.zidx + 1
940
941 if s.zidx == len( s.zxl ):
942 # Adding onto the end, no big deal
943 s.zxl.append( atof( coords[0] ) )
944 s.zyl.append( atof( coords[1] ) )
945 s.zxr.append( atof( coords[2] ) )
946 s.zyr.append( atof( coords[3] ) )
947 else:
948 # Adding into the middle...
949 s.zxl[ s.zidx ] = atof( coords[0] )
950 s.zyl[ s.zidx ] = atof( coords[1] )
951 s.zxr[ s.zidx ] = atof( coords[2] )
952 s.zyr[ s.zidx ] = atof( coords[3] )
953
954 if s.zidx < len( s.zxl ) - 1:
955 # Now chop off the end.
956 s.zxl = s.zxl[0:s.zidx+1]
957 s.zyl = s.zyl[0:s.zidx+1]
958 s.zxr = s.zxr[0:s.zidx+1]
959 s.zyr = s.zyr[0:s.zidx+1]
960
961 def zoom_back(s):
962 "Traverse the zoom windows list backward."
963
964 if s.zidx > 0:
965 s.zidx = s.zidx - 1
966
967 xl = s.zxl[ s.zidx ]; yl = s.zyl[ s.zidx ]
968 xr = s.zxr[ s.zidx ]; yr = s.zyr[ s.zidx ]
969
970 # Select plot region
971
972 s.tk.call( s.plf._w, 'view', 'select', xl, yl, xr, yr )
973
974 def zoom_forward(s):
975 "Traverse the zoom windows list forward."
976
977 zframes = len( s.zxl )
978
979 if zframes == 1 or s.zidx == zframes-1:
980 return
981
982 s.zidx = s.zidx + 1
983
984 xl = s.zxl[ s.zidx ]; yl = s.zyl[ s.zidx ]
985 xr = s.zxr[ s.zidx ]; yr = s.zyr[ s.zidx ]
986
987 # Select plot region
988
989 s.tk.call( s.plf._w, 'view', 'select', xl, yl, xr, yr )
990
991 def view_scroll(s):
992 print "in view_scroll"
993
994 def fixview( s, hscroll, vscroll ):
995 "Handles updates of scrollbars & plot after view change."
996
997 ## Create scrollbars if they don't already exist.
998
999 created_sb = 0
1000 if hscroll and not s.hscroll_exists:
1001 s.hscroll = Scrollbar( s, relief=SUNKEN, orient=HORIZONTAL )
1002 s.hscroll['command'] = ( s.plf._w, 'xscroll' )
1003 s.plf.config( xscroll=( s.hscroll, 'set' ) )
1004 s.hscroll_exists = 1
1005 created_sb = 1
1006
1007 if vscroll and not s.vscroll_exists:
1008 s.vscroll = Scrollbar( s, relief=SUNKEN, orient=VERTICAL )
1009 s.vscroll['command'] = ( s.plf._w, 'yscroll' )
1010 s.plf.config( yscroll=( s.vscroll, 'set' ) )
1011 s.vscroll_exists = 1
1012 created_sb = 1
1013
1014 ## When scrollbars are first created, it may be necessary to unmap
1015 ## then map the plframe widget so that it has a chance to initialize
1016 ## the scrollbars before they are mapped.
1017
1018 if created_sb:
1019 s.plf.grid_forget()
1020 s.plf.grid( row=1, column=0, sticky='nsew' )
1021
1022 ## Map scrollbars if not already mapped. To get packing right, need
1023 ## to unmap then remap plot widget. Otherwise need to do explicit
1024 ## redraw.
1025
1026 if hscroll and not atoi( s.tk.call( 'winfo', 'ismapped',
1027 s.hscroll._w ) ) \
1028 or vscroll and not atoi( s.tk.call( 'winfo', 'ismapped',
1029 s.vscroll._w ) ) :
1030 s.update()
1031 s.plf.grid_forget()
1032 if hscroll:
1033 s.hscroll.grid( row=2, column=0, sticky='ew' )
1034 if vscroll:
1035 s.vscroll.grid( row=1, column=1, sticky='ns' )
1036 s.plf.grid( row=1, column=0, sticky='nsew' )
1037 else:
1038 s.tk.call( s.plf._w, 'redraw' )
1039
1040 ## Hmmm. Actually, "update_view" doesn't seem to be used by anything...
1041 ## def update_view(s):
1042 ## """Updates view.
1043 ## Results in scrollbars being added if they are appropriate.
1044 ## Does nothing if the plot window is unchanged from the default."""
1045 ##
1046 ## print "in update_view"
1047 #### set coords [[plwin] view]
1048 ####
1049 #### set xl [lindex "$coords" 0]
1050 #### set yl [lindex "$coords" 1]
1051 #### set xr [lindex "$coords" 2]
1052 #### set yr [lindex "$coords" 3]
1053 ####
1054 #### view_select $xl $yl $xr $yr
1055
1056 def status_msg(s,msg):
1057 s.label_set(msg)
1058 # schedule removal of the message with Tk "after"
1059 s.after( 2500, s.label_reset )
1060
1061 def label_reset(s):
1062 s.ftop.lstat.config( text='' )
1063
1064 def label_set(s,msg):
1065 s.ftop.lstat.config( text=msg )
1066
1067 def plcmap0_edit(s):
1068 print "in plcmap0_edit"
1069
1070 def plcmap1_edit(s):
1071 print "in plcmap1_edit"
1072
1073 ## Now do the PLplot API. Just vector these off to the contained
1074 ## Plframe widget.
1075
1076 def cmd( s, *args ):
1077 "Invoke a subcommand on the plframe widget."
1078 apply( s.tk.call, (s.plf._w, 'cmd',) + _flatten(args) )
1079
1080 def pladv( s, page ):
1081 s.cmd( 'pladv', page )
1082
1083 def plaxes( s, x0, y0, xopt, xtick, nxsub, yopt, ytick, nysub ):
1084 s.cmd( 'plaxes', x0, y0, xopt, xtick, nxsub, yopt, ytick, nysub )
1085
1086 def plbin(s): pass
1087 def plbop(s):
1088 s.cmd( 'plbop' )
1089
1090 def plbox( s, xopt, xtick, nxsub, yopt, ytick, nysub ):
1091 s.cmd( 'plbox', xopt, xtick, nxsub, yopt, ytick, nysub )
1092
1093 def plbox3( s, xopt, xlabel, xtick, nsubx,
1094 yopt, ylabel, ytick, nsuby,
1095 zopt, zlabel, ztick, nsubz ):
1096 s.cmd( 'plbox3',
1097 xopt, xlabel, xtick, nsubx,
1098 yopt, ylabel, ytick, nsuby,
1099 zopt, zlabel, ztick, nsubz )
1100
1101 def plcol0( s, col0 ):
1102 s.cmd( 'plcol0', col0 )
1103
1104 def plcol1( s, col1 ):
1105 s.cmd( 'plcol1', col1 )
1106
1107 # def plcont( s ): pass
1108
1109 ## def plcontxxx( s, z, kx, lx, ky, ly, clev, pltr, xg, yg, wrap ):
1110 ## plsstrm( s.strm )
1111 ## plcont( z, kx, lx, ky, ly, clev, pltr, xg, yg, wrap )
1112
1113 def plcont( s, *args ):
1114 plsstrm( s.strm )
1115 apply( plcont, args )
1116
1117 def plfcont( s ): pass
1118 def plcpstream( s ): pass
1119
1120 def plenv( s, xmin, xmax, ymin, ymax, i, j ):
1121 s.cmd( 'plenv', xmin, xmax, ymin, ymax, i, j )
1122
1123 def pleop(s):
1124 s.cmd( 'pleop' )
1125 #print "should've waited here, but it didn't."
1126 s.plf.setvar( 'wv', '0' )
1127 s.label_set( "Plotting paused ... (Hit Clear to continue)" )
1128 #print "preparing to wait for wv to change"
1129 s.plf.waitvar( 'wv' )
1130 #print "it changed."
1131 s.label_reset()
1132 s.update()
1133
1134 def clearpage(s):
1135 s.plf.setvar( 'wv', 1 )
1136
1137 def plfill( s, x, y ):
1138 plsstrm( s.strm )
1139 plfill( x, y )
1140
1141 def plfont( s, ifnt ):
1142 s.cmd( 'plfont', ifnt )
1143
1144 def plfontld( s, fnt ):
1145 s.cmd( 'plfontld', fnt )
1146
1147 def plhist( s, data, datmin, datmax, nbin, oldwin ):
1148 plsstrm( s.strm )
1149 plhist( data, datmin, datmax, nbin, oldwin )
1150
1151 def plhls( s, h, l, sat ):
1152 s.cmd( 'plhls', h, l, sat )
1153
1154 def pljoin( s, x1, y1, x2, y2 ):
1155 s.cmd( 'pljoin', x1, y1, x2, y2 )
1156
1157 def pllab( s, xlab, ylab, tlab ):
1158 s.cmd( 'pllab', xlab, ylab, tlab )
1159
1160 def plline( s, x, y ):
1161 plsstrm( s.strm )
1162 plline( x, y )
1163
1164 def plline3( s, x, y, z ):
1165 plsstrm( s.strm )
1166 plline3( x, y, z )
1167
1168 def pllsty( s, lin ):
1169 s.cmd( 'pllsty', lin )
1170
1171 # map and merridians ommitted.
1172
1173 def plmesh( s, x, y, z, opt ):
1174 plsstrm( s.strm )
1175 plmesh( x, y, z, opt )
1176
1177 def plmtex( s, side, disp, pos, just, text ):
1178 s.cmd( 'plmtex', side, disp, pos, just, text )
1179
1180 def plot3d( s, x, y, z, opt, side ):
1181 plsstrm( s.strm )
1182 plplot3d( x, y, z, opt, side )
1183
1184 def plplot3d( s, x, y, z, opt, side ):
1185 plsstrm( s.strm )
1186 plplot3d( x, y, z, opt, side )
1187
1188 def plpoin( s, xs, ys, mark ):
1189 plsstrm( s.strm )
1190 plpoin( xs, ys, mark )
1191
1192 def plpoin3( s, x, y, z, code ):
1193 plsstrm( s.strm )
1194 plpoin3( x, y, z, code )
1195
1196 def plpoly3( s, x, y, z, draw ):
1197 plsstrm( s.strm )
1198 plpoly3( x, y, z, draw )
1199
1200 def plprec( s, setp, prec ):
1201 s.cmd( 'plprec', setp, prec )
1202
1203 def plpsty( s, patt ):
1204 s.cmd( 'plpsty', patt )
1205
1206 def plptex( s, x, y, dx, dy, just, text ):
1207 s.cmd( 'plptex', x, y, dx, dy, just, text )
1208
1209 def plreplot( s ):
1210 s.cmd( 'plreplot' )
1211
1212 def plrgb( s, r, g, b ):
1213 s.cmd( 'plrgb', r, g, b )
1214
1215 def plrgb1( s, r, g, b ):
1216 s.cmd( 'plrgb1', r, g, b )
1217
1218 def plschr( s, dflt, scale ):
1219 s.cmd( 'plschr', dflt, scale )
1220
1221 def plshade( s, z, xmin, xmax, ymin, ymax,
1222 sh_min, sh_max, sh_cmap, sh_color, sh_width,
1223 min_col, min_wid, max_col, max_wid, rect,
1224 pltr='pltr0', xg=None, yg=None, wrap=0 ):
1225 "Color filled shad plot."
1226
1227 plsstrm( s.strm );
1228 plshade( z, xmin, xmax, ymin, ymax,
1229 sh_min, sh_max, sh_cmap, sh_color, sh_width,
1230 min_col, min_wid, max_col, max_wid, rect,
1231 pltr, xg, yg, wrap )
1232
1233 ## def plshade2( s, z, xmin, xmax, ymin, ymax,
1234 ## sh_min, sh_max, sh_cmap, sh_color, sh_width,
1235 ## min_col, min_wid, max_col, max_wid, rect,
1236 ## pltr, xg, yg, wrap ):
1237 ## "Was unable to fix plshade, must make new plshade2, grrr."
1238 ##
1239 ## print "in plshade2"
1240 ## plsstrm( s.strm );
1241 ## plshade( z, xmin, xmax, ymin, ymax,
1242 ## sh_min, sh_max, sh_cmap, sh_color, sh_width,
1243 ## min_col, min_wid, max_col, max_wid, rect,
1244 ## pltr, xg, yg, wrap )
1245
1246 # Only works for special conditions so comment it out for now.
1247 # def plsmem( ny, ny, plotmem ):
1248 # s.cmd('plsmem', nx, ny plotmem )
1249
1250 def plssub( s, nx, ny ):
1251 s.cmd( 'plssub', nx, ny )
1252
1253 def plssym( s, dflt, scale ):
1254 s.cmd( 'plssym', dflt, scale )
1255
1256 # plstar and plstart not relevant
1257
1258 #def plstyl( s, ...
1259
1260 def plsvpa( s, xmin, xmax, ymin, ymax ):
1261 s.cmd( 'plsvpa', xmin, xmax, ymin, ymax )
1262
1263 def plsxax( s, digmax, digits ):
1264 s.cmd( 'plsxax', digmax, digits )
1265
1266 def plsyax( s, digmax, digits ):
1267 s.cmd( 'plsyax', digmax, digits )
1268
1269 def plsym( s, x, y, code ):
1270 plsstrm( s.strm )
1271 plsym( x, y, code )
1272
1273 def plszax( s, digmax, digits ):
1274 s.cmd( 'plszax', digmax, digits )
1275
1276 def plvasp( s, aspect ):
1277 s.cmd( 'plvasp', aspect )
1278
1279 def plvpas( s, xmin, xmax, ymin, ymax, aspect ):
1280 s.cmd( 'plvpas', xmin, xmax, ymin, ymax, aspect )
1281
1282 def plvpor( s, xmin, xmax, ymin, ymax ):
1283 s.cmd( 'plvpor', xmin, xmax, ymin, ymax )
1284
1285 def plvsta(s):
1286 s.cmd( 'plvsta' )
1287
1288 def plw3d( s, basex, basey, height, xmin0,
1289 xmax0, ymin0, ymax0, zmin0, zmax0, alt, az):
1290 s.cmd( 'plw3d',
1291 basex, basey, height, xmin0,
1292 xmax0, ymin0, ymax0, zmin0, zmax0, alt, az)
1293
1294 def plwid( s, width ):
1295 s.cmd( 'plwid', width )
1296
1297 def plwind( s, xmin, xmax, ymin, ymax ):
1298 s.cmd( 'plwind', xmin, xmax, ymin, ymax )
1299
1300 def debug(s):
1301 print "Debugging dump for PlXframe:"
1302 print "s.saveopt_dev = ", s.saveopt_dev
1303
1304 ## End of Plframe.py