diff --git a/traitsui/extras/demo.py b/traitsui/extras/demo.py index 70d707767..6c80a367c 100644 --- a/traitsui/extras/demo.py +++ b/traitsui/extras/demo.py @@ -24,6 +24,7 @@ import glob from io import StringIO import operator +import os from os import listdir from os.path import ( abspath, @@ -60,6 +61,7 @@ Str, ) from traitsui.api import ( + Action, CodeEditor, Handler, Heading, @@ -69,10 +71,14 @@ Include, InstanceEditor, Item, + ModelView, ObjectTreeNode, spring, + ShellEditor, + spring, Tabbed, TitleEditor, + ToolBar, TreeEditor, TreeNodeObject, UIInfo, @@ -370,7 +376,7 @@ class DemoFileBase(DemoTreeNodeObject): parent = Any() #: Name of file system path to this file: - path = Property() + path = Property(depends_on='parent.path,name') #: Name of the file: name = Str() @@ -384,6 +390,12 @@ class DemoFileBase(DemoTreeNodeObject): #: Description of what the demo does: description = HTML() + #: The base URL for links: + base_url = Property(depends_on='path') + + #: The css file for this node. + css_filename = Str("default.css") + #: Log of all print messages displayed: log = Code() @@ -399,6 +411,13 @@ def init(self): def _get_path(self): return join(self.parent.path, self.name) + def _get_base_url(self): + if isdir(self.path): + base_dir = self.path + else: + base_dir = dirname(self.path) + return base_dir + # ------------------------------------------------------------------------- # Implementation of the 'nice_name' property: # ------------------------------------------------------------------------- @@ -437,10 +456,13 @@ class DemoFile(DemoFileBase): #: Demo object whose traits UI is to be displayed: demo = Instance(HasTraits) + #: Local namespace for executed code: + locals = Dict(Str, Any) + def init(self): super(DemoFile, self).init() description, source = parse_source(self.path) - self.description = publish_html_str(description) + self.description = publish_html_str(description, self.css_filename) self.source = source self.run_code() @@ -469,6 +491,7 @@ def run_code(self): traceback.print_exc() else: self.demo = demo + self.locals = locals # ------------------------------------------------------------------------- # Get a specified object from the execution dictionary: @@ -492,25 +515,28 @@ def _get_object(self, name, dic): # HTML template for displaying an image file: _image_template = """
+ - + """ class DemoContentFile(DemoFileBase): + def init(self): super(DemoContentFile, self).init() file_str = _read_file(self.path) - self.description = publish_html_str(file_str) + self.description = publish_html_str(file_str, self.css_filename) class DemoImageFile(DemoFileBase): + def init(self): super(DemoImageFile, self).init() - self.description = _image_template.format(self.path) + self.description = _image_template.format(self.css_filename, self.path) class DemoPath(DemoTreeNodeObject): @@ -523,7 +549,16 @@ class DemoPath(DemoTreeNodeObject): parent = Any() #: Name of file system path to this package: - path = Property() + path = Property(depends_on='parent.path,name') + + #: Description of what the demo does: + description = Property(HTML, depends_on="_description") + + #: The base URL for links: + base_url = Property(depends_on='path') + + #: The css file for this node. + css_filename = Str("default.css") #: Name of the directory: name = Str() @@ -531,9 +566,6 @@ class DemoPath(DemoTreeNodeObject): #: UI form of the 'name': nice_name = Property() - #: Description of the contents of the directory: - description = Property(HTML, depends_on="_description") - #: Source code contained in the '__init__.py' file: source = Property(Code) @@ -553,6 +585,9 @@ class DemoPath(DemoTreeNodeObject): #: Configuration file for this node. config_filename = Str() + #: The css file for this node. + css_filename = Str("default.css") + #: Shadow trait for description property _description = Str() @@ -586,6 +621,13 @@ def _get_path(self): return path + def _get_base_url(self): + if isdir(self.path): + base_dir = self.path + else: + base_dir = dirname(self.path) + return base_dir + # ------------------------------------------------------------------------- # Implementation of the 'nice_name' property: # ------------------------------------------------------------------------- @@ -610,10 +652,15 @@ def _set_nice_name(self, value): @cached_property def _get_description(self): - if self._description is None: + if not self._description: self._get_init() - return publish_html_str(self._description) + if self.css_filename: + result = publish_html_str( + self._description, self.css_filename) + else: + result = publish_html_str(self._description) + return result # ------------------------------------------------------------------------- # Implementation of the 'source' property: @@ -647,7 +694,7 @@ def _get_init(self): join(self.path, "__init__.py") ) else: - self._description = '' + self._description = ".. image:: traits_ui_demo.jpg" source = "" self._source = exec_str + source @@ -701,11 +748,18 @@ def get_children_from_datastructure(self): cur_path = join(path, name) if isdir(cur_path): if self.has_py_files(cur_path): - dirs.append(DemoPath(parent=self, name=name)) + dirs.append( + DemoPath( + parent=self, + name=name, + css_filename=join('..', self.css_filename) + ) + ) elif self.use_files: if name != "__init__.py": try: demo_file = self._handle_file(name) + demo_file.css_filename = self.css_filename files.append(demo_file) except KeyError: pass @@ -743,7 +797,11 @@ def get_children_from_config(self): sourcedir = value.pop("sourcedir", None) if sourcedir is not None: # This is a demo directory. - demoobj = DemoPath(parent=self, name=sourcedir) + demoobj = DemoPath( + parent=self, + name=sourcedir, + css_filename=join("..", self.css_filename), + ) demoobj.nice_name = keyword demoobj.config_dict = value dirs.append(demoobj) @@ -764,11 +822,13 @@ def get_children_from_config(self): demoobj = DemoPath(parent=self, name="") demoobj.nice_name = keyword demoobj.config_dict = config_dict + demoobj.css_filename = os.path.join("..", self.css_filename) dirs.append(demoobj) elif len(names) == 1: try: demo_file = self._handle_file(name) files.append(demo_file) + demo_file.css_filename = self.css_filename except KeyError: pass @@ -804,39 +864,43 @@ def _handle_file(self, filename): demo_file = file_factory(parent=self, name=filename) return demo_file + # ------------------------------------------------------------------------- # Defines the demo tree editor: # ------------------------------------------------------------------------- demo_path_view = View( - Tabbed( - UItem( - "description", - style="readonly", - editor=HTMLEditor(format_text=True), + UItem( + "description", + style="readonly", + editor=HTMLEditor( + format_text=True, + base_url_name='base_url', ), - UItem("source", style="custom"), ), id="demo_path_view", + kind='subpanel', ) demo_file_view = View( HSplit( - Tabbed( - UItem( - "description", - style="readonly", - editor=HTMLEditor(format_text=True), + UItem( + "description", + style="readonly", + editor=HTMLEditor( + format_text=True, + base_url_name='base_url', ), ), VSplit( VGroup( - Tabbed( - UItem("source", style="custom"), - ), - UItem( - "handler.run_button", - visible_when="source is not None" + UItem("source", style="custom"), + HGroup( + spring, + UItem( + "handler.run_button", + ), + visible_when="source is not None", ), ), Tabbed( @@ -850,17 +914,24 @@ def _handle_file(self, filename): label="Output", show_label=False ), + Item( + "locals", + editor=ShellEditor(share=True), + label="Shell", + show_label=False + ), UItem( "demo", style="custom", resizable=True, ), ), - dock="horizontal" + dock="horizontal", ), ), id="demo_file_view", handler=demo_file_handler, + kind='subpanel', ) demo_content_view = View( @@ -868,10 +939,14 @@ def _handle_file(self, filename): UItem( "description", style="readonly", - editor=HTMLEditor(format_text=True), + editor=HTMLEditor( + format_text=True, + base_url_name='base_url', + ), ), ), handler=demo_file_handler, + kind='subpanel', ) @@ -880,7 +955,7 @@ def _handle_file(self, filename): ObjectTreeNode( node_for=[DemoPath], label="nice_name", - view=demo_path_view + view=demo_path_view, ), ObjectTreeNode( node_for=[DemoFile], @@ -898,31 +973,45 @@ def _handle_file(self, filename): view=demo_content_view ), ], - selected='selected_node' + selected='selected_node', + ) -class Demo(HasPrivateTraits): +next_tool = Action( + name='Next', + image=ImageResource("next"), + tooltip="Go to next file", + action="do_next", + enabled_when="_next_node is not None", +) - # ------------------------------------------------------------------------- - # Trait definitions: - # ------------------------------------------------------------------------- +previous_tool = Action( + name='Previous', + image=ImageResource("previous"), + tooltip="Go to next file", + action="do_previous", + enabled_when="_previous_node is not None", +) - #: Navifate to next node. - next_button = Button(image=ImageResource("next"), label="Next") +parent_tool = Action( + name='Parent', + image=ImageResource("parent"), + tooltip="Go to next file", + action="do_parent", + enabled_when="(selected_node is not None) and " + "(object.selected_node.parent is not None)", +) - #: Navigate to parent of selected node. - parent_button = Button(image=ImageResource("parent"), label="Parent") - #: Navigate to previous node. - previous_button = Button(image=ImageResource("previous"), label="Previous") +class Demo(ModelView): + + #: Root path object for locating demo files: + model = Instance(DemoPath) #: Path to the root demo directory: path = Str() - #: Root path object for locating demo files: - root = Instance(DemoPath) - #: Selected node of the demo path tree. selected_node = Any() @@ -933,6 +1022,20 @@ class Demo(HasPrivateTraits): _previous_node = Property() + def do_next(self, event=None): + self.selected_node = self._next_node + + def do_previous(self, event=None): + self.selected_node = self._previous_node + + def do_parent(self, event=None): + if self.selected_node is not None: + parent = self.selected_node.parent + self.selected_node = parent + + def init(self, info): + info.ui.title = self.title + def _get__next_node(self): next = None node = self.selected_node @@ -971,65 +1074,26 @@ def _get__previous_node(self): return previous - def _next_button_changed(self): - self.selected_node = self._next_node - - def _parent_button_changed(self): - if self.selected_node is not None: - parent = self.selected_node.parent - self.selected_node = parent - - def _previous_button_changed(self): - self.selected_node = self._previous_node - # ------------------------------------------------------------------------- # Traits view definitions: # ------------------------------------------------------------------------- def default_traits_view(self): - """ Constructs the default traits view.""" - - traits_view = View( - HGroup( - UItem( - "previous_button", - style="custom", - enabled_when="_previous_node is not None", - tooltip="Go to previous file" - ), - UItem( - "parent_button", - style="custom", - enabled_when="(selected_node is not None) and " - "(object.selected_node.parent is not None)", - tooltip="Go up one level" - ), - UItem( - "title", - springy=True, - editor=TitleEditor() - ), - UItem( - "next_button", - style="custom", - enabled_when="_next_node is not None", - tooltip="Go to next file" - ), - "_", - ), + # XXX need to do this to hide the instantiation of ToolBar from null + # toolkit tests. It would be preferable to have this be declarative. + return View( Item( - name="root", - id="root", + name="model", + id="model", show_label=False, editor=demo_tree_editor, ), - title=self.title, id="traitsui.demos.demo.Demo", + toolbar=ToolBar(previous_tool, parent_tool, next_tool, show_tool_names=True), resizable=True, - width=950, - height=900, + width=1200, + height=700, ) - return traits_view # ------------------------------------------------------------------------- @@ -1053,7 +1117,7 @@ def _get_settings(css_path=None): settings = {'output_encoding': 'unicode'} if css_path is not None: settings['stylesheet_path'] = css_path - settings['embed_stylesheet'] = True + settings['embed_stylesheet'] = False settings['stylesheet'] = None return settings @@ -1121,7 +1185,7 @@ def publish_html_file(rst_file_path, html_out_path, css_path=None): def demo( - use_files=False, dir_name=None, config_filename="", title="Traits UI Demos" + use_files=False, dir_name=None, config_filename="", title="Traits UI Demos", css_filename="default.css" ): if dir_name is None: dir_name = dirname(abspath(sys.argv[0])) @@ -1131,10 +1195,11 @@ def demo( Demo( path=path, title=title, - root=DemoPath( + model=DemoPath( name=dir_name, nice_name=user_name_for(name), use_files=use_files, - config_filename=config_filename + config_filename=config_filename, + css_filename=css_filename, ), ).configure_traits()