class ThemedCheckboxEditor(BasicEditorFactory):

    # The editor class to be created:
    klass = _ThemedCheckboxEditor

    # The checkbox label:
    label = Str

    # The basic theme for the checkbox (i.e. the 'off' state):
    theme = ATheme

    # The optional 'on' state theme for the checkbox:
    on_theme = ATheme

    # The optional 'hover off' state theme for the checkbox:
    hover_off_theme = ATheme

    # The optional 'hover on' state theme for the checbox:
    hover_on_theme = ATheme

    # The optional image to display in the checkbox (i.e. the 'off' state):
    image = Image('cb_off')

    # The optional 'on' state image to display in the checkbox:
    on_image = Image('cb_on')

    # The optional 'hover off' state image to display in the checkbox:
    hover_off_image = Image('cb_hover_off')

    # The optional 'hover on' state image to display in the checkbox:
    hover_on_image = Image('cb_hover_on')

    # The position of the image relative to the text:
    position = Position

    # The amount of space between the image and the text:
    spacing = Spacing
示例#2
0
class VolumeManagerAdapter(TabularAdapter):
    """ TabularEditor adapter for use with the VolumeManager view.
    """
    # The table columns:
    columns = [('Image Name', 'image')]

    # Adapter properties:
    text = Property
    image = Property

    # Image definitions:
    alert_image = Image('@std:alert16')

    #-- Property Implementations -----------------------------------------------

    def _get_text(self):
        return self.item.image.image_name

    def _get_image(self):
        if self.item.modified:
            return self.alert_image

        return None
示例#3
0
class SimpleEditor(Editor):
    """ Simple style of editor for lists, which displays a scrolling list box
    with only one item visible at a time. A icon next to the list box displays
    a menu of operations on the list.
    """

    #---------------------------------------------------------------------------
    #  Trait definitions:
    #---------------------------------------------------------------------------

    # The kind of editor to create for each list item
    kind = Str

    # Is the list of items being edited mutable?
    mutable = Bool

    # The image used by the editor:
    image = Image('list_editor')

    # The bitmap used by the editor:
    bitmap = Property

    #---------------------------------------------------------------------------
    #  Class constants:
    #---------------------------------------------------------------------------

    # Whether the list is displayed in a single row
    single_row = True

    #---------------------------------------------------------------------------
    #  Normal list item menu:
    #---------------------------------------------------------------------------

    # Menu for modifying the list
    list_menu = """
       Add Before     [_menu_before]: self.add_before()
       Add After      [_menu_after]:  self.add_after()
       ---
       Delete         [_menu_delete]: self.delete_item()
       ---
       Move Up        [_menu_up]:     self.move_up()
       Move Down      [_menu_down]:   self.move_down()
       Move to Top    [_menu_top]:    self.move_top()
       Move to Bottom [_menu_bottom]: self.move_bottom()
    """

    #---------------------------------------------------------------------------
    #  Empty list item menu:
    #---------------------------------------------------------------------------

    empty_list_menu = """
       Add: self.add_empty()
    """

    #---------------------------------------------------------------------------
    #  Finishes initializing the editor by creating the underlying toolkit
    #  widget:
    #---------------------------------------------------------------------------

    def init(self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        # Initialize the trait handler to use:
        trait_handler = self.factory.trait_handler
        if trait_handler is None:
            trait_handler = self.object.base_trait(self.name).handler
        self._trait_handler = trait_handler

        # Create a scrolled window to hold all of the list item controls:
        self.control = wxsp.ScrolledPanel(parent, -1)
        self.control.SetBackgroundColour(parent.GetBackgroundColour())
        self.control.SetAutoLayout(True)

        # Remember the editor to use for each individual list item:
        editor = self.factory.editor
        if editor is None:
            editor = trait_handler.item_trait.get_editor()
        self._editor = getattr(editor, self.kind)

        # Set up the additional 'list items changed' event handler needed for
        # a list based trait. Note that we want to fire the update_editor_item
        # only when the items in the list change and not when intermediate
        # traits change. Therefore, replace "." by ":" in the extended_name
        # when setting up the listener.
        extended_name = self.extended_name.replace('.', ':')
        self.context_object.on_trait_change(self.update_editor_item,
                                            extended_name + '_items?',
                                            dispatch='ui')
        self.set_tooltip()

    #---------------------------------------------------------------------------
    #  Disposes of the contents of an editor:
    #---------------------------------------------------------------------------

    def dispose(self):
        """ Disposes of the contents of an editor.
        """
        extended_name = self.extended_name.replace('.', ':')
        self.context_object.on_trait_change(self.update_editor_item,
                                            extended_name + '_items?',
                                            remove=True)
        self._dispose_items()

        super(SimpleEditor, self).dispose()

    #---------------------------------------------------------------------------
    #  Updates the editor when the object trait changes external to the editor:
    #---------------------------------------------------------------------------

    def update_editor(self):
        """ Updates the editor when the object trait changes externally to the
            editor.
        """
        # Disconnect the editor from any control about to be destroyed:
        self._dispose_items()

        # Get rid of any previous contents:
        list_pane = self.control
        list_pane.SetSizer(None)
        list_pane.DestroyChildren()

        # Create all of the list item trait editors:
        trait_handler = self._trait_handler
        resizable = ((trait_handler.minlen != trait_handler.maxlen)
                     and self.mutable)
        item_trait = trait_handler.item_trait
        factory = self.factory
        list_sizer = wx.FlexGridSizer(len(self.value),
                                      (1 + resizable) * factory.columns, 0, 0)
        j = resizable
        for i in range(factory.columns):
            list_sizer.AddGrowableCol(j)
            j += (1 + resizable)

        values = self.value
        index = 0
        width, height = 0, 0

        is_fake = (resizable and (values is None or len(values) == 0))
        if is_fake:
            values = [item_trait.default_value()[1]]

        panel_height = 0
        editor = self._editor
        for value in values:
            width1 = height = 0
            if resizable:
                control = ImageControl(list_pane, self.bitmap, -1,
                                       self.popup_menu)
                width1, height = control.GetSize()
                width1 += 4

            try:
                proxy = ListItemProxy(self.object, self.name, index,
                                      item_trait, value)
                if resizable:
                    control.proxy = proxy
                peditor = editor(self.ui, proxy, 'value', self.description,
                                 list_pane).set(object_name='')
                peditor.prepare(list_pane)
                pcontrol = peditor.control
                pcontrol.proxy = proxy
            except:
                if not is_fake:
                    raise

                pcontrol = wx.Button(list_pane, -1, 'sample')

            pcontrol.Fit()
            width2, height2 = size = pcontrol.GetSize()
            pcontrol.SetMinSize(size)
            width = max(width, width1 + width2)
            height = max(height, height2)
            panel_height += height

            if resizable:
                list_sizer.Add(control, 0, wx.LEFT | wx.RIGHT, 2)

            list_sizer.Add(pcontrol, 0, wx.EXPAND)
            index += 1

        list_pane.SetSizer(list_sizer)

        if not self.mutable:
            #list_sizer.SetDimension(0,0,width, panel_height)
            list_pane.SetInitialSize(list_sizer.GetSize())

        if is_fake:
            self._cur_control = control
            self.empty_list()
            control.Destroy()
            pcontrol.Destroy()

        rows = 1
        if not self.single_row:
            rows = self.factory.rows

        # Make sure we have valid values set for width and height (in case there
        # was no data to base them on):
        if width == 0:
            width = 100

        if panel_height == 0:
            panel_height = 20

        list_pane.SetMinSize(
            wx.Size(width + ((trait_handler.maxlen > rows) * scrollbar_dx),
                    panel_height))

        list_pane.SetupScrolling()
        list_pane.GetParent().Layout()

    #---------------------------------------------------------------------------
    #  Updates the editor when an item in the object trait changes external to
    #  the editor:
    #---------------------------------------------------------------------------

    def update_editor_item(self, obj, name, event):
        """ Updates the editor when an item in the object trait changes
        externally to the editor.
        """
        # If this is not a simple, single item update, rebuild entire editor:
        if (len(event.removed) != 1) or (len(event.added) != 1):
            self.update_editor()
            return

        # Otherwise, find the proxy for this index and update it with the
        # changed value:
        for control in self.control.GetChildren():
            proxy = control.proxy
            if proxy.index == event.index:
                proxy.value = event.added[0]
                break

    #---------------------------------------------------------------------------
    #  Creates an empty list entry (so the user can add a new item):
    #---------------------------------------------------------------------------

    def empty_list(self):
        """ Creates an empty list entry (so the user can add a new item).
        """
        control = ImageControl(self.control, self.bitmap, -1,
                               self.popup_empty_menu)
        control.is_empty = True
        proxy = ListItemProxy(self.object, self.name, -1, None, None)
        pcontrol = wx.StaticText(self.control, -1, '   (Empty List)')
        pcontrol.proxy = control.proxy = proxy
        self.reload_sizer([(control, pcontrol)])

    #---------------------------------------------------------------------------
    #  Reloads the layout from the specified list of ( button, proxy ) pairs:
    #---------------------------------------------------------------------------

    def reload_sizer(self, controls, extra=0):
        """ Reloads the layout from the specified list of ( button, proxy )
            pairs.
        """
        sizer = self.control.GetSizer()
        for i in xrange(2 * len(controls) + extra):
            sizer.Remove(0)
        index = 0
        for control, pcontrol in controls:
            sizer.Add(control, 0, wx.LEFT | wx.RIGHT, 2)
            sizer.Add(pcontrol, 1, wx.EXPAND)
            control.proxy.index = index
            index += 1
        sizer.Layout()
        self.control.SetVirtualSize(sizer.GetMinSize())

    #---------------------------------------------------------------------------
    #  Returns the associated object list and current item index:
    #---------------------------------------------------------------------------

    def get_info(self):
        """ Returns the associated object list and current item index.
        """
        proxy = self._cur_control.proxy
        return (proxy.list, proxy.index)

    #---------------------------------------------------------------------------
    #  Displays the empty list editor popup menu:
    #---------------------------------------------------------------------------

    def popup_empty_menu(self, control):
        """ Displays the empty list editor popup menu.
        """
        self._cur_control = control
        menu = MakeMenu(self.empty_list_menu, self, True, self.control).menu
        self.control.PopupMenu(menu, control.GetPosition())
        menu.Destroy()

    #---------------------------------------------------------------------------
    #  Displays the list editor popup menu:
    #---------------------------------------------------------------------------

    def popup_menu(self, control):
        """ Displays the list editor popup menu.
        """
        self._cur_control = control
        # Makes sure that any text that was entered get's added (Pressure #145):
        control.SetFocus()
        proxy = control.proxy
        index = proxy.index
        menu = MakeMenu(self.list_menu, self, True, self.control).menu
        len_list = len(proxy.list)
        not_full = (len_list < self._trait_handler.maxlen)
        self._menu_before.enabled(not_full)
        self._menu_after.enabled(not_full)
        self._menu_delete.enabled(len_list > self._trait_handler.minlen)
        self._menu_up.enabled(index > 0)
        self._menu_top.enabled(index > 0)
        self._menu_down.enabled(index < (len_list - 1))
        self._menu_bottom.enabled(index < (len_list - 1))
        self.control.PopupMenu(menu, control.GetPosition())
        menu.Destroy()

    #---------------------------------------------------------------------------
    #  Adds a new value at the specified list index:
    #---------------------------------------------------------------------------

    def add_item(self, offset):
        """ Adds a new value at the specified list index.
        """
        list, index = self.get_info()
        index += offset
        item_trait = self._trait_handler.item_trait
        value = item_trait.default_value_for(self.object, self.name)
        self.value = list[:index] + [value] + list[index:]
        self.update_editor()

    #---------------------------------------------------------------------------
    #  Inserts a new item before the current item:
    #---------------------------------------------------------------------------

    def add_before(self):
        """ Inserts a new item before the current item.
        """
        self.add_item(0)

    #---------------------------------------------------------------------------
    #  Inserts a new item after the current item:
    #---------------------------------------------------------------------------

    def add_after(self):
        """ Inserts a new item after the current item.
        """
        self.add_item(1)

    #---------------------------------------------------------------------------
    #  Adds a new item when the list is empty:
    #---------------------------------------------------------------------------

    def add_empty(self):
        """ Adds a new item when the list is empty.
        """
        list, index = self.get_info()
        self.add_item(0)

    #---------------------------------------------------------------------------
    #  Delete the current item:
    #---------------------------------------------------------------------------

    def delete_item(self):
        """ Delete the current item.
        """
        list, index = self.get_info()
        self.value = list[:index] + list[index + 1:]
        self.update_editor()

    #---------------------------------------------------------------------------
    #  Move the current item up one in the list:
    #---------------------------------------------------------------------------

    def move_up(self):
        """ Move the current item up one in the list.
        """
        list, index = self.get_info()
        self.value = (list[:index - 1] + [list[index], list[index - 1]] +
                      list[index + 1:])
        self.update_editor()

    #---------------------------------------------------------------------------
    #  Moves the current item down one in the list:
    #---------------------------------------------------------------------------

    def move_down(self):
        """ Moves the current item down one in the list.
        """
        list, index = self.get_info()
        self.value = (list[:index] + [list[index + 1], list[index]] +
                      list[index + 2:])
        self.update_editor()

    #---------------------------------------------------------------------------
    #  Moves the current item to the top of the list:
    #---------------------------------------------------------------------------

    def move_top(self):
        """ Moves the current item to the top of the list.
        """
        list, index = self.get_info()
        self.value = [list[index]] + list[:index] + list[index + 1:]
        self.update_editor()

    #---------------------------------------------------------------------------
    #  Moves the current item to the bottom of the list:
    #---------------------------------------------------------------------------

    def move_bottom(self):
        """ Moves the current item to the bottom of the list.
        """
        list, index = self.get_info()
        self.value = list[:index] + list[index + 1:] + [list[index]]
        self.update_editor()

    #-- Property Implementations -----------------------------------------------

    @cached_property
    def _get_bitmap(self):
        return convert_bitmap(self.image)

    #-- Private Methods --------------------------------------------------------

    def _dispose_items(self):
        """ Disposes of each current list item.
        """
        for control in self.control.GetChildren():
            editor = getattr(control, '_editor', None)
            if editor is not None:
                editor.dispose()
                editor.control = None

    #-- Trait initializers ----------------------------------------------------

    def _kind_default(self):
        """ Returns a default value for the 'kind' trait.
        """
        return self.factory.style + '_editor'

    def _mutable_default(self):
        """ Trait handler to set the mutable trait from the factory. """
        return self.factory.mutable
示例#4
0
class ThemedControl ( ThemedWindow ):

    #-- Public Traits ----------------------------------------------------------

    # An (optional) image to be drawn inside the control:
    image = Image( event = 'updated' )

    # The (optional) text to be displayed inside the control:
    text = Str( event = 'updated' )

    # The position of the image relative to the text:
    position = Position( event = 'updated' )

    # The amount of spacing between the image and the text:
    spacing = Spacing( event = 'updated' )

    # Is the text value private (like a password):
    password = Bool( False, event = 'updated' )

    # An additional, optional offset to apply to the text/image position:
    offset = Tuple( Int, Int )

    # Minimum default size for the control:
    min_size = Tuple( Int, Int )

    # Is the control enabled:
    enabled = Bool( True )

    #-- Private Traits ---------------------------------------------------------

    # An event fired when any display related value changes:
    updated = Event

    # The underlying wx.Window control:
    control = Instance( wx.Window )

    # The current text value to display:
    current_text = Property( depends_on = 'text, password' )

    # The best size for the control:
    best_size = Property

    # The size of the current text:
    text_size = Property( depends_on = 'current_text, control' )

    # The position and size of the current text within the control:
    text_bounds = Property( depends_on = 'updated' )

    # The position and size of the current bitmap within the control:
    bitmap_bounds = Property( depends_on = 'updated' )

    #-- Public Methods ---------------------------------------------------------

    def create_control ( self, parent ):
        """ Creates the underlying wx.Window object.
        """
        self.control = control = wx.Window( parent, -1,
                            size  = wx.Size( 70, 20 ),
                            style = wx.FULL_REPAINT_ON_RESIZE | wx.WANTS_CHARS )

        # Initialize the control (set-up event handlers, ...):
        self.init_control()

        # Make sure all internal state gets initialized:
        self._image_changed( self.image )

        # Make sure the control is sized correctly:
        size = self.best_size
        control.SetMinSize( size )
        control.SetSize( size )

        return control

    #-- Property Implementations -----------------------------------------------

    @cached_property
    def _get_current_text ( self ):
        """ Returns the current text to display.
        """
        if self.password:
            return '*' * len( self.text )

        return self.text

    def _get_best_size ( self ):
        """ Returns the 'best' size for the control.
        """
        cx, cy, cdx, cdy = self._get_bounds_for( TheControl )
        mdx, mdy         = self.min_size
        return wx.Size( max( cdx, mdx ), max( cdy, mdy ) )

    @cached_property
    def _get_text_size ( self ):
        """ Returns the text size information for the window.
        """
        text = self.current_text
        if (text == '') or (self.control is None):
            return ZeroTextSize

        return self.control.GetFullTextExtent( text )

    @cached_property
    def _get_text_bounds ( self ):
        """ Returns the position and size of the text within the window.
        """
        return self._get_bounds_for( TheText )

    @cached_property
    def _get_bitmap_bounds ( self ):
        """ Returns the size and position of the bitmap within the window.
        """
        return self._get_bounds_for( TheBitmap )

    #-- Event Handlers ---------------------------------------------------------

    @on_trait_change( 'theme.+, image' )
    def _updated_changed ( self ):
        """ Handles any update related trait being changed.
        """
        if self.control is not None:
            self.control.Refresh()

    def _image_changed ( self, image ):
        """ Handles the image being changed by updating the corresponding
            bitmap.
        """
        if image is None:
            self._bitmap = None
        else:
            self._bitmap = image.create_image().ConvertToBitmap()

    #-- wxPython Event Handlers ------------------------------------------------

    def _paint_fg ( self, dc ):
        """ Paints the foreground into the specified device context.
        """
        self.enabled = self.control.IsEnabled()

        # Get the text and image offset to use:
        theme    = self.theme or default_theme
        label    = theme.label
        ox, oy   = label.left, label.top
        ox2, oy2 = self.offset
        ox      += ox2
        oy      += oy2

        # Draw the bitmap (if any):
        ix, iy, idx, idy = self.bitmap_bounds
        if idx != 0:
            dc.DrawBitmap( self._bitmap, ix + ox, iy + oy, True )

        # Draw the text (if any):
        tx, ty, tdx, tdy = self.text_bounds
        if tdx != 0:
            dc.SetBackgroundMode( wx.TRANSPARENT )
            dc.SetTextForeground( theme.content_color )
            dc.SetFont( self.control.GetFont() )
            dc.DrawText( self.current_text, tx + ox, ty + oy )

    def _size ( self, event ):
        """ Handles the control being resized.
        """
        super( ThemedControl, self )._size( event )
        self.updated = True

    #-- Private Methods --------------------------------------------------------

    def _get_bounds_for ( self, item ):
        """ Returns all text and image related position and size information.
        """
        control = self.control
        if control is None:
            return EmptyBounds

        tdx, tdy, descent, leading = self.text_size
        bitmap = self._bitmap
        if bitmap is None:
            bdx, bdy = 0, 0
        else:
            bdx, bdy = bitmap.GetWidth(), bitmap.GetHeight()
        if (tdx + bdx) == 0:
            return EmptyBounds

        wdx, wdy  = control.GetClientSizeTuple()
        spacing   = (tdx != 0) * (bdx != 0) * self.spacing
        theme     = self.theme or default_theme
        slice     = theme.image_slice or ImageSlice()
        content   = theme.content

        position = self.position
        if position in ( 'above', 'below' ):
            cdx = max( tdx, bdx )
            cdy = tdy + spacing + bdy
        else:
            cdx = tdx + spacing + bdx
            cdy = max( tdy, bdy )

        if item == TheControl:
            cdx += content.left + content.right
            cdy += content.top  + content.bottom

            return ( 0, 0, max( slice.left  + slice.right,
                                slice.xleft + slice.xright  + cdx ),
                           max( slice.top   + slice.bottom,
                                slice.xtop  + slice.xbottom + cdy ) )

        alignment = theme.alignment
        if alignment == 'default':
            alignment = self.default_alignment
        if alignment == 'left':
            x = slice.xleft + content.left
        elif alignment == 'center':
            x = slice.xleft + content.left + ((wdx - slice.xleft - slice.xright
                            - content.left - content.right - cdx) / 2)
        else:
            x = wdx - slice.xright - content.right - cdx

        if position == 'left':
            bx = x
            tx = bx + bdx + spacing
        elif position == 'right':
            tx = x
            bx = tx + tdx + spacing
        else:
            x += (max( tdx, bdx ) / 2)
            tx = x - (tdx / 2 )
            bx = x - (bdx / 2 )

        y = slice.xtop + content.top + ((wdy - slice.xtop - slice.xbottom -
                         content.top - content.bottom - cdy) / 2)
        if position == 'above':
            by = y
            ty = by + bdy + spacing
        elif position == 'below':
            ty = y
            by = ty + tdy + spacing
        else:
            y += (max( tdy, bdy ) / 2)
            ty = y - ((tdy + 1) / 2)
            by = y - (bdy / 2)

        if item == TheText:
            return ( tx, ty, tdx, tdy )

        return ( bx, by, bdx, bdy )