PLplot  5.10.0
 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 
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 
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 
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 
1068  print "in plcmap0_edit"
1069 
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