2 # Author: Michael H. Hohn (mhhohn@lbl.gov)
4 # Copyright (c) 2006, The Regents of the University of California
6 # See legal.txt and license.txt
10 Miscellaneous functions and classes.
13 import exceptions
, thread
, sys
, os
14 from copy
import copy
, deepcopy
17 import gnomecanvas
as canvas
18 from gnomecanvas
import MOVETO_OPEN
, MOVETO
, LINETO
, CURVETO
20 # Try backported version used in sparx.
22 from canvas
import MOVETO_OPEN
, MOVETO
, LINETO
, CURVETO
25 from l3lang
import utils
, ast
, interp
, view
26 import l3lang
.globals as G
28 #* event separation via thread locks
30 # wrapping whole event handlers, as in
31 # self._root_group.connect("event",
32 # lambda *a: misc.single_event(self.drag_event, *a))
33 # really throws off the handling.
35 # This single-event restriction must be used locally to block specific
36 # other events when a significant (event-affecting) state modification
39 # See l3Nested.drag_event for an example.
41 _the_lck
= thread
.allocate_lock()
42 def single_event(func
, *args
):
43 if _the_lck
.acquire(0):
52 #* data / file content -> l3 conversion
53 #** file type dispatcher
54 def file2ast(fname
, shape
= 'list'):
55 ''' Produce a raw astType for the content of file `fname`.
57 The `shape` argument is 'list' or 'square'; the 'list' form will
58 not change the "natural" shape of the data whereas 'square'
59 adjusts the shape for better space utilization.
61 The returned astType is not .setup().
63 For unrecognized file types, `fname` is returned.
65 assert isinstance(fname
, ast
.String
), "Expecting file name."
68 if not os
.path
.exists(fname
):
71 # File type check by extension.
72 (root
, ext
) = os
.path
.splitext(fname
)
74 _identity
= lambda aa
: aa
76 '.hdf' : _file2ast_hdf
,
77 '.png' : png2pixbuf
, # todo: handle files
79 }[ext
](fname
, shape
= shape
)
82 def _file2ast_hdf(fname
, shape
= 'list'):
83 '''Convert an hdf5 file containing a list of images.
85 The contents are converted to numpy arrays and a new l3 list
86 returned; editing the new list will not affect the original.
88 import l3lang
.external
.hdf_mixins
as hm
89 if not hm
.l3_have_pytables
:
90 G
.logger
.message("pytables is not available. Unable to load hdf file.")
94 fi
= tables
.openFile(fname
)
97 # For the file to be closed here, the l3_getimg() arrays must be
98 # copied as numpy arrays.
100 vals
= [ (ii
, fi
.l3_getimg(ii
)[:,:]) for ii
in range(0, num_img
) ]
102 # Produce list of lists for viewing, if requested.
104 if (M
> 3) and shape
== 'square':
105 cols
= int(floor(sqrt(M
)))
106 vals
= [tuple(vals
[ii
: ii
+ cols
]) for ii
in range(0, M
, cols
)]
109 rv
= ast
.val2ast(vals
)
116 #** array to image conversions
117 #*** numpy array to pixbuf
118 def arr2pix(num_arr
):
119 ''' Convert a 2D float array to a greyscale pixmap. '''
121 assert len(num_arr
.shape
) == 2, "Expected 2D array."
123 # Scale float array to 8-bits.
124 rows
, cols
= num_arr
.shape
125 bottom
= N
.min(num_arr
)
127 scale
= (top
- bottom
)
128 norm_arr
= ((num_arr
- bottom
) / scale
* 255).astype(N
.uint8
)\
131 # Use 3 8-bit arrays as (rgb) values.
132 t3
= N
.outer(norm_arr
, N
.array([1,1,1], 'b'))
133 t4
= N
.reshape(t3
, (rows
, cols
, 3))
134 pix_arr
= N
.array(t4
, dtype
=N
.uint8
) #
137 # array input indexing is row, pixel, [r,g,b {,a} ]
138 pixbuf
= gtk
.gdk
.pixbuf_new_from_data(
139 pix_arr
.tostring(order
='a'),
140 gtk
.gdk
.COLORSPACE_RGB
,
150 #*** emdata conversions
151 def emdata2pixbuf(emd
):
152 from EMAN2
import EMNumPy
, EMData
156 assert isinstance(emd
, EMData
), "Expected EMdata instance"
157 # eman -> numeric -> gtk
158 num_arr
= EMNumPy
.em2numpy(emd
)
159 return arr2pix(num_arr
)
162 def png2pixbuf(png_file
, shape
= 'list'):
163 import matplotlib
.image
as mi
165 if isinstance(png_file
, ast
.String
):
166 png_file
= png_file
.py_string()
167 img_arr
= mi
.imread(png_file
)
168 rows
, cols
, bytes
= img_arr
.shape
170 # Scale from [0,1] to [0,255]
171 img_arr
= N
.array(255 * img_arr
, dtype
=N
.uint8
)
174 # array input indexing is row, pixel, [r,g,b {,a} ]
175 pixbuf
= gtk
.gdk
.pixbuf_new_from_data(
176 img_arr
.tostring(order
='a'),
177 gtk
.gdk
.COLORSPACE_RGB
,
185 ## misc.png2pixbuf = png2pixbuf
187 # # pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,
188 # # has_alpha = True,
189 # # bits_per_sample = 8,
193 # # array = pixbuf_get_pixels_array(pixbuf)
194 # # array[:,:,:] = image_array
198 def escape_markup(text
):
199 '''Change html-like markup in `text` so that GnomeCanvasText ignores it.
200 See http://developer.gnome.org/doc/API/2.0/pango/PangoMarkupFormat.html
202 return text
.replace("<", "<")
205 def triple_quote(text
):
206 return '"""%s"""' % text
208 class DisplayError(exceptions
.Exception):
209 def __init__(self
, e_args
=None):
212 return repr(self
.e_args
)
216 # Update bounding boxes, among others.
217 while gtk
.events_pending():
220 def python_main_loop():
223 gtk
.main_iteration_do()
227 def destroy_if_last(obj
):
229 rc
= sys
.getrefcount(obj
)
230 print "refcount: ", rc
232 # Ignore local obj, getrefcount arg
242 print >> sys
.stderr
, "Warning: ", strng
245 def slot_available(w_
, old_entry
):
246 if w_
.fluid_ref(insert_l3_tree
= True):
247 if not isinstance(old_entry
, ast
.aNone
):
248 raise DisplayError("Insertion in occupied slot.")
251 def canvas_item_get_bounds_world(item
):
252 i2w
= item
.get_property("parent").i2w
253 x1
, y1
, x2
, y2
= item
.get_bounds()
256 return u1
, v1
, u2
, v2
259 #* comment -> node connection
260 def associate_comments(w_
, tree
, src_string
):
261 from l3lang
import reader
, interp
, ast
262 tm_play_env
= interp
.get_child_env(w_
.state_
.def_env
,
264 for nd
, comment
in reader
.parse_comments(tree
, src_string
):
265 ctree
= w_
.deco_table_
[nd
._id
] = ast
.Comment(comment
)
266 ctree
.setup(ast
.empty_parent(),
273 def affine_rotate(theta
):
274 # Return rotation tuple for theta degrees.
275 s
= sin (theta
* pi
/ 180.0)
276 c
= cos (theta
* pi
/ 180.0)
277 return (c
, s
, -s
, c
, 0, 0)
279 #* rounded corner rectangle
280 # Rectangle w/ rounded corners, leaving the bezier control points on
282 # Note: using one definition, say on [0,1]x[0,1] and scaling will also
283 # scale the rounded edges, but these must have absolute size. [ie. the
284 # rounding size must be independent of the rectangle size.]
285 def path_rectangle_rounded(el
, er
,
286 et
, eb
, # edge positions
287 cv
, ch
, # corner point indentation
288 sv
, sh
, # spline control point indentation
293 # Suppress rounded corner for extreme cases.
294 if not ((er
- el
) > 2*ch
):
297 if not ((eb
- et
) > 2*cv
):
300 path
= [(MOVETO
, el
, et
+ cv
),
302 (LINETO
, el
, eb
- cv
),
303 (CURVETO
, # bottom left
309 (LINETO
, er
- ch
, eb
), # bottom right
316 (LINETO
, er
, et
+ cv
),
317 (CURVETO
, # top right
323 (LINETO
, el
+ ch
, et
),
324 (CURVETO
, # top left
332 #* file selection popup
333 def file_selection(call_w_selection
):
335 # Let the user select a filename NAME, and call
336 # call_w_selection(NAME)
338 fs
= gtk
.FileSelection("Select file")
341 fname
= fs
.get_filename()
342 print ("Selected filename: %s\n" % fname
)
343 call_w_selection(fname
)
345 fs
.ok_button
.connect( "clicked", wrap
)
346 fs
.ok_button
.connect_after( "clicked",
347 lambda *a
: fs
.destroy())
348 fs
.cancel_button
.connect_after( "clicked",
349 lambda *a
: fs
.destroy())
356 def validate_input(text
):
357 # Check the input text; use interactive widget if fixing is
362 #* selection handling
363 #** Selectable interface
366 raise Exception("Interface only.")
368 def plain_view(self
):
369 raise DisplayError("interface only")
372 raise DisplayError("interface only")
375 raise DisplayError("interface only")
381 """ Item selection handler.
383 This class handles the selection data, holding zero or more
384 selected L3 display types.
386 Typically, Button-1 selects one item, clearing the existing selection.
387 Ctrl-Button-1 selects one item, adding it to the existing
391 def __init__(self
, w_
):
392 self
._current
_node
_l = [] # Selectable list
395 def have_selection(self
):
396 return len(self
._current
_node
_l) > 0
397 Selector
.have_selection
= have_selection
400 if self
.have_selection():
401 [foo
.plain_view() for foo
in self
._current
_node
_l]
402 self
._current
_node
_l = []
403 Selector
.unselect
= unselect
406 #*** Single-selection interface
407 def set_selection(self
, item
):
408 ''' Clear existing selection and add item. '''
409 assert isinstance(item
, Selectable
)
410 # Adjust existing node.
411 if self
.have_selection():
412 [foo
.plain_view() for foo
in self
._current
_node
_l]
414 # Set up new selection.
415 self
._current
_node
_l = [item
]
418 # Highlight associated tree also.
419 source_id_l
= item
._l3tree
.getthe('value_source_tree_id')
421 for source_id
in source_id_l
:
422 linked_pic
= utils
.key_of(source_id
, item
._canvas
._nodes
)
424 self
.add_to_selection(linked_pic
)
425 Selector
.set_selection
= set_selection
427 def get_selection(self
):
428 if self
.have_selection():
429 return self
._current
_node
_l[-1]
432 Selector
.get_selection
= get_selection
435 #*** Multi-selection interface
436 def add_to_selection(self
, item
):
437 """ Add item to selection if it is not there. """
438 assert isinstance(item
, Selectable
)
439 if self
.have_selection():
440 if item
not in self
._current
_node
_l:
441 self
._current
_node
_l.append(item
)
444 self
._current
_node
_l = [item
]
446 Selector
.add_to_selection
= add_to_selection
448 def remove(self
, item
):
449 """ Remove item from selection. """
450 assert isinstance(item
, Selectable
)
451 if self
.have_selection():
452 if item
in self
._current
_node
_l:
453 self
._current
_node
_l.remove(item
)
455 Selector
.remove
= remove
457 def toggle_selection(self
, item
):
459 Add item to selection if not there; remove it if present already.
461 assert isinstance(item
, Selectable
)
462 if self
.have_selection():
463 if item
not in self
._current
_node
_l:
464 self
._current
_node
_l.append(item
)
467 self
._current
_node
_l.remove(item
)
470 self
._current
_node
_l = [item
]
472 Selector
.toggle_selection
= toggle_selection
474 def get_selection_list(self
):
477 return copy(self
._current
_node
_l)
478 Selector
.get_selection_list
= get_selection_list
481 #** copy selection to clipboard
484 # GtkClipboard prematurely finalized
486 gtk_primary_clipboard
= gtk
.Clipboard(selection
='PRIMARY')
487 gtk_clipboard_clipboard
= gtk
.Clipboard(selection
='CLIPBOARD')
489 def copy_selection(self
):
490 from l3lang
import reader
, interp
, ast
491 if not self
.have_selection():
493 s_tree
= self
.get_selection().l3tree()
495 # Get the pretty-printed string and verify the tree.
496 comm_dct
= self
.w_
.deco_table_
498 pp_string
= s_tree
.get_infix_string(self
.w_
.pprint_width
,
500 pp_tree
= reader
.parse(pp_string
) ## .single_program()
501 pp_env
= interp
.get_child_env(self
.w_
.state_
.def_env
,
502 self
.w_
.state_
.storage
)
503 pp_tree
.setup(ast
.empty_parent(), pp_env
, self
.w_
.state_
.storage
)
504 associate_comments(self
.w_
, pp_tree
, pp_string
)
505 diff
= s_tree
.tree_difference(comm_dct
, pp_tree
.body(), comm_dct
)
507 gtk_primary_clipboard
.set_text(pp_string
)
508 gtk_clipboard_clipboard
.set_text(pp_string
)
510 G
.logger
.warn("Unable to copy tree:\n%s\n" % diff
)
511 Selector
.copy_selection
= copy_selection
514 #* Property getting / setting
515 def _pickle_these(*args
): ### test only
517 utils
.file_pickle( data
)
519 def _get_props(item
, prop_list
, *properties
):
520 # Return a (property, value) list
521 # When property is of the form "*-set", it is queried and only if
522 # true is the following property used.
523 # See also _set_props()
526 for pp
in properties
:
531 if len(pp
) > 4 and pp
[-4:None] == "-set":
532 if not item
.get_property(pp
):
536 prop
= item
.get_property(pp
)
538 print "_get_props Failed for item ", item
,
539 print ", property: ", pp
542 # Retrieve non-pickling properties as text.
543 # [ first needed for .set_property("anchor", 'GTK_ANCHOR_NORTH_WEST')]
545 prop
= prop
.value_name
547 prop_list
.append( (pp
, prop
) )
550 def _set_props(item
, prop_list
):
551 # Update the ITEM's properties.
553 # prop_list: (property, value) list
555 # See also _get_props().
557 for (prop
, value
) in prop_list
:
559 item
.set_property(prop
, value
)
560 except Exception, foo
:
561 print foo
, prop
, value
563 #* unique dictionary insertion
564 def unique_set(dic
, key
, val
):
566 raise KeyError("Key `%s` exists in dictionary." % key
)
569 ## misc.unique_set = unique_set
574 if obj
is None: return
578 # Only dereference the gtk object while it is alive.
580 def __init__(self
, obj
):
583 obj
.connect("destroy", self
._destroyed
)
585 def __getattr__(self
, nm
):
587 return self
._obj
.__getattribute
__(nm
)
589 raise Exception("Accessing dead gtk object.")
591 def _destroyed(self
, widget
):
595 #* main I/O entry points.
598 def load_script(w_
, filename
):
599 from l3lang
import reader
, interp
, ast
601 text
= "".join(open(filename
, "r").readlines())
602 tm_tree
= reader
.parse(text
)
603 tm_play_env
= interp
.get_child_env(w_
.state_
.def_env
,
605 tm_tree
.setup(ast
.empty_parent(), tm_play_env
, w_
.state_
.storage
)
606 tm_tree
.set_outl_edges(w_
, None)
608 # Comment association.
609 associate_comments(w_
, tm_tree
, text
)
612 tm_tree_l3pic
= w_
.canvas
.view
.start_add_l3tree(tm_tree
)
616 def load_library(w_
, filename
, maxdepth
= 1):
617 ''' Load library entries from file, display without Program.'''
618 # See also add_l3tree_clipboard
619 from l3lang
import reader
, interp
, ast
622 text
= "".join(open(filename
, "r").readlines())
623 tm_tree
= reader
.parse(text
)
624 tm_play_env
= interp
.get_child_env(w_
.state_
.def_env
,
626 tm_tree
.setup(ast
.empty_parent(), tm_play_env
, w_
.state_
.storage
)
627 tm_tree
.set_outl_edges(w_
, None)
629 # Comment association.
630 associate_comments(w_
, tm_tree
, text
)
632 # Display independent nodes.
634 for tree
in [ee
for ee
in tm_tree
.entries()]: # copy for iteration
636 tree
.detach_from_parent_rec(w_
.state_
.storage
)
638 # Limit tree display depth.
639 if isinstance(tree
, (ast
.cls_viewList
, ast
.Program
)):
640 tree
.outl_flat_display(maxdepth
)
643 l3pic
= w_
.lib_canvas
.view
.start_add_l3tree(tree
)
647 pl
, pt
, pr
, pb
= l3pic
.get_bounds_world()
648 l3pic
.move(0.0, shift_y
)
649 shift_y
+= pb
- pt
+ w_
.cp_
.marker_padding
655 def save_state(w_
, filename
):
657 return utils
.file_cPickle( get_current_state(w_
), filename
= filename
)
659 # # return utils.file_pickle( get_current_state(w_), filename = filename)
661 def get_current_state(w_
):
663 ### selection saving must be via REFERENCE -- maybe use _id
665 ### selection = w_.selector.get_selection().get_state(),
670 deco_table_
= w_
.deco_table_
,
673 canvas_view
= w_
.canvas
.view
.get_state(),
674 lib_canvas_view
= w_
.lib_canvas
.view
.get_state(),
679 def load_state(w_
, filename
):
680 data
= utils
.file_unpickle(filename
)
684 # Clear existing canvas content first.
686 w_
.canvas
.view
.clear_state( )
687 w_
.lib_canvas
.view
.clear_state( )
689 # Restore previous data structures.
690 w_
.cp_
.pull(data
.cp_
)
691 w_
.state_
.pull(state_
)
692 w_
.deco_table_
= data
.deco_table_
694 # Restore non-persistent environment.
695 interp
.setup_envs(state_
.storage
, state_
.root_env
, state_
.def_env
)
697 # And rebuild the canvas' display and data.
698 w_
.canvas
.view
.set_state ( data
.canvas_view
)
699 w_
.lib_canvas
.view
.set_state ( data
.lib_canvas_view
)
701 ### selection saving...
702 # if data.selection != None:
703 # selection = w_.selector.get_selection().get_state(),
704 # w_.selector.set_selection (data.selection)
706 # Ugly hack to force update
707 for canv
in [w_
.canvas
.view
, w_
.lib_canvas
.view
]:
708 canv
._pixpu
= (1.00001 * canv
._pixpu
)
709 canv
.set_pixels_per_unit(canv
._pixpu
)
710 # Restore missing graph edges.
726 def print_display(w_
):
727 # Produce a postscript dump of the text of both canvases on stdout.
729 # A full "screenshot" including all graphics requires more
730 # adjustments. In particular:
731 # -- for 'show', the PS position is (left, baseline);
732 # the gc (gnome canvas) position is (center, center)
733 # -- stroke width has to be included
738 % Change orientation, (0,0) in upper left
739 0 11 72 mul translate
741 1 4 div dup scale % adjust to fit page
744 /Courier findfont fscale 1.7 mul scalefont setfont
747 % Change orientation, y increases down.
748 % Scale so 1 unit == font height + depth
756 { fntunit -1 mul moveto } def
759 { fntunit -1 mul lineto } def
761 /gcshow % string : --
762 { % move point from (left, baseline) to (left, top)
763 0 fscale 1 mul neg rmoveto
767 # left canvas: w_.lib_canvas.view.get_state(),
769 w_
.with_fluids(lambda : w_
.canvas
.view
.get_state(),
773 #* stdin/out/error handling
777 def __init__(self
, w_
):
780 self
._sys
= (sys
.stdin
, sys
.stdout
, sys
.stderr
)
781 StdInOutErr
.__init
__ = __init__
784 def std_connection(self
):
786 (sys
.stdin
, sys
.stdout
, sys
.stderr
) = self
._sys
787 StdInOutErr
.std_connection
= std_connection
791 # Store stdin/out/err. Used by other instances before they grab
793 self
._stack
.append( (sys
.stdin
, sys
.stdout
, sys
.stderr
) )
794 StdInOutErr
.push
= push
798 # Restore stdin/out/err. Used by other instances to release
800 (sys
.stdin
, sys
.stdout
, sys
.stderr
) = self
._stack
.pop()
801 StdInOutErr
.pop
= pop
803 #* application-level glue functions / classes
804 #** Console visibility
805 def show_hide_console(w_
):
806 bot
= w_
.vpane
.get_property("max-position")
807 prop
= w_
.consoles
.proportion_y
808 if bot
== w_
.vpane
.get_property("position"):
810 top
= w_
.vpane
.get_property("min-position")
811 w_
.vpane
.set_property("position", prop
* top
+ (1 - prop
) * bot
)
814 w_
.vpane
.set_property("position", bot
)
816 def show_program_only(w_
):
817 # Hide vertical pane.
818 w_
.vpane
.set_property("position",
819 w_
.vpane
.get_property("max-position"))
820 # Hide library canvas.
821 w_
.hpane
.set_property("position",
822 w_
.hpane
.get_property("min-position"))
825 def show_consoles(w_
):
826 top
= w_
.vpane
.get_property("min-position")
827 bot
= w_
.vpane
.get_property("max-position")
828 pos
= w_
.vpane
.get_property("position")
829 prop
= w_
.consoles
.proportion_y
830 min_pos
= prop
* top
+ (1 - prop
) * bot
832 # enlarge visible area
833 w_
.vpane
.set_property("position", min_pos
)
835 #** Custom notebook for consoles
836 class ConsoleNtbk(gtk
.Notebook
):
839 def __init__(self
, w_
):
840 gtk
.Notebook
.__init
__(self
)
842 self
.set_tab_pos(gtk
.POS_LEFT
)
843 self
._entries
= {} # (name -> widget) dict
844 ConsoleNtbk
.__init
__ = __init__
846 def add_page(self
, widget
, name
):
847 # Add and track `widget` under label `name`.
848 assert not self
._entries
.has_key(name
), "Page already exists."
849 label
= gtk
.Label(name
)
850 label
.set_padding(2, 2)
851 self
.append_page(widget
, label
)
852 self
._entries
[name
] = widget
853 ConsoleNtbk
.add_page
= add_page
855 def page_index(self
, name
):
856 assert self
._entries
.has_key(name
)
857 idx
= self
.page_num(self
._entries
[name
])
858 assert idx
!= -1 # Invalid _entries.
860 ConsoleNtbk
.page_index
= page_index
862 def destroy_page(self
, name
):
863 idx
= self
.page_index(name
)
864 self
.remove_page(idx
)
865 self
._entries
[name
].destroy()
866 del self
._entries
[name
]
867 ConsoleNtbk
.destroy_page
= destroy_page
870 #** l3 console with notebook / canvas / l3List connections
871 def add_l3appconsole(w_
):
872 from l3lang
import reader
873 from l3gui
.cruft
.pylab
import l3Console
874 from l3gui
import widgets
, misc
875 # Introduce a new Program.
876 storage
= w_
.state_
.storage
877 def_env
= w_
.state_
.def_env
878 program
= reader
.empty_program().setup(ast
.empty_parent(),
881 program
.set_outl_edges(w_
, None)
882 # no comments: associate_comments(w_, program, text)
883 program_l3pic
= w_
.canvas
.view
.start_add_l3tree(program
)
885 # Set up the console.
886 name_s
= "l3 Console"
887 console
= l3Console( w_
, locals(),
889 lambda *args
: (w_
.notebk_consoles
.destroy_page(name_s
),
892 w_
.notebk_consoles
.add_page(console
, name_s
)
893 w_
.stdinouterr
.push()
894 console
.std_to_widget()
895 console
.write("==== l3 Console; exit with end-of-file (ctrl-d) ====\n",
899 # Connect Program and console.
900 console
.associate_alist(program_l3pic
._alist
, def_env
, storage
)
902 # Display the console.
903 misc
.show_consoles(w_
)
904 cs
= w_
.notebk_consoles
905 cs
.set_current_page(cs
.page_index(name_s
))
909 #** terminal console with notebook / canvas / l3List connections
910 def new_termconsole(w_
):
912 Start a new Program, and connect it to the main terminal.
913 Terminal history accumulates as entries into the Program.
915 from l3lang
import reader
916 # Introduce a new Program.
917 storage
= w_
.state_
.storage
918 def_env
= w_
.state_
.def_env
919 program
= reader
.empty_program().setup(ast
.empty_parent(),
922 program
.set_outl_edges(w_
, None)
923 # no comments: associate_comments(w_, program, text)
924 program_l3pic
= w_
.canvas
.view
.start_add_l3tree(program
)
926 # Set up the console.
927 from l3lang
import repl
928 console
, interpreter
= repl
.get_console_interpreter_pair(def_env
, storage
)
929 def_env
.import_external('console', console
)
930 def_env
.import_external('interp', interpreter
)
932 # Connect Program and console.
933 disp_alist
= program_l3pic
._alist
935 # Display the console.
936 w_
.stdinouterr
.push()
937 w_
.stdinouterr
.std_connection()
939 def root_display(cons_prog
):
940 body
= cons_prog
.body()
942 # Ignore empty programs.
943 if isinstance(body
, ast
.aNone
):
944 storage
.remove(cons_prog
._id
)
947 # Extract body from Program.
948 cons_prog
._primary
[0].detach_child(body
._id
, storage
)
950 # Delete Program(). This avoids leaving unpickleable objects
952 storage
.remove(cons_prog
._id
)
955 body
.set_outl_edges(w_
, None)
956 body_l3pic
= w_
.canvas
.view
.start_add_l3tree(body
)
957 disp_alist
.append(body_l3pic
)
960 console
.interact(display_via
= root_display
,
961 do_event
= flush_events
,
963 (lambda tree
, src
: associate_comments(w_
, tree
, src
)),
967 #* Visual interpretation tracing
969 ''' Indicate progress by selecting the node about to be interpreted.
973 def __init__(self
, w_
, l3pic
):
976 self
._l3pic
= l3pic
# The (l3Base) display.
977 DisplayInterp
.__init
__ = __init__
979 def __call__(self
, l3tree
, env
, storage
):
980 # todo: the context of the call could be shown to indicate loop
981 # progress. Or display an iteration count as part of the
984 # Tree still visible?
986 if hasattr(self
._l3pic
, '_root_group'):
987 self
.w_
.selector
.set_selection(self
._l3pic
)
988 gtk
.main_iteration(block
= False)
989 DisplayInterp
.__call
__ = __call__
991 def __getstate__(self
):
992 # A DisplayInterp must be an in-memory only attachment.
993 return {'_active' : False}
994 DisplayInterp
.__getstate
__ = __getstate__
996 def __setstate__(self
, stuff
):
997 self
.__dict
__.update(stuff
)
998 DisplayInterp
.__setstate
__ = __setstate__
1000 def __deepcopy__(self
, memo
):
1001 return DisplayInterp(self
.w_
, self
._l3pic
)
1002 DisplayInterp
.__deepcopy
__ = __deepcopy__
1004 #* Copy node and deco_table entries
1005 def node_copy(w_
, orig
, ):
1006 node
, nid
= deepcopy(orig
).\
1007 setup(ast
.empty_parent(),
1011 # Copy relevant table entries.
1012 oit
= orig
.top_down()
1013 nit
= node
.top_down()
1017 id_o
= oit
.next()._id
1018 if dt
.has_key(id_o
):
1019 # Copy node's comment.
1020 comm
= deepcopy(dt
[id_o
])
1021 comm
.setup(ast
.empty_parent(),
1024 dt
[nit
.next()._id
] = comm
1027 except StopIteration:
1030 # Set outline edges.
1031 node
.set_outl_edges(w_
, None)