def __init__( self, panel, group, ui, suppress_label, is_dock_window, create_panel ): """ Initializes the object. """ # Get the contents of the group: content = group.get_content() # Create a group editor object if one is needed: self.control = self.sizer = editor = None self.ui = ui self.group = group self.is_horizontal = group.orientation == "horizontal" layout = group.layout is_scrolled_panel = group.scrollable is_splitter = layout == "split" is_tabbed = layout == "tabbed" id = group.id # Assume our contents are not resizable: self.resizable = False if is_dock_window and (is_splitter or is_tabbed): if is_splitter: self.dock_contents = self.add_dock_window_splitter_items( panel, content, group ) else: self.resizable = group.springy self.dock_contents = create_notebook_for_items( content, ui, panel, group, self.add_notebook_item, True ) return if ( is_dock_window or create_panel or is_scrolled_panel or (id != "") or (group.visible_when != "") or (group.enabled_when != "") ): if is_scrolled_panel: new_panel = TraitsUIScrolledPanel(panel) new_panel.SetMinSize(panel.GetMinSize()) self.resizable = True else: new_panel = TraitsUIPanel(panel, -1) sizer = panel.GetSizer() if sizer is None: sizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(sizer) self.control = panel = new_panel if is_splitter or is_tabbed: editor = DockWindowGroupEditor(control=panel, ui=ui) else: editor = GroupEditor(control=panel) if id != "": ui.info.bind(group.id, editor) if group.visible_when != "": ui.add_visible(group.visible_when, editor) if group.enabled_when != "": ui.add_enabled(group.enabled_when, editor) self.panel = panel self.dock_contents = None # Determine the horizontal/vertical orientation of the group: if self.is_horizontal: orientation = wx.HORIZONTAL else: orientation = wx.VERTICAL # Set up a group with or without a border around its contents: label = "" if not suppress_label: label = group.label if group.show_border: box = wx.StaticBox(panel, -1, label) self._set_owner(box, group) self.sizer = wx.StaticBoxSizer(box, orientation) else: if layout == "flow": self.sizer = FlowSizer(orientation) else: self.sizer = wx.BoxSizer(orientation) if label != "": self.sizer.Add( heading_text(panel, text=label).control, 0, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT, 4, ) # If no sizer has been specified for the panel yet, make the new sizer # the layout sizer for the panel: if panel.GetSizer() is None: panel.SetSizer(self.sizer) # Set up scrolling now that the sizer has been set: if is_scrolled_panel: if self.is_horizontal: panel.SetupScrolling(scroll_y=False) else: panel.SetupScrolling(scroll_x=False) if is_splitter: dw = DockWindow( panel, handler=ui.handler, handler_args=(ui.info,), id=ui.id, theme=group.dock_theme, ).control if editor is not None: editor.dock_window = dw dw.SetSizer( DockSizer( contents=self.add_dock_window_splitter_items( dw, content, group ) ) ) self.sizer.Add(dw, 1, wx.EXPAND) elif len(content) > 0: if is_tabbed: self.resizable = group.springy dw = create_notebook_for_items( content, ui, panel, group, self.add_notebook_item ) if editor is not None: editor.dock_window = dw self.sizer.Add(dw, self.resizable, wx.EXPAND) # Check if content is all Group objects: elif layout == "fold": self.resizable = True self.sizer.Add( self.create_fold_for_items(panel, content), 1, wx.EXPAND ) elif isinstance(content[0], Group): # If so, add them to the panel and exit: self.add_groups(content, panel) else: self.add_items(content, panel, self.sizer) # If the caller is a DockWindow, we need to define the content we are # adding to it: if is_dock_window: self.dock_contents = DockRegion( contents=[ DockControl( name=group.get_label(self.ui), image=group.image, id=group.get_id(), style=group.dock, dockable=DockableViewElement(ui=ui, element=group), export=group.export, control=panel, ) ] )
class FillPanel(object): """ A subpanel for a single group of items. """ def __init__( self, panel, group, ui, suppress_label, is_dock_window, create_panel ): """ Initializes the object. """ # Get the contents of the group: content = group.get_content() # Create a group editor object if one is needed: self.control = self.sizer = editor = None self.ui = ui self.group = group self.is_horizontal = group.orientation == "horizontal" layout = group.layout is_scrolled_panel = group.scrollable is_splitter = layout == "split" is_tabbed = layout == "tabbed" id = group.id # Assume our contents are not resizable: self.resizable = False if is_dock_window and (is_splitter or is_tabbed): if is_splitter: self.dock_contents = self.add_dock_window_splitter_items( panel, content, group ) else: self.resizable = group.springy self.dock_contents = create_notebook_for_items( content, ui, panel, group, self.add_notebook_item, True ) return if ( is_dock_window or create_panel or is_scrolled_panel or (id != "") or (group.visible_when != "") or (group.enabled_when != "") ): if is_scrolled_panel: new_panel = TraitsUIScrolledPanel(panel) new_panel.SetMinSize(panel.GetMinSize()) self.resizable = True else: new_panel = TraitsUIPanel(panel, -1) sizer = panel.GetSizer() if sizer is None: sizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(sizer) self.control = panel = new_panel if is_splitter or is_tabbed: editor = DockWindowGroupEditor(control=panel, ui=ui) else: editor = GroupEditor(control=panel) if id != "": ui.info.bind(group.id, editor) if group.visible_when != "": ui.add_visible(group.visible_when, editor) if group.enabled_when != "": ui.add_enabled(group.enabled_when, editor) self.panel = panel self.dock_contents = None # Determine the horizontal/vertical orientation of the group: if self.is_horizontal: orientation = wx.HORIZONTAL else: orientation = wx.VERTICAL # Set up a group with or without a border around its contents: label = "" if not suppress_label: label = group.label if group.show_border: box = wx.StaticBox(panel, -1, label) self._set_owner(box, group) self.sizer = wx.StaticBoxSizer(box, orientation) else: if layout == "flow": self.sizer = FlowSizer(orientation) else: self.sizer = wx.BoxSizer(orientation) if label != "": self.sizer.Add( heading_text(panel, text=label).control, 0, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT, 4, ) # If no sizer has been specified for the panel yet, make the new sizer # the layout sizer for the panel: if panel.GetSizer() is None: panel.SetSizer(self.sizer) # Set up scrolling now that the sizer has been set: if is_scrolled_panel: if self.is_horizontal: panel.SetupScrolling(scroll_y=False) else: panel.SetupScrolling(scroll_x=False) if is_splitter: dw = DockWindow( panel, handler=ui.handler, handler_args=(ui.info,), id=ui.id, theme=group.dock_theme, ).control if editor is not None: editor.dock_window = dw dw.SetSizer( DockSizer( contents=self.add_dock_window_splitter_items( dw, content, group ) ) ) self.sizer.Add(dw, 1, wx.EXPAND) elif len(content) > 0: if is_tabbed: self.resizable = group.springy dw = create_notebook_for_items( content, ui, panel, group, self.add_notebook_item ) if editor is not None: editor.dock_window = dw self.sizer.Add(dw, self.resizable, wx.EXPAND) # Check if content is all Group objects: elif layout == "fold": self.resizable = True self.sizer.Add( self.create_fold_for_items(panel, content), 1, wx.EXPAND ) elif isinstance(content[0], Group): # If so, add them to the panel and exit: self.add_groups(content, panel) else: self.add_items(content, panel, self.sizer) # If the caller is a DockWindow, we need to define the content we are # adding to it: if is_dock_window: self.dock_contents = DockRegion( contents=[ DockControl( name=group.get_label(self.ui), image=group.image, id=group.get_id(), style=group.dock, dockable=DockableViewElement(ui=ui, element=group), export=group.export, control=panel, ) ] ) def add_dock_window_splitter_items(self, window, content, group): """ Adds a set of groups or items separated by splitter bars to a DockWindow. """ contents = [ self.add_dock_window_splitter_item(window, item, group) for item in content ] # Create a splitter group to hold the contents: result = DockSection(contents=contents, is_row=self.is_horizontal) # If nothing is resizable, then mark each DockControl as such: if not self.resizable: for item in result.get_controls(): item.resizable = False # Return the DockSection we created: return result def add_dock_window_splitter_item(self, window, item, group): """ Adds a single group or item to a DockWindow. """ if isinstance(item, Group): sizer, resizable, contents = fill_panel_for_group( window, item, self.ui, suppress_label=True, is_dock_window=True ) self.resizable |= resizable return contents orientation = wx.VERTICAL if self.is_horizontal: orientation = wx.HORIZONTAL sizer = wx.BoxSizer(orientation) panel = TraitsUIPanel(window, -1) panel.SetSizer(sizer) self.add_items([item], panel, sizer) return DockRegion( contents=[ DockControl( name=item.get_label(self.ui), image=item.image, id=item.get_id(), style=item.dock, dockable=DockableViewElement(ui=self.ui, element=item), export=item.export, control=panel, ) ] ) def create_fold_for_items(self, window, content): """ Adds a set of groups or items as vertical notebook pages to a vertical notebook. """ raise NotImplementedError("VFold is not implemented for Wx backend") def create_fold_for_item(self, notebook, item): """ Adds a single group or item to a vertical notebook. """ # Create a new notebook page: page = notebook.create_page() # Create the page contents: if isinstance(item, Group): panel, resizable, contents = fill_panel_for_group( page.parent, item, self.ui, suppress_label=True, create_panel=True, ) else: panel = TraitsUIPanel(page.parent, -1) sizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(sizer) self.add_items([item], panel, sizer) # Set the page name and control: page.name = item.get_label(self.ui) page.control = panel # Return the new notebook page: return page def add_notebook_item(self, item, parent, sizer): """ Adds a single Item to a notebook. """ self.add_items([item], parent, sizer) def add_groups(self, content, panel): """ Adds a list of Group objects to the panel. """ sizer = self.sizer # Process each group: for subgroup in content: # Add the sub-group to the panel: sg_sizer, sg_resizable, contents = fill_panel_for_group( panel, subgroup, self.ui ) # If the sub-group is resizable: if sg_resizable: # Then so are we: self.resizable = True # Add the sub-group so that it can be resized by the layout: sizer.Add(sg_sizer, 1, wx.EXPAND | wx.ALL, 2) else: style = wx.EXPAND | wx.ALL growable = 0 if self.is_horizontal: if subgroup.springy: growable = 1 sizer.Add(sg_sizer, growable, style, 2) def add_items(self, content, panel, sizer): """ Adds a list of Item objects to the panel. """ # Get local references to various objects we need: ui = self.ui info = ui.info handler = ui.handler group = self.group show_left = group.show_left padding = group.padding col = -1 col_incr = 1 self.label_flags = 0 show_labels = False for item in content: show_labels |= item.show_label if (not self.is_horizontal) and (show_labels or (group.columns > 1)): # For a vertical list of Items with labels or multiple columns, use # a 'FlexGridSizer': self.label_pad = 0 cols = group.columns if show_labels: cols *= 2 col_incr = 2 flags = wx.TOP | wx.BOTTOM border_size = 1 item_sizer = wx.FlexGridSizer(0, cols, 0, 5) if show_left: self.label_flags = wx.ALIGN_RIGHT if show_labels: for i in range(1, cols, 2): item_sizer.AddGrowableCol(i) else: # Otherwise, the current sizer will work as is: self.label_pad = 4 cols = 1 flags = wx.ALL border_size = 1 item_sizer = sizer # Process each Item in the list: for item in content: # Get the name in order to determine its type: name = item.name # Check if is a label: if name == "": label = item.label if label != "": # Update the column counter: col += col_incr # If we are building a multi-column layout with labels, # just add space in the next column: if (cols > 1) and show_labels: item_sizer.Add((1, 1)) if item.style == "simple": # Add a simple text label: label = wx.StaticText( panel, -1, label, style=wx.ALIGN_LEFT ) item_sizer.Add(label, 0, wx.EXPAND) else: # Add the label to the sizer: label = heading_text(panel, text=label).control item_sizer.Add( label, 0, wx.TOP | wx.BOTTOM | wx.EXPAND, 3 ) if item.emphasized: self._add_emphasis(label) # Continue on to the next Item in the list: continue # Update the column counter: col += col_incr # Check if it is a separator: if name == "_": for i in range(cols): if self.is_horizontal: # Add a vertical separator: line = wx.StaticLine(panel, -1, style=wx.LI_VERTICAL) item_sizer.Add( line, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 2 ) else: # Add a horizontal separator: line = wx.StaticLine(panel, -1, style=wx.LI_HORIZONTAL) item_sizer.Add( line, 0, wx.TOP | wx.BOTTOM | wx.EXPAND, 2 ) self._set_owner(line, item) # Continue on to the next Item in the list: continue # Convert a blank to a 5 pixel spacer: if name == " ": name = "5" # Check if it is a spacer: if all_digits.match(name): # If so, add the appropriate amount of space to the sizer: n = int(name) if self.is_horizontal: item_sizer.Add((n, 1)) else: spacer = (1, n) item_sizer.Add(spacer) if show_labels: item_sizer.Add(spacer) # Continue on to the next Item in the list: continue # Otherwise, it must be a trait Item: object = eval(item.object_, globals(), ui.context) trait = object.base_trait(name) desc = trait.tooltip if desc is None: desc = "Specifies " + trait.desc if trait.desc else "" label = None # If we are displaying labels on the left, add the label to the # user interface: if show_left: if item.show_label: label = self.create_label( item, ui, desc, panel, item_sizer, border=group.show_border, ) elif (cols > 1) and show_labels: label = self.dummy_label(panel, item_sizer) # Get the editor factory associated with the Item: editor_factory = item.editor if editor_factory is None: editor_factory = trait.get_editor() # If still no editor factory found, use a default text editor: if editor_factory is None: from .text_editor import ToolkitEditorFactory editor_factory = ToolkitEditorFactory() # If the item has formatting traits set them in the editor # factory: if item.format_func is not None: editor_factory.format_func = item.format_func if item.format_str != "": editor_factory.format_str = item.format_str # If the item has an invalid state extended trait name, set it # in the editor factory: if item.invalid != "": editor_factory.invalid = item.invalid # Set up the background image (if used): item_panel = panel # Create the requested type of editor from the editor factory: factory_method = getattr(editor_factory, item.style + "_editor") editor = factory_method( ui, object, name, item.tooltip, item_panel ).trait_set(item=item, object_name=item.object) # Tell editor to actually build the editing widget: editor.prepare(item_panel) # Set the initial 'enabled' state of the editor from the factory: editor.enabled = editor_factory.enabled # Add emphasis to the editor control if requested: if item.emphasized: self._add_emphasis(editor.control) # Give the editor focus if it requested it: if item.has_focus: editor.control.SetFocus() # Adjust the maximum border size based on the editor's settings: border_size = min(border_size, editor.border_size) # Set up the reference to the correct 'control' to use in the # following section, depending upon whether we have wrapped an # ImagePanel around the editor control or not: control = editor.control width, height = control.GetSize() # Set the correct size on the control, as specified by the user: scrollable = editor.scrollable item_width = item.width item_height = item.height growable = 0 if (item_width != -1.0) or (item_height != -1.0): if (0.0 < item_width <= 1.0) and self.is_horizontal: growable = int(1000.0 * item_width) item_width = -1 item_width = int(item_width) if item_width < -1: item_width = -item_width elif item_width != -1: item_width = max(item_width, width) if (0.0 < item_height <= 1.0) and (not self.is_horizontal): growable = int(1000.0 * item_height) item_height = -1 item_height = int(item_height) if item_height < -1: item_height = -item_height elif item_height != -1: item_height = max(item_height, height) control.SetMinSize(wx.Size(item_width, item_height)) # Bind the item to the control and all of its children: self._set_owner(control, item) # Bind the editor into the UIInfo object name space so it can be # referred to by a Handler while the user interface is active: id = item.id or name info.bind(id, editor, item.id) # Also, add the editors to the list of editors used to construct # the user interface: ui._editors.append(editor) # If the handler wants to be notified when the editor is created, # add it to the list of methods to be called when the UI is # complete: defined = getattr(handler, id + "_defined", None) if defined is not None: ui.add_defined(defined) # If the editor is conditionally visible, add the visibility # 'expression' and the editor to the UI object's list of monitored # objects: if item.visible_when != "": ui.add_visible(item.visible_when, editor) # If the editor is conditionally enabled, add the enabling # 'expression' and the editor to the UI object's list of monitored # objects: if item.enabled_when != "": ui.add_enabled(item.enabled_when, editor) # Add the created editor control to the sizer with the appropriate # layout flags and values: ui._scrollable |= scrollable item_resizable = (item.resizable is True) or ( (item.resizable is Undefined) and scrollable ) if item_resizable: growable = growable or 500 self.resizable = True elif item.springy: growable = growable or 500 # The following is a hack to allow 'readonly' text fields to # work correctly (wx has a bug that setting wx.EXPAND on a # StaticText control seems to cause the text to be aligned higher # than it would be otherwise, causing it to misalign with its # label). layout_style = editor.layout_style if not show_labels: layout_style |= wx.EXPAND item_sizer.Add( control, growable, flags | layout_style, max(0, border_size + padding + item.padding), ) # If we are displaying labels on the right, add the label to the # user interface: if not show_left: if item.show_label: label = self.create_label( item, ui, desc, panel, item_sizer, "", wx.RIGHT ) elif (cols > 1) and show_labels: label = self.dummy_label(panel, item_sizer) # If the Item is resizable, and we are using a multi-column grid: if item_resizable and (cols > 1): # Mark the entire row as growable: item_sizer.AddGrowableRow(col // cols) # Save the reference to the label control (if any) in the editor: editor.label_control = label # If we created a grid sizer, add it to the original sizer: if item_sizer is not sizer: growable = 0 if self.resizable: growable = 1 sizer.Add(item_sizer, growable, wx.EXPAND | wx.ALL, 2) def create_label( self, item, ui, desc, parent, sizer, suffix=":", pad_side=wx.LEFT, border=False, ): """ Creates an item label. """ label = item.get_label(ui) if (label == "") or (label[-1:] in "?=:;,.<>/\\\"'-+#|"): suffix = "" control = wx.StaticText( parent, -1, label + suffix, style=wx.ALIGN_LEFT | wx.SIMPLE_BORDER if border else wx.NO_BORDER, ) self._set_owner(control, item) if item.emphasized: self._add_emphasis(control) # XXX: Turning off help popups for now # control.Bind(wx.EVT_LEFT_UP, show_help_popup) control.help = item.get_help(ui) control.SetToolTip(wx.ToolTip(item.get_help(ui))) sizer.Add( control, 0, self.label_flags | wx.ALIGN_TOP | pad_side, self.label_pad, ) if desc != "": control.SetToolTip(desc) return control def dummy_label(self, parent, sizer): """ Creates an item label. """ control = wx.StaticText(parent, -1, "", style=wx.ALIGN_RIGHT) sizer.Add(control, 0) return control def _add_emphasis(self, control): """ Adds emphasis to a specified control's font. """ global emphasis_font control.SetForegroundColour(emphasis_color) if emphasis_font is None: font = control.GetFont() emphasis_font = wx.Font( font.GetPointSize() + 1, font.GetFamily(), font.GetStyle(), wx.BOLD, ) control.SetFont(emphasis_font) def _set_owner(self, control, owner): control._owner = owner for child in control.GetChildren(): self._set_owner(child, owner)