Пример #1
0
class ReportAdapter(TabularAdapter):

    columns = [('Name', 'name'), ('Age', 'age'), ('Address', 'address'),
               ('Spouse', 'spouse')]

    font = 'Courier 10'
    age_alignment = Constant('right')
    MarriedPerson_age_image = Property
    MarriedPerson_bg_color = Color(0xE0E0FF)
    MarriedPerson_spouse_text = Property
    Person_spouse_text = Constant('')

    def _get_MarriedPerson_age_image(self):
        if self.item.age < 18:
            return '@icons:red_ball'

        return None

    def _get_MarriedPerson_spouse_text(self):
        return self.item.partner.name
Пример #2
0
class ScatterPlotTraits(HasTraits):

    plot = Instance(Plot)
    color = Color("blue")
    marker = marker_trait
    marker_size = Int(4)

    traits_view = View(Group(Item('color', label="Color"),
                             Item('marker', label="Marker"),
                             Item('marker_size', label="Size"),
                             Item('plot',
                                  editor=ComponentEditor(),
                                  show_label=False),
                             orientation="vertical"),
                       width=800,
                       height=600,
                       resizable=True,
                       title="Chaco Plot")

    def __init__(self):
        super(ScatterPlotTraits, self).__init__()
        x = linspace(-14, 14, 100)
        y = sin(x) * x**3
        plotdata = ArrayPlotData(x=x, y=y)
        plot = Plot(plotdata)

        self.renderer = plot.plot(("x", "y"), type="scatter", color="blue")[0]
        self.plot = plot

    def _color_changed(self):
        self.renderer.color = self.color

    def _marker_changed(self):
        self.renderer.marker = self.marker

    def _marker_size_changed(self):
        self.renderer.marker_size = self.marker_size
Пример #3
0
class MultiFitGui(HasTraits):
    """
    data should be c x N where c is the number of data columns/axes and N is
    the number of points
    """
    doplot3d = Bool(False)
    show3d = Button('Show 3D Plot')
    replot3d = Button('Replot 3D')
    scalefactor3d = Float(0)
    do3dscale = Bool(False)
    nmodel3d = Int(1024)
    usecolor3d = Bool(False)
    color3d = Color((0,0,0))
    scene3d = Instance(MlabSceneModel,())
    plot3daxes = Tuple(('x','y','z'))
    data = Array(shape=(None,None))
    weights = Array(shape=(None,))
    curveaxes = List(Tuple(Int,Int))
    axisnames = Dict(Int,Str)
    invaxisnames = Property(Dict,depends_on='axisnames')

    fgs = List(Instance(FitGui))


    traits_view = View(VGroup(Item('fgs',editor=ListEditor(use_notebook=True,page_name='.plotname'),style='custom',show_label=False),
                              Item('show3d',show_label=False)),
                              resizable=True,height=900,buttons=['OK','Cancel'],title='Multiple Model Data Fitters')

    plot3d_view = View(VGroup(Item('scene3d',editor=SceneEditor(scene_class=MayaviScene),show_label=False,resizable=True),
                              Item('plot3daxes',editor=TupleEditor(cols=3,labels=['x','y','z']),label='Axes'),
                              HGroup(Item('do3dscale',label='Scale by weight?'),
                              Item('scalefactor3d',label='Point scale'),
                              Item('nmodel3d',label='Nmodel')),
                              HGroup(Item('usecolor3d',label='Use color?'),Item('color3d',label='Relation Color',enabled_when='usecolor3d')),
                              Item('replot3d',show_label=False),springy=True),
                       resizable=True,height=800,width=800,title='Multiple Model3D Plot')

    def __init__(self,data,names=None,models=None,weights=None,dofits=True,**traits):
        """
        :param data: The data arrays
        :type data: sequence of c equal-length arrays (length N)
        :param names: Names
        :type names: sequence of strings, length c
        :param models:
            The models to fit for each pair either as strings or
            :class:`astroypsics.models.ParametricModel` objects.
        :type models: sequence of models, length c-1
        :param weights: the weights for each point or None for no weights
        :type weights: array-like of size N or None
        :param dofits:
            If True, the data will be fit to the models when the object is
            created, otherwise the models will be passed in as-is (or as
            created).
        :type dofits: bool

        extra keyword arguments get passed in as new traits
        (r[finmask],m[finmask],l[finmask]),names='rh,Mh,Lh',weights=w[finmask],models=models,dofits=False)
        """
        super(MultiFitGui,self).__init__(**traits)
        self._lastcurveaxes = None

        data = np.array(data,copy=False)
        if weights is None:
            self.weights = np.ones(data.shape[1])
        else:
            self.weights = np.array(weights)

        self.data = data
        if data.shape[0] < 2:
            raise ValueError('Must have at least 2 columns')

        if isinstance(names,basestring):
            names = names.split(',')
        if names is None:
            if len(data) == 2:
                self.axisnames = {0:'x',1:'y'}
            elif len(data) == 3:
                self.axisnames = {0:'x',1:'y',2:'z'}
            else:
                self.axisnames = dict((i,str(i)) for i in data)
        elif len(names) == len(data):
            self.axisnames = dict([t for t in enumerate(names)])
        else:
            raise ValueError("names don't match data")

        #default to using 0th axis as parametric
        self.curveaxes = [(0,i) for i in range(len(data))[1:]]
        if models is not None:
            if len(models) != len(data)-1:
                raise ValueError("models don't match data")
            for i,m in enumerate(models):
                fg = self.fgs[i]
                newtmodel = TraitedModel(m)
                if dofits:
                    fg.tmodel = newtmodel
                    fg.fitmodel = True #should happen automatically, but this makes sure
                else:
                    oldpard = newtmodel.model.pardict
                    fg.tmodel = newtmodel
                    fg.tmodel .model.pardict = oldpard
                if dofits:
                    fg.fitmodel = True

    def _data_changed(self):
        self.curveaxes = [(0,i) for i in range(len(self.data))[1:]]

    def _axisnames_changed(self):
        for ax,fg in zip(self.curveaxes,self.fgs):
            fg.plot.x_axis.title = self.axisnames[ax[0]] if ax[0] in self.axisnames else ''
            fg.plot.y_axis.title = self.axisnames[ax[1]] if ax[1] in self.axisnames else ''
        self.plot3daxes = (self.axisnames[0],self.axisnames[1],self.axisnames[2] if len(self.axisnames) > 2 else self.axisnames[1])

    @on_trait_change('curveaxes[]')
    def _curveaxes_update(self,names,old,new):
        ax=[]
        for t in self.curveaxes:
            ax.append(t[0])
            ax.append(t[1])
        if set(ax) != set(range(len(self.data))):
            self.curveaxes = self._lastcurveaxes
            return #TOOD:check for recursion

        if self._lastcurveaxes is None:
            self.fgs = [FitGui(self.data[t[0]],self.data[t[1]],weights=self.weights) for t in self.curveaxes]
            for ax,fg in zip(self.curveaxes,self.fgs):
                fg.plot.x_axis.title = self.axisnames[ax[0]] if ax[0] in self.axisnames else ''
                fg.plot.y_axis.title = self.axisnames[ax[1]] if ax[1] in self.axisnames else ''
        else:
            for i,t in enumerate(self.curveaxes):
                if  self._lastcurveaxes[i] != t:
                    self.fgs[i] = fg = FitGui(self.data[t[0]],self.data[t[1]],weights=self.weights)
                    ax = self.curveaxes[i]
                    fg.plot.x_axis.title = self.axisnames[ax[0]] if ax[0] in self.axisnames else ''
                    fg.plot.y_axis.title = self.axisnames[ax[1]] if ax[1] in self.axisnames else ''

        self._lastcurveaxes = self.curveaxes

    def _show3d_fired(self):
        self.edit_traits(view='plot3d_view')
        self.doplot3d = True
        self.replot3d = True

    def _plot3daxes_changed(self):
        self.replot3d = True

    @on_trait_change('weights',post_init=True)
    def weightsChanged(self):
        for fg in self.fgs:
            if fg.weighttype != 'custom':
                fg.weighttype = 'custom'
            fg.weights = self.weights


    @on_trait_change('data','fgs','replot3d','weights')
    def _do_3d(self):
        if self.doplot3d:
            M = self.scene3d.mlab
            try:
                xi = self.invaxisnames[self.plot3daxes[0]]
                yi = self.invaxisnames[self.plot3daxes[1]]
                zi = self.invaxisnames[self.plot3daxes[2]]

                x,y,z = self.data[xi],self.data[yi],self.data[zi]
                w = self.weights

                M.clf()
                if self.scalefactor3d == 0:
                    sf = x.max()-x.min()
                    sf *= y.max()-y.min()
                    sf *= z.max()-z.min()
                    sf = sf/len(x)/5
                    self.scalefactor3d = sf
                else:
                    sf = self.scalefactor3d
                glyph = M.points3d(x,y,z,w,scale_factor=sf)
                glyph.glyph.scale_mode = 0 if self.do3dscale else 1
                M.axes(xlabel=self.plot3daxes[0],ylabel=self.plot3daxes[1],zlabel=self.plot3daxes[2])

                try:
                    xs = np.linspace(np.min(x),np.max(x),self.nmodel3d)

                    #find sequence of models to go from x to y and z
                    ymods,zmods = [],[]
                    for curri,mods in zip((yi,zi),(ymods,zmods)):
                        while curri != xi:
                            for i,(i1,i2) in enumerate(self.curveaxes):
                                if curri==i2:
                                    curri = i1
                                    mods.insert(0,self.fgs[i].tmodel.model)
                                    break
                            else:
                                raise KeyError

                    ys = xs
                    for m in ymods:
                        ys = m(ys)
                    zs = xs
                    for m in zmods:
                        zs = m(zs)

                    if self.usecolor3d:
                        c = (self.color3d[0]/255,self.color3d[1]/255,self.color3d[2]/255)
                        M.plot3d(xs,ys,zs,color=c)
                    else:
                        M.plot3d(xs,ys,zs,np.arange(len(xs)))
                except (KeyError,TypeError):
                    M.text(0.5,0.75,'Underivable relation')
            except KeyError:
                M.clf()
                M.text(0.25,0.25,'Data problem')



    @cached_property
    def _get_invaxisnames(self):
        d={}
        for k,v in self.axisnames.iteritems():
            d[v] = k
        return d
Пример #4
0
class StarComponent(Component):
    stars = List(Star)
    star_color = Color((255,255,255))
    edges = Range(3, 10, 5)
    sx = Float # 移动开始时的星星中心X坐标
    sy = Float # 移动开始时的星星中心Y坐标
    mx = Float # 移动开始时的鼠标X坐标
    my = Float # 移动开始时的鼠标Y坐标
    moving_star = Instance(Star)
    
    event_state = Enum("normal", "drawing", "moving") 


    def normal_left_down(self, event):
        "添加一个Star对象进stars列表,并切换到drawing状态"
        self.stars.append(
            Star(x=event.x, y=event.y, r=0, theta=0, n=self.edges,
                s = 0.5, c=convert_color(self.star_color)))
        self.event_state = "drawing" 
        self.request_redraw()
        
    def drawing_mouse_move(self, event):
        "修改stars中最后一个Star对象的半径和起始角度"
        star = self.stars[-1]
        star.r = np.sqrt((event.x-star.x)**2+(event.y-star.y)**2)
        star.theta = np.arctan2(event.y-star.y, event.x-star.x)
        self.request_redraw()
        
    def drawing_left_up(self, event):
        "完成一个星形的绘制,回到normal状态"
        self.event_state = "normal"        
        
    def normal_mouse_wheel(self, event):
        "找到包含鼠标坐标的星形,并修改其半径比例"
        star = self.find_star(event.x, event.y) 
        if star is not None:
            star.s += event.mouse_wheel * 0.02
            if star.s < 0.05: star.s = 0.05
            self.request_redraw()
                
    def normal_right_down(self, event):
        "找到包含鼠标坐标的星形,用moving_star属性保存它,并进入moving状态"
        star = self.find_star(event.x, event.y)
        if star is not None:
            self.mx, self.my = event.x, event.y # 记录鼠标位置
            self.sx, self.sy = star.x, star.y # 记录星形的中心位置
            self.moving_star = star
            self.event_state = "moving"
    
    def moving_mouse_move(self, event):
        "修改moving_star的x,y坐标,实现星形的移动"
        self.moving_star.x = self.sx + event.x - self.mx
        self.moving_star.y = self.sy + event.y - self.my
        self.request_redraw()
        
    def moving_right_up(self, event):
        "移动操作结束,回到normal状态"
        self.event_state = "normal"

        
    def _draw_overlay(self, gc, view_bounds=None, mode="normal"):
        gc.clear((0,0,0,1)) #填充为全黑
        gc.save_state()
        for star in self.stars:
            draw_star(gc, star.x, star.y, star.r, star.c, star.theta, star.n, star.s)
            gc.draw_path()
        gc.restore_state()        


    def find_star(self, x, y):
        from enthought.kiva.agg import points_in_polygon
        for star in self.stars[::-1]:
            if points_in_polygon((x, y), star.polygon()):        
                return star
        return None
Пример #5
0
class TableColumn(HasPrivateTraits):
    """ Represents a column in a table editor.
    """

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

    # Column label to use for this column
    label = Str(UndefinedLabel)

    # Type of data contained by the column
    type = Enum('text')

    # Text color for this column
    text_color = Color('black')

    # Text font for this column
    text_font = Font

    # Cell background color for this column
    cell_color = Color('white')

    # Cell background color for non-editable columns
    read_only_cell_color = Color(0xF4F3EE)

    # Callable called when the mouse moves into the column
    mouse_over = Callable

    # Horizontal alignment of text in the column
    horizontal_alignment = Enum('left', ['left', 'center', 'right'])

    # Vertical alignment of text in the column
    vertical_alignment = Enum('center', ['top', 'center', 'bottom'])

    # Is the table column visible (i.e., viewable)?
    visible = true

    # Is this column editable?
    editable = true

    # Can external objects be dropped on the column?
    droppable = false

    # Context menu to display when this column is right-clicked
    menu = Instance(Menu)

    # The width of the column (< 0.0: Default, 0.0..1.0: fraction of total table
    # width, > 1.0: absolute width in pixels)
    width = Float(-1.0)

    #---------------------------------------------------------------------------
    #  Returns the actual object being edited:
    #---------------------------------------------------------------------------

    def get_object(self, object):
        """ Returns the actual object being edited.
        """
        return object

    #---------------------------------------------------------------------------
    #  Gets the label of the column:
    #---------------------------------------------------------------------------

    def get_label(self):
        """ Gets the label of the column.
        """
        return self.label

    #---------------------------------------------------------------------------
    #  Returns the width of the column:
    #---------------------------------------------------------------------------

    def get_width(self):
        """ Returns the width of the column.
        """
        return self.width

    #---------------------------------------------------------------------------
    #  Gets the type of data for the column for a specified object:
    #---------------------------------------------------------------------------

    def get_type(self, object):
        """ Gets the type of data for the column for a specified object.
        """
        return self.type

    #---------------------------------------------------------------------------
    #  Returns the text color for the column for a specified object:
    #---------------------------------------------------------------------------

    def get_text_color(self, object):
        """ Returns the text color for the column for a specified object.
        """
        return self.text_color_

    #---------------------------------------------------------------------------
    #  Returns the text font for the column for a specified object:
    #---------------------------------------------------------------------------

    def get_text_font(self, object):
        """ Returns the text font for the column for a specified object.
        """
        return self.text_font

    #---------------------------------------------------------------------------
    #  Returns the cell background color for the column for a specified object:
    #---------------------------------------------------------------------------

    def get_cell_color(self, object):
        """ Returns the cell background color for the column for a specified
            object.
        """
        if self.is_editable(object):
            return self.cell_color_
        return self.read_only_cell_color_

    #---------------------------------------------------------------------------
    #  Returns the horizontal alignment for the column for a specified object:
    #---------------------------------------------------------------------------

    def get_horizontal_alignment(self, object):
        """ Returns the horizontal alignment for the column for a specified
            object.
        """
        return self.horizontal_alignment

    #---------------------------------------------------------------------------
    #  Returns the vertical alignment for the column for a specified object:
    #---------------------------------------------------------------------------

    def get_vertical_alignment(self, object):
        """ Returns the vertical alignment for the column for a specified
            object.
        """
        return self.vertical_alignment

    #---------------------------------------------------------------------------
    #  Returns whether the column is editable for a specified object:
    #---------------------------------------------------------------------------

    def is_editable(self, object):
        """ Returns whether the column is editable for a specified object.
        """
        return self.editable

    #---------------------------------------------------------------------------
    #  Returns whether a specified value is valid for dropping on the column
    #  for a specified object:
    #---------------------------------------------------------------------------

    def is_droppable(self, object, value):
        """ Returns whether a specified value is valid for dropping on the
            column for a specified object.
        """
        return self.droppable

    #---------------------------------------------------------------------------
    #  Returns the context menu to display when the user right-clicks on the
    #  column for a specified object:
    #---------------------------------------------------------------------------

    def get_menu(self, object):
        """ Returns the context menu to display when the user right-clicks on
            the column for a specified object.
        """
        return self.menu

    #---------------------------------------------------------------------------
    #  Called when the mouse moves into the column:
    #---------------------------------------------------------------------------

    def on_mouse_over(self, object):
        """ Called when the mouse moves into the column.
        """
        if self.mouse_over is not None:
            self.mouse_over(object)

    #---------------------------------------------------------------------------
    #  Returns the string representation of the table column:
    #---------------------------------------------------------------------------

    def __str__(self):
        """ Returns the string representation of the table column.
        """
        return self.get_label()
Пример #6
0
class NumericColumn(ObjectColumn):
    """ A column for editing Numeric arrays.
    """
    #---------------------------------------------------------------------------
    #  Trait definitions:
    #---------------------------------------------------------------------------

    # Column label to use for this column
    label = Property

    # Text color this column when selected
    selected_text_color = Color('black')

    # Text font for this column when selected
    selected_text_font = Font

    # Cell background color for this column when selected
    selected_cell_color = Color(0xD8FFD8)

    # Formatting string for the cell value
    format = Str('%s')

    # Horizontal alignment of text in the column; this value overrides the
    # default.
    horizontal_alignment = 'center'

    #---------------------------------------------------------------------------
    #  Implementation of the 'label' property:
    #---------------------------------------------------------------------------

    def _get_label(self):
        """ Gets the label of the column.
        """
        if self._label is not None:
            return self._label
        return self.name

    def _set_label(self, label):
        old, self._label = self._label, label
        if old != label:
            self.trait_property_changed('label', old, label)

    #---------------------------------------------------------------------------
    #  Gets the type of data for the column for a specified object row:
    #---------------------------------------------------------------------------

    def get_type(self, object, row):
        """ Gets the type of data for the column for a specified object row.
        """
        return self.type

    #---------------------------------------------------------------------------
    #  Returns the text color for the column for a specified object row:
    #---------------------------------------------------------------------------

    def get_text_color(self, object, row):
        """ Returns the text color for the column for a specified object row.
        """
        if self._is_selected(object, row):
            return self.selected_text_color_
        return self.text_color_

    #---------------------------------------------------------------------------
    #  Returns the text font for the column for a specified object row:
    #---------------------------------------------------------------------------

    def get_text_font(self, object, row):
        """ Returns the text font for the column for a specified object row.
        """
        if self._is_selected(object, row):
            return self.selected_text_font
        return self.text_font

    #---------------------------------------------------------------------------
    #  Returns the cell background color for the column for a specified object
    #  row:
    #---------------------------------------------------------------------------

    def get_cell_color(self, object, row):
        """ Returns the cell background color for the column for a specified
            object row.
        """
        if self.is_editable(object, row):
            if self._is_selected(object, row):
                return self.selected_cell_color_
            return self.cell_color_
        return self.read_only_cell_color_

    #---------------------------------------------------------------------------
    #  Returns the horizontal alignment for the column for a specified object
    #  row:
    #---------------------------------------------------------------------------

    def get_horizontal_alignment(self, object, row):
        """ Returns the horizontal alignment for the column for a specified
            object row.
        """
        return self.horizontal_alignment

    #---------------------------------------------------------------------------
    #  Returns the vertical alignment for the column for a specified object row:
    #---------------------------------------------------------------------------

    def get_vertical_alignment(self, object, row):
        """ Returns the vertical alignment for the column for a specified
            object row.
        """
        return self.vertical_alignment

    #---------------------------------------------------------------------------
    #  Returns whether the column is editable for a specified object row:
    #---------------------------------------------------------------------------

    def is_editable(self, object, row):
        """ Returns whether the column is editable for a specified object row.
        """
        return self.editable

    #---------------------------------------------------------------------------
    #  Returns whether a specified value is valid for dropping on the column
    #  for a specified object row:
    #---------------------------------------------------------------------------

    def is_droppable(self, object, row, value):
        """ Returns whether a specified value is valid for dropping on the
            column for a specified object row.
        """
        return self.droppable

    #---------------------------------------------------------------------------
    #  Returns the context menu to display when the user right-clicks on the
    #  column for a specified object row:
    #---------------------------------------------------------------------------

    def get_menu(self, object, row):
        """ Returns the context menu to display when the user right-clicks on
            the column for a specified object row.
        """
        return self.menu

    #---------------------------------------------------------------------------
    #  Gets the value of the column for a specified object row:
    #---------------------------------------------------------------------------

    def get_value(self, object, row):
        """ Gets the value of the column for a specified object row.
        """
        try:
            value = getattr(object, self.name)[row]
            try:
                return self.format % value
            except:
                return 'Format!'
        except:
            return 'Undefined!'

    #---------------------------------------------------------------------------
    #  Sets the value of the column for a specified object row:
    #---------------------------------------------------------------------------

    def set_value(self, object, row, value):
        """ Sets the value of the column for a specified object row.
        """
        column = self.get_data_column(object)
        column[row] = type(column[row])(value)

    #---------------------------------------------------------------------------
    #  Gets the editor for the column of a specified object row:
    #---------------------------------------------------------------------------

    def get_editor(self, object, row):
        """ Gets the editor for the column of a specified object row.
        """
        return super(NumericColumn, self).get_editor(object)

    #---------------------------------------------------------------------------
    #  Gets the entire contents of the specified object column:
    #---------------------------------------------------------------------------

    def get_data_column(self, object):
        """ Gets the entire contents of the specified object column.
        """
        return getattr(object, self.name)

    #---------------------------------------------------------------------------
    #  Returns whether a specified object row is selected or not:
    #---------------------------------------------------------------------------

    def _is_selected(self, object, row):
        """ Returns whether a specified object row is selected.
        """
        selection = object.model_selection
        return (selection is not None) and (selection[row] != 0)
Пример #7
0
#)

color_schemes = [
    "X11", "Accent", "Blues", "BRBG", "BUGN", "BUPU", "Dark", "GUBU", "Greens",
    "Greys", "Oranges", "OORD", "Paired", "Pastel", "PIYG", "PRGN", "PUBU",
    "PUBUGN", "PUOR", "PURD", "Purples", "RDBU", "RDGY", "RDPU", "RDYLBU",
    "RDYLGN", "Reds", "Set", "Spectral", "YLGN", "YLGNBU", "YLORBR", "YLORRD"
]

color_scheme_trait = Enum(color_schemes,
                          desc="a color scheme namespace",
                          label="Color scheme",
                          graphviz=True)

color_trait = Color("black",
                    desc="drawing color for graphics, not text",
                    graphviz=True)

comment_trait = Str(desc="comments inserted into output", graphviz=True)

fontcolor_trait = Color("black",
                        desc="color used for text",
                        label="Font color",
                        graphviz=True)

fontname_trait = Font("Times-Roman",
                      desc="font used for text",
                      label="Font name",
                      graphviz=True)

fontsize_trait = Float(14.0,
Пример #8
0
class TraitsTest(HasTraits):

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

    enabled = true
    integer_text = Int(1)
    enumeration = Trait('one', 'two', 'three', 'four', 'five', 'six', cols=3)
    float_range = Range(0.0, 10.0, 10.0)
    int_range = Range(1, 6)
    int_range2 = Range(1, 50)
    compound = Trait(1, Range(1, 6), 'one', 'two', 'three', 'four', 'five',
                     'six')
    boolean = true
    instance = Trait(Instance())
    color = Color('cyan')
    font = Font()
    check_list = List(
        editor=CheckListEditor(values=['one', 'two', 'three', 'four'], cols=4))
    list = List(Str,
                ['East of Eden', 'The Grapes of Wrath', 'Of Mice and Men'])
    button = Event(0, editor=ButtonEditor(label='Click'))
    file = File()
    directory = Directory()
    image_enum = Trait(editor=ImageEnumEditor(values=origin_values,
                                              suffix='_origin',
                                              cols=4,
                                              klass=Instance),
                       *origin_values)

    #---------------------------------------------------------------------------
    #  View definitions:
    #---------------------------------------------------------------------------

    view = View(
        ('|{Enum}', ('enabled', ),
         ('|<[Enumeration]', 'f1:enumeration[Simple]', '_',
          'f2:enumeration[Custom]@', '_', 'f3:enumeration[Text]*', '_',
          'f4:enumeration[Readonly]~'),
         ('|<[Check List]', 'f5:check_list[Simple]', '_',
          'f6:check_list[Custom]@', '_', 'f7:check_list[Text]*', '_',
          'f8:check_list[Readonly]~')),
        ('|{Range}',
         ('|<[Float Range]', 'f9:float_range[Simple]', '_',
          'f10:float_range[Custom]@', '_', 'f11:float_range[Text]*', '_',
          'f12:float_range[Readonly]~'),
         ('|<[Int Range]', 'f13:int_range[Simple]', '_',
          'f14:int_range[Custom]@', '_', 'f15:int_range[Text]*', '_',
          'f16:int_range[Readonly]~'),
         ('|<[Int Range 2]', 'f17:int_range2[Simple]', '_',
          'f18:int_range2[Custom]@', '_', 'f19:int_range2[Text]*', '_',
          'f20:int_range2[Readonly]~')),
        ('|{Misc}',
         ('|<[Integer Text]', 'f21:integer_text[Simple]', '_',
          'f22:integer_text[Custom]@', '_', 'f23:integer_text[Text]*', '_',
          'f24:integer_text[Readonly]~'),
         ('|<[Compound]', 'f25:compound[Simple]', '_', 'f26:compound[Custom]@',
          '_', 'f27:compound[Text]*', '_', 'f28:compound[Readonly]~'),
         ('|<[Boolean]', 'f29:boolean[Simple]', '_', 'f30:boolean[Custom]@',
          '_', 'f31:boolean[Text]*', '_', 'f32:boolean[Readonly]~')),
        ('|{Color/Font}',
         ('|<[Color]', 'f33:color[Simple]', '_', 'f34:color[Custom]@', '_',
          'f35:color[Text]*', '_', 'f36:color[Readonly]~'),
         ('|<[Font]', 'f37:font[Simple]', '_', 'f38:font[Custom]@', '_',
          'f39:font[Text]*', '_', 'f40:font[Readonly]~')),
        ('|{List}', ('|<[List]', 'f41:list[Simple]', '_', 'f42:list[Custom]@',
                     '_', 'f43:list[Text]*', '_', 'f44:list[Readonly]~')),
        (
            '|{Button}',
            ('|<[Button]', 'f45:button[Simple]', '_', 'f46:button[Custom]@'),
            #                                        'button[Text]*',
            #                                        'button[Readonly]~' ),
            ('|<[Image Enum]', 'f47:image_enum[Simple]', '_',
             'f48:image_enum[Custom]@', '_', 'f49:image_enum[Text]*', '_',
             'f50:image_enum[Readonly]~'),
            ('|<[Instance]', 'f51:instance[Simple]', '_',
             'f52:instance[Custom]@', '_', 'f53:instance[Text]*', '_',
             'f54:instance[Readonly]~'),
        ),
        ('|{File}', (
            '|<[File]',
            'f55:file[Simple]',
            '_',
            'f56:file[Custom]@',
            '_',
            'f57:file[Text]*',
            '_',
            'f58:file[Readonly]~',
        ), ('|<[Directory]', 'f59:directory[Simple]', '_',
            'f60:directory[Custom]@', '_', 'f61:directory[Text]*', '_',
            'f62:directory[Readonly]~')),
        apply=True,
        revert=True,
        undo=True,
        ok=True,
        handler=TraitsTestHandler())
Пример #9
0
class FeatureBar(HasPrivateTraits):

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

    # The wx.Window which is the parent for the FeatureBar:
    parent = Instance(wx.Window)

    # The DockControl whose features are being displayed:
    dock_control = Instance(DockControl)

    # The wx.Window being used for the FeatureBar:
    control = Instance(wx.Window)

    # Event posted when the user has completed using the FeatureBar:
    completed = Event

    # The background color for the FeatureBar:
    bg_color = Color(0xDBEEF7, allow_none=True)

    # The border color for the FeatureBar:
    border_color = Color(0X2583AF, allow_none=True)

    # Should the feature bar display horizontally (or vertically)?
    horizontal = Bool(True)

    #---------------------------------------------------------------------------
    #  Hides the feature bar:
    #---------------------------------------------------------------------------

    def hide(self):
        """ Hides the feature bar.
        """
        if self.control is not None:
            self.control.Hide()

    #---------------------------------------------------------------------------
    #  Shows the feature bar:
    #---------------------------------------------------------------------------

    def show(self):
        """ Shows the feature bar.
        """
        # Make sure all prerequisites are met:
        dock_control, parent = self.dock_control, self.parent
        if (dock_control is None) or (parent is None):
            return

        # Create the actual control (if needed):
        control = self.control
        if control is None:
            self.control = control = wx.Frame(None,
                                              -1,
                                              '',
                                              style=wx.BORDER_NONE)

            # Set up the 'erase background' event handler:
            wx.EVT_ERASE_BACKGROUND(control, self._erase_background)

            # Set up the 'paint' event handler:
            wx.EVT_PAINT(control, self._paint)

            # Set up mouse event handlers:
            wx.EVT_LEFT_DOWN(control, self._left_down)
            wx.EVT_LEFT_UP(control, self._left_up)
            wx.EVT_RIGHT_DOWN(control, self._right_down)
            wx.EVT_RIGHT_UP(control, self._right_up)
            wx.EVT_MOTION(control, self._mouse_move)
            wx.EVT_ENTER_WINDOW(control, self._mouse_enter)

            control.SetDropTarget(PythonDropTarget(self))

        # Calculate the best size and position for the feature bar:
        size = wx.Size(32, 32)
        width = height = 0
        horizontal = self.horizontal
        for feature in dock_control.active_features:
            bitmap = feature.bitmap
            if bitmap is not None:
                if horizontal:
                    width += (bitmap.GetWidth() + 3)
                    height = max(height, bitmap.GetHeight())
                else:
                    width = max(width, bitmap.GetWidth())
                    height += (bitmap.GetHeight() + 3)

        if width > 0:
            if horizontal:
                size = wx.Size(width + 5, height + 8)
            else:
                size = wx.Size(width + 8, height + 5)

        control.SetSize(size)
        px, py = parent.GetScreenPosition()
        fx, fy = dock_control.feature_popup_position
        control.SetPosition(wx.Point(px + fx, py + fy))
        control.Show()

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

    #---------------------------------------------------------------------------
    #  Handles repainting the window:
    #---------------------------------------------------------------------------

    def _paint(self, event):
        """ Handles repainting the window.
        """
        window = self.control
        dx, dy = window.GetSizeTuple()
        dc = wx.PaintDC(window)

        # Draw the feature container:
        bg_color = self.bg_color
        border_color = self.border_color
        if (bg_color is not None) or (border_color is not None):
            if border_color is None:
                dc.SetPen(wx.TRANSPARENT_PEN)
            else:
                dc.SetPen(wx.Pen(border_color, 1, wx.SOLID))
            if bg_color is None:
                dc.SetBrush(wx.TRANSPARENT_PEN)
            else:
                dc.SetBrush(wx.Brush(bg_color, wx.SOLID))
            dc.DrawRectangle(0, 0, dx, dy)

        # Draw the feature icons:
        if self.horizontal:
            x = 4
            for feature in self.dock_control.active_features:
                bitmap = feature.bitmap
                if bitmap is not None:
                    dc.DrawBitmap(bitmap, x, 4, True)
                    x += (bitmap.GetWidth() + 3)
        else:
            y = 4
            for feature in self.dock_control.active_features:
                bitmap = feature.bitmap
                if bitmap is not None:
                    dc.DrawBitmap(bitmap, 4, y, True)
                    y += (bitmap.GetHeight() + 3)

    #---------------------------------------------------------------------------
    #  Handles erasing the window background:
    #---------------------------------------------------------------------------

    def _erase_background(self, event):
        """ Handles erasing the window background.
        """
        pass

    #---------------------------------------------------------------------------
    #  Handles the left mouse button being pressed:
    #---------------------------------------------------------------------------

    def _left_down(self, event):
        """ Handles the left mouse button being pressed.
        """
        self._feature = self._feature_at(event)
        self._dragging = False
        self._xy = (event.GetX(), event.GetY())
        #self.control.CaptureMouse()

    #---------------------------------------------------------------------------
    #  Handles the left mouse button being released:
    #---------------------------------------------------------------------------

    def _left_up(self, event):
        """ Handles the left mouse button being released.
        """
        #self.control.ReleaseMouse()
        self._dragging = None
        feature, self._feature = self._feature, None
        if feature is not None:
            if feature is self._feature_at(event):
                self.control.ReleaseMouse()
                self.completed = True
                feature._set_event(event)
                feature.click()

    #---------------------------------------------------------------------------
    #  Handles the right mouse button being pressed:
    #---------------------------------------------------------------------------

    def _right_down(self, event):
        """ Handles the right mouse button being pressed.
        """
        self._feature = self._feature_at(event)
        self._dragging = False
        self._xy = (event.GetX(), event.GetY())
        #self.control.CaptureMouse()

    #---------------------------------------------------------------------------
    #  Handles the right mouse button being released:
    #---------------------------------------------------------------------------

    def _right_up(self, event):
        """ Handles the right mouse button being released.
        """
        #self.control.ReleaseMouse()
        self._dragging = None
        feature, self._feature = self._feature, None
        if feature is not None:
            if feature is self._feature_at(event):
                self.control.ReleaseMouse()
                self.completed = True
                feature._set_event(event)
                feature.right_click()

    #---------------------------------------------------------------------------
    #  Handles the mouse moving over the window:
    #---------------------------------------------------------------------------

    def _mouse_move(self, event):
        """ Handles the mouse moving over the window.
        """
        # Update tooltips if no mouse button is currently pressed:
        if self._dragging is None:
            feature = self._feature_at(event)
            if feature is not self._tooltip_feature:
                self._tooltip_feature = feature
                tooltip = ''
                if feature is not None:
                    tooltip = feature.tooltip
                wx.ToolTip.Enable(False)
                wx.ToolTip.Enable(True)
                self.control.SetToolTip(wx.ToolTip(tooltip))

            # Check to see if the mouse has left the window, and mark it
            # completed if it has:
            x, y = event.GetX(), event.GetY()
            dx, dy = self.control.GetSizeTuple()
            if (x < 0) or (y < 0) or (x >= dx) or (y >= dy):
                self.control.ReleaseMouse()
                self._tooltip_feature = None
                self.completed = True

            return

        # Check to see if we are in 'drag mode' yet:
        if not self._dragging:
            x, y = self._xy
            if (abs(x - event.GetX()) + abs(y - event.GetY())) < 3:
                return

            self._dragging = True

            # Check to see if user is trying to drag a 'feature':
            feature = self._feature
            if feature is not None:
                feature._set_event(event)

                prefix = button = ''
                if event.RightIsDown():
                    button = 'right_'
                if event.ControlDown():
                    prefix = 'control_'
                elif event.AltDown():
                    prefix = 'alt_'
                elif event.ShiftDown():
                    prefix = 'shift_'

                object = getattr(feature, '%s%sdrag' % (prefix, button))()
                if object is not None:
                    self.control.ReleaseMouse()
                    self._feature = None
                    self.completed = True
                    self.dock_control.pre_drag_all(object)
                    PythonDropSource(self.control, object)
                    self.dock_control.post_drag_all()
                    self._dragging = None

    #---------------------------------------------------------------------------
    #  Handles the mouse entering the window:
    #---------------------------------------------------------------------------

    def _mouse_enter(self, event):
        """ Handles the mouse entering the window.
        """
        self.control.CaptureMouse()

#-- Drag and drop event handlers: ----------------------------------------------

#---------------------------------------------------------------------------
#  Handles a Python object being dropped on the control:
#---------------------------------------------------------------------------

    def wx_dropped_on(self, x, y, data, drag_result):
        """ Handles a Python object being dropped on the window.
        """
        # Determine what, if any, feature the object was dropped on:
        feature = self._can_drop_on_feature(x, y, data)

        # Indicate use of the feature bar is complete:
        self.completed = True

        # Reset any drag state information:
        self.dock_control.post_drag(FEATURE_EXTERNAL_DRAG)

        # Check to see if the data was dropped on a feature or not:
        if feature is not None:
            if isinstance(data, IFeatureTool):
                # Handle an object implementing IFeatureTool being dropped:
                dock_control = feature.dock_control
                data.feature_dropped_on_dock_control(dock_control)
                data.feature_dropped_on(dock_control.object)
            else:
                # Handle a normal object being dropped:
                wx, wy = self.control.GetScreenPosition()
                feature.set(x=wx + x, y=wy + y)
                feature.drop(data)

            return drag_result

        return wx.DragNone

    #---------------------------------------------------------------------------
    #  Handles a Python object being dragged over the control:
    #---------------------------------------------------------------------------

    def wx_drag_over(self, x, y, data, drag_result):
        """ Handles a Python object being dragged over the control.
        """
        # Handle the case of dragging a normal object over a 'feature':
        if self._can_drop_on_feature(x, y, data) is not None:
            return drag_result

        return wx.DragNone

    #---------------------------------------------------------------------------
    #  Handles a dragged Python object leaving the window:
    #---------------------------------------------------------------------------

    def wx_drag_leave(self, data):
        """ Handles a dragged Python object leaving the window.
        """
        # Indicate use of the feature bar is complete:
        self.completed = True

        # Reset any drag state information:
        self.dock_control.post_drag(FEATURE_EXTERNAL_DRAG)

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

    #---------------------------------------------------------------------------
    #  Returns a feature that the pointer is over and which can accept the
    #  specified data:
    #---------------------------------------------------------------------------

    def _can_drop_on_feature(self, x, y, data):
        """ Returns a feature that the pointer is over and which can accept the
            specified data.
        """
        feature = self._feature_at(FakeEvent(x, y))
        if (feature is not None) and feature.can_drop(data):
            return feature

        return None

    #---------------------------------------------------------------------------
    #  Returns the DockWindowFeature (if any) at a specified window position:
    #---------------------------------------------------------------------------

    def _feature_at(self, event):
        """ Returns the DockWindowFeature (if any) at a specified window
            position.
        """
        if self.horizontal:
            x = 4
            for feature in self.dock_control.active_features:
                bitmap = feature.bitmap
                if bitmap is not None:
                    bdx = bitmap.GetWidth()
                    if self._is_in(event, x, 4, bdx, bitmap.GetHeight()):
                        return feature

                    x += (bdx + 3)
        else:
            y = 4
            for feature in self.dock_control.active_features:
                bitmap = feature.bitmap
                if bitmap is not None:
                    bdy = bitmap.GetHeight()
                    if self._is_in(event, 4, y, bitmap.GetWidth(), bdy):
                        return feature

                    y += (bdy + 3)

        return None

    #---------------------------------------------------------------------------
    #  Returns whether or not an event is within a specified bounds:
    #---------------------------------------------------------------------------

    def _is_in(self, event, x, y, dx, dy):
        """ Returns whether or not an event is within a specified bounds.
        """
        return ((x <= event.GetX() < (x + dx)) and (y <= event.GetY() <
                                                    (y + dy)))
Пример #10
0
class Edge(HasTraits):
    """ Defines a graph edge. """

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

    # Tail/from/source/start node.
    tail_node = Instance(Node, allow_none=False)

    # Head/to/target/end node.
    head_node = Instance(Node, allow_none=False)

    # String identifier (TreeNode label).
    name = Property(
        Str,
        depends_on=["tail_node", "tail_node.ID", "head_node", "head_node.ID"])

    # Connection string used in string output.
    conn = Enum("->", "--")

    # Nodes from which the tail and head nodes may be selected.
    _nodes = List(Instance(Node))  # GUI specific.

    #--------------------------------------------------------------------------
    #  Xdot trait definitions:
    #--------------------------------------------------------------------------

    # For a given graph object, one will typically a draw directive before the
    # label directive. For example, for a node, one would first use the
    # commands in _draw_ followed by the commands in _ldraw_.
    _draw_ = Str(desc="xdot drawing directive", label="draw")
    _ldraw_ = Str(desc="xdot label drawing directive", label="ldraw")

    _hdraw_ = Str(desc="edge head arrowhead drawing directive.", label="hdraw")
    _tdraw_ = Str(desc="edge tail arrowhead drawing directive.", label="tdraw")
    _hldraw_ = Str(desc="edge head label drawing directive.", label="hldraw")
    _tldraw_ = Str(desc="edge tail label drawing directive.", label="tldraw")

    #--------------------------------------------------------------------------
    #  Enable trait definitions:
    #--------------------------------------------------------------------------

    # Container of drawing components, typically the edge spline.
    drawing = Instance(Container)

    # Container of label components.
    label_drawing = Instance(Container)

    # Container of head arrow components.
    arrowhead_drawing = Instance(Container)

    # Container of tail arrow components.
    arrowtail_drawing = Instance(Container)

    # Container of head arrow label components.
    arrowhead_label_drawing = Instance(Container)

    # Container of tail arrow label components.
    arrowtail_label_drawing = Instance(Container)

    # Container for the drawing, label, arrow and arrow label components.
    component = Instance(Container, desc="container of graph components.")

    # A view into a sub-region of the canvas.
    vp = Instance(Viewport, desc="a view of a sub-region of the canvas")

    # Use Graphviz to arrange all graph components.
    arrange = Button("Arrange All")

    #--------------------------------------------------------------------------
    #  Dot trait definitions:
    #--------------------------------------------------------------------------

    # Style of arrowhead on the head node of an edge.
    # See also the <html:a rel="attr">dir</html:a> attribute,
    # and the <html:a rel="note">undirected</html:a> note.
    arrowhead = arrow_trait

    # Multiplicative scale factor for arrowheads.
    arrowsize = Float(1.0,
                      desc="multiplicative scale factor for arrowheads",
                      label="Arrow size",
                      graphviz=True)

    # Style of arrowhead on the tail node of an edge.
    # See also the <html:a rel="attr">dir</html:a> attribute,
    # and the <html:a rel="note">undirected</html:a> note.
    arrowtail = arrow_trait

    # Basic drawing color for graphics, not text. For the latter, use the
    # <html:a rel="attr">fontcolor</html:a> attribute.
    #
    # For edges, the value
    # can either be a single <html:a rel="type">color</html:a> or a <html:a rel="type">colorList</html:a>.
    # In the latter case, the edge is drawn using parallel splines or lines,
    # one for each color in the list, in the order given.
    # The head arrow, if any, is drawn using the first color in the list,
    # and the tail arrow, if any, the second color. This supports the common
    # case of drawing opposing edges, but using parallel splines instead of
    # separately routed multiedges.
    color = color_trait

    # This attribute specifies a color scheme namespace. If defined, it specifies
    # the context for interpreting color names. In particular, if a
    # <html:a rel="type">color</html:a> value has form <html:code>xxx</html:code> or <html:code>//xxx</html:code>,
    # then the color <html:code>xxx</html:code> will be evaluated according to the current color scheme.
    # If no color scheme is set, the standard X11 naming is used.
    # For example, if <html:code>colorscheme=bugn9</html:code>, then <html:code>color=7</html:code>
    # is interpreted as <html:code>/bugn9/7</html:code>.
    colorscheme = color_scheme_trait

    # Comments are inserted into output. Device-dependent.
    comment = comment_trait

    # If <html:span class="val">false</html:span>, the edge is not used in
    # ranking the nodes.
    constraint = Bool(True,
                      desc="if edge is used in ranking the nodes",
                      graphviz=True)

    # If <html:span class="val">true</html:span>, attach edge label to edge by a 2-segment
    # polyline, underlining the label, then going to the closest point of spline.
    decorate = Bool(
        False,
        desc="to attach edge label to edge by a 2-segment "
        "polyline, underlining the label, then going to the closest point of "
        "spline",
        graphviz=True)

    # Set edge type for drawing arrowheads. This indicates which ends of the
    # edge should be decorated with an arrowhead. The actual style of the
    # arrowhead can be specified using the <html:a rel="attr">arrowhead</html:a>
    # and <html:a rel="attr">arrowtail</html:a> attributes.
    # See <html:a rel="note">undirected</html:a>.
    dir = Enum("forward",
               "back",
               "both",
               "none",
               label="Direction",
               desc="edge type for drawing arrowheads",
               graphviz=True)

    # Synonym for <html:a rel="attr">edgeURL</html:a>.
    #    edgehref = Alias("edgeURL", desc="synonym for edgeURL")
    edgehref = Synced(sync_to="edgeURL", graphviz=True)

    # If the edge has a URL or edgeURL  attribute, this attribute determines
    # which window of the browser is used for the URL attached to the non-label
    # part of the edge. Setting it to "_graphviz" will open a new window if it
    # doesn't already exist, or reuse it if it does. If undefined, the value of
    # the target is used.
    edgetarget = Str("",
                     desc="which window of the browser is used for the "
                     "URL attached to the non-label part of the edge",
                     label="Edge target",
                     graphviz=True)

    # Tooltip annotation attached to the non-label part of an edge.
    # This is used only if the edge has a <html:a rel="attr">URL</html:a>
    # or <html:a rel="attr">edgeURL</html:a> attribute.
    edgetooltip = Str("",
                      desc="annotation attached to the non-label part of "
                      "an edge",
                      label="Edge tooltip",
                      graphviz=True)
    #    edgetooltip = EscString

    # If <html:a rel="attr">edgeURL</html:a> is defined, this is the link used for the non-label
    # parts of an edge. This value overrides any <html:a rel="attr">URL</html:a>
    # defined for the edge.
    # Also, this value is used near the head or tail node unless overridden
    # by a <html:a rel="attr">headURL</html:a> or <html:a rel="attr">tailURL</html:a> value,
    # respectively.
    # See <html:a rel="note">undirected</html:a>.
    edgeURL = Str("",
                  desc="link used for the non-label parts of an edge",
                  label="Edge URL",
                  graphviz=True)  #LabelStr

    # Color used for text.
    fontcolor = fontcolor_trait

    # Font used for text. This very much depends on the output format and, for
    # non-bitmap output such as PostScript or SVG, the availability of the font
    # when the graph is displayed or printed. As such, it is best to rely on
    # font faces that are generally available, such as Times-Roman, Helvetica or
    # Courier.
    #
    # If Graphviz was built using the
    # <html:a href="http://pdx.freedesktop.org/~fontconfig/fontconfig-user.html">fontconfig library</html:a>, the latter library
    # will be used to search for the font. However, if the <html:a rel="attr">fontname</html:a> string
    # contains a slash character "/", it is treated as a pathname for the font
    # file, though font lookup will append the usual font suffixes.
    #
    # If Graphviz does not use fontconfig, <html:a rel="attr">fontname</html:a> will be
    # considered the name of a Type 1 or True Type font file.
    # If you specify <html:code>fontname=schlbk</html:code>, the tool will look for a
    # file named  <html:code>schlbk.ttf</html:code> or <html:code>schlbk.pfa</html:code> or <html:code>schlbk.pfb</html:code>
    # in one of the directories specified by
    # the <html:a rel="attr">fontpath</html:a> attribute.
    # The lookup does support various aliases for the common fonts.
    fontname = fontname_trait

    # Font size, in <html:a rel="note">points</html:a>, used for text.
    fontsize = fontsize_trait

    # If <html:span class="val">true</html:span>, the head of an edge is clipped to the boundary of the head node;
    # otherwise, the end of the edge goes to the center of the node, or the
    # center of a port, if applicable.
    headclip = Bool(True,
                    desc="head of an edge to be clipped to the boundary "
                    "of the head node",
                    label="Head clip",
                    graphviz=True)

    # Synonym for <html:a rel="attr">headURL</html:a>.
    headhref = Alias("headURL", desc="synonym for headURL", graphviz=True)

    # Text label to be placed near head of edge.
    # See <html:a rel="note">undirected</html:a>.
    headlabel = Str("",
                    desc="text label to be placed near head of edge",
                    label="Head label",
                    graphviz=True)

    headport = port_pos_trait

    # If the edge has a headURL, this attribute determines which window of the
    # browser is used for the URL. Setting it to "_graphviz" will open a new
    # window if it doesn't already exist, or reuse it if it does. If undefined,
    # the value of the target is used.
    headtarget = Str(desc="which window of the browser is used for the URL",
                     label="Head target",
                     graphviz=True)

    # Tooltip annotation attached to the head of an edge. This is used only
    # if the edge has a <html:a rel="attr">headURL</html:a> attribute.
    headtooltip = Str("",
                      desc="tooltip annotation attached to the head of an "
                      "edge",
                      label="Head tooltip",
                      graphviz=True)

    # If <html:a rel="attr">headURL</html:a> is defined, it is
    # output as part of the head label of the edge.
    # Also, this value is used near the head node, overriding any
    # <html:a rel="attr">URL</html:a> value.
    # See <html:a rel="note">undirected</html:a>.
    headURL = Str("",
                  desc="output as part of the head label of the edge",
                  label="Head URL",
                  graphviz=True)

    # Synonym for <html:a rel="attr">URL</html:a>.
    href = Alias("URL", desc="synonym for URL", graphviz=True)

    # Text label attached to objects.
    # If a node's <html:a rel="attr">shape</html:a> is record, then the label can
    # have a <html:a href="http://www.graphviz.org/doc/info/shapes.html#record">special format</html:a>
    # which describes the record layout.
    label = label_trait

    # This, along with <html:a rel="attr">labeldistance</html:a>, determine
    # where the
    # headlabel (taillabel) are placed with respect to the head (tail)
    # in polar coordinates. The origin in the coordinate system is
    # the point where the edge touches the node. The ray of 0 degrees
    # goes from the origin back along the edge, parallel to the edge
    # at the origin.
    #
    # The angle, in degrees, specifies the rotation from the 0 degree ray,
    # with positive angles moving counterclockwise and negative angles
    # moving clockwise.
    labelangle = Float(
        -25.0,
        desc=", along with labeldistance, where the "
        "headlabel (taillabel) are placed with respect to the head (tail)",
        label="Label angle",
        graphviz=True)

    # Multiplicative scaling factor adjusting the distance that
    # the headlabel (taillabel) is from the head (tail) node.
    # The default distance is 10 points. See <html:a rel="attr">labelangle</html:a>
    # for more details.
    labeldistance = Float(
        1.0,
        desc="multiplicative scaling factor adjusting "
        "the distance that the headlabel (taillabel) is from the head (tail) "
        "node",
        label="Label distance",
        graphviz=True)

    # If true, allows edge labels to be less constrained in position. In
    # particular, it may appear on top of other edges.
    labelfloat = Bool(False,
                      desc="edge labels to be less constrained in "
                      "position",
                      label="Label float",
                      graphviz=True)

    # Color used for headlabel and taillabel.
    # If not set, defaults to edge's fontcolor.
    labelfontcolor = Color("black",
                           desc="color used for headlabel and "
                           "taillabel",
                           label="Label font color",
                           graphviz=True)

    # Font used for headlabel and taillabel.
    # If not set, defaults to edge's fontname.
    labelfontname = Font("Times-Roman",
                         desc="Font used for headlabel and "
                         "taillabel",
                         label="Label font name",
                         graphviz=True)

    # Font size, in <html:a rel="note">points</html:a>, used for headlabel and taillabel.
    # If not set, defaults to edge's fontsize.
    labelfontsize = Float(14.0,
                          desc="Font size, in points, used for "
                          "headlabel and taillabel",
                          label="label_font_size",
                          graphviz=True)

    # Synonym for <html:a rel="attr">labelURL</html:a>.
    labelhref = Alias("labelURL", desc="synonym for labelURL", graphviz=True)

    # If the edge has a URL or labelURL  attribute, this attribute determines
    # which window of the browser is used for the URL attached to the label.
    # Setting it to "_graphviz" will open a new window if it doesn't already
    # exist, or reuse it if it does. If undefined, the value of the target is
    # used.
    labeltarget = Str("",
                      desc="which window of the browser is used for the "
                      "URL attached to the label",
                      label="Label target",
                      graphviz=True)

    # Tooltip annotation attached to label of an edge.
    # This is used only if the edge has a <html:a rel="attr">URL</html:a>
    # or <html:a rel="attr">labelURL</html:a> attribute.
    labeltooltip = Str("",
                       desc="tooltip annotation attached to label of an "
                       "edge",
                       label="Label tooltip",
                       graphviz=True)

    # If <html:a rel="attr">labelURL</html:a> is defined, this is the link used for the label
    # of an edge. This value overrides any <html:a rel="attr">URL</html:a>
    # defined for the edge.
    labelURL = Str(desc="link used for the label of an edge", graphviz=True)

    # Specifies layers in which the node or edge is present.
    layer = layer_trait

    # Preferred edge length, in inches.
    len = Float(1.0, desc="preferred edge length, in inches",
                graphviz=True)  #0.3(fdp)

    # Logical head of an edge. When compound is true, if lhead is defined and
    # is the name of a cluster containing the real head, the edge is clipped to
    # the boundary of the cluster.
    lhead = Str(desc="Logical head of an edge", graphviz=True)

    # Label position, in points. The position indicates the center of the label.
    lp = point_trait

    # Logical tail of an edge. When compound is true, if ltail is defined and
    # is the name of a cluster containing the real tail, the edge is clipped to
    # the boundary of the cluster.
    ltail = Str(desc="logical tail of an edge", graphviz=True)

    # Minimum edge length (rank difference between head and tail).
    minlen = Int(1, desc="minimum edge length", graphviz=True)

    # By default, the justification of multi-line labels is done within the
    # largest context that makes sense. Thus, in the label of a polygonal node,
    # a left-justified line will align with the left side of the node (shifted
    # by the prescribed margin). In record nodes, left-justified line will line
    # up with the left side of the enclosing column of fields. If nojustify is
    # "true", multi-line labels will be justified in the context of itself. For
    # example, if the attribute is set, the first label line is long, and the
    # second is shorter and left-justified, the second will align with the
    # left-most character in the first line, regardless of how large the node
    # might be.
    nojustify = nojustify_trait

    # Position of node, or spline control points.
    # For nodes, the position indicates the center of the node.
    # On output, the coordinates are in <html:a href="#points">points</html:a>.
    #
    # In neato and fdp, pos can be used to set the initial position of a node.
    # By default, the coordinates are assumed to be in inches. However, the
    # <html:a href="http://www.graphviz.org/doc/info/command.html#d:s">-s</html:a> command line flag can be used to specify
    # different units.
    #
    # When the <html:a href="http://www.graphviz.org/doc/info/command.html#d:n">-n</html:a> command line flag is used with
    # neato, it is assumed the positions have been set by one of the layout
    # programs, and are therefore in points. Thus, <html:code>neato -n</html:code> can accept
    # input correctly without requiring a <html:code>-s</html:code> flag and, in fact,
    # ignores any such flag.
    pos = List(Tuple(Float, Float), desc="spline control points")

    # Edges with the same head and the same <html:a rel="attr">samehead</html:a> value are aimed
    # at the same point on the head.
    # See <html:a rel="note">undirected</html:a>.
    samehead = Str("",
                   desc="dges with the same head and the same samehead "
                   "value are aimed at the same point on the head",
                   graphviz=True)

    # Edges with the same tail and the same <html:a rel="attr">sametail</html:a> value are aimed
    # at the same point on the tail.
    # See <html:a rel="note">undirected</html:a>.
    sametail = Str("",
                   desc="edges with the same tail and the same sametail "
                   "value are aimed at the same point on the tail",
                   graphviz=True)

    # Print guide boxes in PostScript at the beginning of
    # routesplines if 1, or at the end if 2. (Debugging)
    showboxes = showboxes_trait

    # Set style for node or edge. For cluster subgraph, if "filled", the
    # cluster box's background is filled.
    style = ListStr(desc="style for node or edge", graphviz=True)

    # If <html:span class="val">true</html:span>, the tail of an edge is clipped to the boundary of the tail node;
    # otherwise, the end of the edge goes to the center of the node, or the
    # center of a port, if applicable.
    tailclip = Bool(True,
                    desc="tail of an edge to be clipped to the boundary "
                    "of the tail node",
                    graphviz=True)

    # Synonym for <html:a rel="attr">tailURL</html:a>.
    tailhref = Alias("tailURL", desc="synonym for tailURL", graphviz=True)

    # Text label to be placed near tail of edge.
    # See <html:a rel="note">undirected</html:a>.
    taillabel = Str(desc="text label to be placed near tail of edge",
                    graphviz=True)

    # Indicates where on the tail node to attach the tail of the edge.
    tailport = port_pos_trait

    # If the edge has a tailURL, this attribute determines which window of the
    # browser is used for the URL. Setting it to "_graphviz" will open a new
    # window if it doesn't already exist, or reuse it if it does. If undefined,
    # the value of the target is used.
    tailtarget = Str(desc="which window of the browser is used for the URL",
                     graphviz=True)

    # Tooltip annotation attached to the tail of an edge. This is used only
    # if the edge has a <html:a rel="attr">tailURL</html:a> attribute.
    tailtooltip = Str("",
                      desc="tooltip annotation attached to the tail of an "
                      "edge",
                      label="Tail tooltip",
                      graphviz=True)

    # If <html:a rel="attr">tailURL</html:a> is defined, it is
    # output as part of the tail label of the edge.
    # Also, this value is used near the tail node, overriding any
    # <html:a rel="attr">URL</html:a> value.
    # See <html:a rel="note">undirected</html:a>.
    tailURL = Str("",
                  desc="output as part of the tail label of the edge",
                  label="Tail URL",
                  graphviz=True)

    # If the object has a URL, this attribute determines which window
    # of the browser is used for the URL.
    # See <html:a href="http://www.w3.org/TR/html401/present/frames.html#adef-target">W3C documentation</html:a>.
    target = target_trait

    # Tooltip annotation attached to the node or edge. If unset, Graphviz
    # will use the object's <html:a rel="attr">label</html:a> if defined.
    # Note that if the label is a record specification or an HTML-like
    # label, the resulting tooltip may be unhelpful. In this case, if
    # tooltips will be generated, the user should set a <html:tt>tooltip</html:tt>
    # attribute explicitly.
    tooltip = tooltip_trait

    # Hyperlinks incorporated into device-dependent output.
    # At present, used in ps2, cmap, i*map and svg formats.
    # For all these formats, URLs can be attached to nodes, edges and
    # clusters. URL attributes can also be attached to the root graph in ps2,
    # cmap and i*map formats. This serves as the base URL for relative URLs in the
    # former, and as the default image map file in the latter.
    #
    # For svg, cmapx and imap output, the active area for a node is its
    # visible image.
    # For example, an unfilled node with no drawn boundary will only be active on its label.
    # For other output, the active area is its bounding box.
    # The active area for a cluster is its bounding box.
    # For edges, the active areas are small circles where the edge contacts its head
    # and tail nodes. In addition, for svg, cmapx and imap, the active area
    # includes a thin polygon approximating the edge. The circles may
    # overlap the related node, and the edge URL dominates.
    # If the edge has a label, this will also be active.
    # Finally, if the edge has a head or tail label, this will also be active.
    #
    # Note that, for edges, the attributes <html:a rel="attr">headURL</html:a>,
    # <html:a rel="attr">tailURL</html:a>, <html:a rel="attr">labelURL</html:a> and
    # <html:a rel="attr">edgeURL</html:a> allow control of various parts of an
    # edge. Also note that, if active areas of two edges overlap, it is unspecified
    # which area dominates.
    URL = url_trait

    # Weight of edge. In dot, the heavier the weight, the shorter, straighter
    # and more vertical the edge is.
    weight = Float(1.0, desc="weight of edge", graphviz=True)

    #--------------------------------------------------------------------------
    #  Views:
    #--------------------------------------------------------------------------

    traits_view = View(VGroup(
        Group(
            Item(name="vp",
                 editor=ComponentEditor(height=100),
                 show_label=False,
                 id=".component"), Item("arrange", show_label=False)),
        Tabbed(
            Group(Item(name="tail_node",
                       editor=InstanceEditor(name="_nodes", editable=False)),
                  Item(name="head_node",
                       editor=InstanceEditor(name="_nodes", editable=False)), [
                           "style", "layer", "color", "colorscheme", "dir",
                           "arrowsize", "constraint", "decorate", "showboxes",
                           "tooltip", "edgetooltip", "edgetarget", "target",
                           "comment"
                       ],
                  label="Edge"),
            Group([
                "label", "fontname", "fontsize", "fontcolor", "nojustify",
                "labeltarget", "labelfloat", "labelfontsize", "labeltooltip",
                "labelangle", "lp", "labelURL", "labelfontname",
                "labeldistance", "labelfontcolor", "labelhref"
            ],
                  label="Label"),
            Group(["minlen", "weight", "len", "pos"], label="Dimension"),
            Group([
                "arrowhead", "samehead", "headURL", "headtooltip", "headclip",
                "headport", "headlabel", "headtarget", "lhead", "headhref"
            ],
                  label="Head"),
            Group([
                "arrowtail", "tailtarget", "tailhref", "ltail", "sametail",
                "tailport", "taillabel", "tailtooltip", "tailURL", "tailclip"
            ],
                  label="Tail"),
            Group(["URL", "href", "edgeURL", "edgehref"], label="URL"),
            Group([
                "_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_",
                "_tldraw_"
            ],
                  label="Xdot"),
            dock="tab"),
        layout="split",
        id=".splitter"),
                       title="Edge",
                       id="godot.edge",
                       buttons=["OK", "Cancel", "Help"],
                       resizable=True)

    #--------------------------------------------------------------------------
    #  "object" interface:
    #--------------------------------------------------------------------------

    def __init__(self,
                 tailnode_or_ID,
                 headnode_or_ID,
                 directed=False,
                 **traits):
        """ Initialises a new Edge instance.
        """
        if not isinstance(tailnode_or_ID, Node):
            tailnodeID = str(tailnode_or_ID)
            tail_node = Node(tailnodeID)
        else:
            tail_node = tailnode_or_ID

        if not isinstance(headnode_or_ID, Node):
            headnodeID = str(headnode_or_ID)
            head_node = Node(headnodeID)
        else:
            head_node = headnode_or_ID

        self.tail_node = tail_node
        self.head_node = head_node

        if directed:
            self.conn = "->"
        else:
            self.conn = "--"

        super(Edge, self).__init__(**traits)

    def __str__(self):
        """ Returns a string representation of the edge.
        """
        attrs = []
        # Traits to be included in string output have 'graphviz' metadata.
        for trait_name, trait in self.traits(graphviz=True).iteritems():
            # Get the value of the trait for comparison with the default.
            value = getattr(self, trait_name)

            # Only print attribute value pairs if not defaulted.
            # FIXME: Alias/Synced traits default to None.
            if (value != trait.default) and (trait.default is not None):
                # Add quotes to the value if necessary.
                if isinstance(value, basestring):
                    valstr = '"%s"' % value
                else:
                    valstr = str(value)

                attrs.append('%s=%s' % (trait_name, valstr))

        if attrs:
            attrstr = " [%s]" % ", ".join(attrs)
        else:
            attrstr = ""

        edge_str = "%s%s %s %s%s%s;" % (self.tail_node.ID, self.tailport,
                                        self.conn, self.head_node.ID,
                                        self.headport, attrstr)
        return edge_str

    #--------------------------------------------------------------------------
    #  Trait initialisers:
    #--------------------------------------------------------------------------

    def _component_default(self):
        """ Trait initialiser.
        """
        component = Container(auto_size=True, bgcolor="green")
        #        component.tools.append( MoveTool(component) )
        #        component.tools.append( TraitsTool(component) )
        return component

    def _vp_default(self):
        """ Trait initialiser.
        """
        vp = Viewport(component=self.component)
        vp.enable_zoom = True
        vp.tools.append(ViewportPanTool(vp))
        return vp

    #--------------------------------------------------------------------------
    #  Property getters:
    #--------------------------------------------------------------------------

    def _get_name(self):
        """ Property getter.
        """
        if (self.tail_node is not None) and (self.head_node is not None):
            return "%s %s %s" % (self.tail_node.ID, self.conn,
                                 self.head_node.ID)
        else:
            return "Edge"

    #--------------------------------------------------------------------------
    #  Event handlers:
    #--------------------------------------------------------------------------

    @on_trait_change("arrange")
    def arrange_all(self):
        """ Arrange the components of the node using Graphviz.
        """
        # FIXME: Circular reference avoidance.
        import godot.dot_data_parser
        import godot.graph

        graph = godot.graph.Graph(ID="g", directed=True)
        self.conn = "->"
        graph.edges.append(self)

        xdot_data = graph.create(format="xdot")
        #        print "XDOT DATA:", xdot_data

        parser = godot.dot_data_parser.GodotDataParser()
        ndata = xdot_data.replace('\\\n', '')
        tokens = parser.dotparser.parseString(ndata)[0]

        for element in tokens[3]:
            cmd = element[0]
            if cmd == "add_edge":
                cmd, src, dest, opts = element
                self.set(**opts)

#    @on_trait_change("_draw_,_hdraw_")

    def _parse_xdot_directive(self, name, new):
        """ Handles parsing Xdot drawing directives.
        """
        parser = XdotAttrParser()
        components = parser.parse_xdot_data(new)

        # The absolute coordinate of the drawing container wrt graph origin.
        x1 = min([c.x for c in components])
        y1 = min([c.y for c in components])

        print "X1/Y1:", name, x1, y1

        # Components are positioned relative to their container. This
        # function positions the bottom-left corner of the components at
        # their origin rather than relative to the graph.
        #        move_to_origin( components )

        for c in components:
            if isinstance(c, Ellipse):
                component.x_origin -= x1
                component.y_origin -= y1
#                c.position = [ c.x - x1, c.y - y1 ]

            elif isinstance(c, (Polygon, BSpline)):
                print "Points:", c.points
                c.points = [(t[0] - x1, t[1] - y1) for t in c.points]
                print "Points:", c.points

            elif isinstance(c, Text):
                #                font = str_to_font( str(c.pen.font) )
                c.text_x, c.text_y = c.x - x1, c.y - y1

        container = Container(auto_size=True,
                              position=[x1, y1],
                              bgcolor="yellow")

        container.add(*components)

        if name == "_draw_":
            self.drawing = container
        elif name == "_hdraw_":
            self.arrowhead_drawing = container
        else:
            raise

    @on_trait_change("drawing,arrowhead_drawing")
    def _on_drawing(self, object, name, old, new):
        """ Handles the containers of drawing components being set.
        """
        attrs = ["drawing", "arrowhead_drawing"]

        others = [getattr(self, a) for a in attrs \
            if (a != name) and (getattr(self, a) is not None)]

        x, y = self.component.position
        print "POS:", x, y, self.component.position

        abs_x = [d.x + x for d in others]
        abs_y = [d.y + y for d in others]

        print "ABS:", abs_x, abs_y

        # Assume that he new drawing is positioned relative to graph origin.
        x1 = min(abs_x + [new.x])
        y1 = min(abs_y + [new.y])

        print "DRAW:", new.position
        new.position = [new.x - x1, new.y - y1]
        print "DRAW:", new.position

        #        for i, b in enumerate( others ):
        #            self.drawing.position = [100, 100]
        #            self.drawing.request_redraw()
        #            print "OTHER:", b.position, abs_x[i] - x1
        #            b.position = [ abs_x[i] - x1, abs_y[i] - y1 ]
        #            b.x = 50
        #            b.y = 50
        #            print "OTHER:", b.position, abs_x[i], x1

        #        for attr in attrs:
        #            if attr != name:
        #                if getattr(self, attr) is not None:
        #                    drawing = getattr(self, attr)
        #                    drawing.position = [50, 50]

        if old is not None:
            self.component.remove(old)
        if new is not None:
            self.component.add(new)

        print "POS NEW:", self.component.position
        self.component.position = [x1, y1]
        print "POS NEW:", self.component.position
        self.component.request_redraw()
        print "POS NEW:", self.component.position
Пример #11
0
class Cluster(BaseGraph):
    """ Defines a representation of a subgraph in Graphviz's dot language.
    """

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

#    ID = Str("cluster", desc="that clusters are encoded as subgraphs whose "
#        "names have the prefix 'cluster'")
#
#    name = Alias("ID", desc="synonym for ID")
#
#    # Nodes in the cluster.
#    nodes = List(Instance(Node))
#
#    # Edges in the cluster.
#    edges = List(Instance(Edge))
#
#    # Subgraphs of the cluster.
#    subgraphs = List(Instance("godot.subgraph.Subgraph"))
#
#    # Separate rectangular layout regions.
#    clusters = List(Instance("godot.cluster.Cluster"))

    # Parent graph in the graph heirarchy.
#    parent = Instance("godot.graph:Graph")

    # Root graph instance.
#    root = Instance("godot.graph:Graph")

    #--------------------------------------------------------------------------
    #  Xdot trait definitions:
    #--------------------------------------------------------------------------

    # For a given graph object, one will typically a draw directive before the
    # label directive. For example, for a node, one would first use the
    # commands in _draw_ followed by the commands in _ldraw_.
#    _draw_ = Str(desc="xdot drawing directive")
#
#    # Label draw directive.
#    _ldraw_ = Str(desc="xdot label drawing directive")

    #--------------------------------------------------------------------------
    #  Graphviz dot language trait definitions:
    #--------------------------------------------------------------------------

    # When attached to the root graph, this color is used as the background for
    # entire canvas. When a cluster attribute, it is used as the initial
    # background for the cluster. If a cluster has a filled
    # <html:a rel="attr">style</html:a>, the
    # cluster's <html:a rel="attr">fillcolor</html:a> will overlay the
    # background color.
    #
    # If no background color is specified for the root graph, no graphics
    # operation are performed on the background. This works fine for
    # PostScript but for bitmap output, all bits are initialized to something.
    # This means that when the bitmap output is included in some other
    # document, all of the bits within the bitmap's bounding box will be
    # set, overwriting whatever color or graphics where already on the page.
    # If this effect is not desired, and you only want to set bits explicitly
    # assigned in drawing the graph, set <html:a rel="attr">bgcolor</html:a>="transparent".
    bgcolor = Color("white", desc="color used as the background for the "
        "entire canvas", label="Background Color", graphviz=True)

    # Basic drawing color for graphics, not text. For the latter, use the
    # <html:a rel="attr">fontcolor</html:a> attribute.
    #
    # For edges, the value can either be a single
    # <html:a rel="type">color</html:a> or a
    # <html:a rel="type">colorList</html:a>.
    # In the latter case, the edge is drawn using parallel splines or lines,
    # one for each color in the list, in the order given.
    # The head arrow, if any, is drawn using the first color in the list,
    # and the tail arrow, if any, the second color. This supports the common
    # case of drawing opposing edges, but using parallel splines instead of
    # separately routed multiedges.
    color = color_trait

    # This attribute specifies a color scheme namespace. If defined, it
    # specifies the context for interpreting color names. In particular, if a
    # <html:a rel="type">color</html:a> value has form <html:code>xxx</html:code> or <html:code>//xxx</html:code>,
    # then the color <html:code>xxx</html:code> will be evaluated according to the current color scheme.
    # If no color scheme is set, the standard X11 naming is used.
    # For example, if <html:code>colorscheme=bugn9</html:code>, then <html:code>color=7</html:code>
    # is interpreted as <html:code>/bugn9/7</html:code>.
    colorscheme = color_scheme_trait

    # Color used to fill the background of a node or cluster
    # assuming <html:a rel="attr">style</html:a>=filled.
    # If <html:a rel="attr">fillcolor</html:a> is not defined, <html:a rel="attr">color</html:a> is
    # used. (For clusters, if <html:a rel="attr">color</html:a> is not defined,
    # <html:a rel="attr">bgcolor</html:a> is used.) If this is not defined,
    # the default is used, except for
    # <html:a rel="attr">shape</html:a>=point or when the output
    # format is MIF,
    # which use black by default.
    #
    # Note that a cluster inherits the root graph's attributes if defined.
    # Thus, if the root graph has defined a <html:a rel="attr">fillcolor</html:a>, this will override a
    # <html:a rel="attr">color</html:a> or <html:a rel="attr">bgcolor</html:a> attribute set for the cluster.
    fillcolor = Color("grey", desc="fill color for background of a node",
        graphviz=True)

    # If true, the node size is specified by the values of the
    # <html:a rel="attr">width</html:a>
    # and <html:a rel="attr">height</html:a> attributes only
    # and is not expanded to contain the text label.
    fixedsize = Bool(False, desc="node size to be specified by 'width' and "
        "'height'", label="Fixed size", graphviz=True)

    # Color used for text.
    fontcolor = fontcolor_trait

    # Font used for text. This very much depends on the output format and, for
    # non-bitmap output such as PostScript or SVG, the availability of the font
    # when the graph is displayed or printed. As such, it is best to rely on
    # font faces that are generally available, such as Times-Roman, Helvetica or
    # Courier.
    #
    # If Graphviz was built using the
    # <html:a href="http://pdx.freedesktop.org/~fontconfig/fontconfig-user.html">fontconfig library</html:a>, the latter library
    # will be used to search for the font. However, if the <html:a rel="attr">fontname</html:a> string
    # contains a slash character "/", it is treated as a pathname for the font
    # file, though font lookup will append the usual font suffixes.
    #
    # If Graphviz does not use fontconfig, <html:a rel="attr">fontname</html:a> will be
    # considered the name of a Type 1 or True Type font file.
    # If you specify <html:code>fontname=schlbk</html:code>, the tool will look for a
    # file named  <html:code>schlbk.ttf</html:code> or <html:code>schlbk.pfa</html:code> or <html:code>schlbk.pfb</html:code>
    # in one of the directories specified by
    # the <html:a rel="attr">fontpath</html:a> attribute.
    # The lookup does support various aliases for the common fonts.
    fontname = fontname_trait

    # Font size, in <html:a rel="note">points</html:a>, used for text.
    fontsize = fontsize_trait

    # Spring constant used in virtual physical model. It roughly corresponds to
    # an ideal edge length (in inches), in that increasing K tends to increase
    # the distance between nodes. Note that the edge attribute len can be used
    # to override this value for adjacent nodes.
    K = Float(0.3, desc="spring constant used in virtual physical model",
        graphviz=True)

    # Text label attached to objects.
    # If a node's <html:a rel="attr">shape</html:a> is record, then the label can
    # have a <html:a href="http://www.graphviz.org/doc/info/shapes.html#record">special format</html:a>
    # which describes the record layout.
    label = label_trait

    # Justification for cluster labels. If <html:span class="val">r</html:span>, the label
    # is right-justified within bounding rectangle; if <html:span class="val">l</html:span>, left-justified;
    # else the label is centered.
    # Note that a subgraph inherits attributes from its parent. Thus, if
    # the root graph sets <html:a rel="attr">labeljust</html:a> to <html:span class="val">l</html:span>, the subgraph inherits
    # this value.
    labeljust = Trait("c", {"Centre": "c", "Right": "r", "Left": "l"},
        desc="justification for cluster labels", label="Label justification",
        graphviz=True)

    # Top/bottom placement of graph and cluster labels.
    # If the attribute is <html:span class="val">t</html:span>, place label at the top;
    # if the attribute is <html:span class="val">b</html:span>, place label at the bottom.
    # By default, root
    # graph labels go on the bottom and cluster labels go on the top.
    # Note that a subgraph inherits attributes from its parent. Thus, if
    # the root graph sets <html:a rel="attr">labelloc</html:a> to <html:span class="val">b</html:span>, the subgraph inherits
    # this value.
    labelloc = Trait("b", {"Bottom": "b", "Top":"t"},
        desc="placement of graph and cluster labels",
        label="Label location", graphviz=True)

    # Label position, in points. The position indicates the center of the
    # label.
    lp = Str#point_trait

    # By default, the justification of multi-line labels is done within the
    # largest context that makes sense. Thus, in the label of a polygonal node,
    # a left-justified line will align with the left side of the node (shifted
    # by the prescribed margin). In record nodes, left-justified line will line
    # up with the left side of the enclosing column of fields. If nojustify is
    # "true", multi-line labels will be justified in the context of itself. For
    # example, if the attribute is set, the first label line is long, and the
    # second is shorter and left-justified, the second will align with the
    # left-most character in the first line, regardless of how large the node
    # might be.
    nojustify = nojustify_trait


    # Color used to draw the bounding box around a cluster. If
    # <html:a rel="attr">pencolor</html:a> is not defined,
    # <html:a rel="attr">color</html:a> is used. If this is not defined,
    # <html:a rel="attr">bgcolor</html:a> is used. If this is not defined, the
    # default is used.
    # Note that a cluster inherits the root graph's attributes if defined.
	# Thus, if the root graph has defined a
    # <html:a rel="attr">pencolor</html:a>, this will override a
	# <html:a rel="attr">color</html:a> or <html:a rel="attr">bgcolor</html:a>
    # attribute set for the cluster.
    pencolor = Color("grey", desc="color for the cluster bounding box",
        graphviz=True)

    # Set style for node or edge. For cluster subgraph, if "filled", the
    # cluster box's background is filled.
#    style = ListStr(desc="style for node")
    style = Str(desc="style for node", graphviz=True)

    # If the object has a URL, this attribute determines which window
    # of the browser is used for the URL.
    # See <html:a href="http://www.w3.org/TR/html401/present/frames.html#adef-target">W3C documentation</html:a>.
    target = target_trait

    # Tooltip annotation attached to the node or edge. If unset, Graphviz
    # will use the object's <html:a rel="attr">label</html:a> if defined.
    # Note that if the label is a record specification or an HTML-like
    # label, the resulting tooltip may be unhelpful. In this case, if
    # tooltips will be generated, the user should set a <html:tt>tooltip</html:tt>
    # attribute explicitly.
    tooltip = tooltip_trait

    # Hyperlinks incorporated into device-dependent output.
    # At present, used in ps2, cmap, i*map and svg formats.
    # For all these formats, URLs can be attached to nodes, edges and
    # clusters. URL attributes can also be attached to the root graph in ps2,
    # cmap and i*map formats. This serves as the base URL for relative URLs in the
    # former, and as the default image map file in the latter.
    #
    # For svg, cmapx and imap output, the active area for a node is its
    # visible image.
    # For example, an unfilled node with no drawn boundary will only be active on its label.
    # For other output, the active area is its bounding box.
    # The active area for a cluster is its bounding box.
    # For edges, the active areas are small circles where the edge contacts its head
    # and tail nodes. In addition, for svg, cmapx and imap, the active area
    # includes a thin polygon approximating the edge. The circles may
    # overlap the related node, and the edge URL dominates.
    # If the edge has a label, this will also be active.
    # Finally, if the edge has a head or tail label, this will also be active.
    #
    # Note that, for edges, the attributes <html:a rel="attr">headURL</html:a>,
    # <html:a rel="attr">tailURL</html:a>, <html:a rel="attr">labelURL</html:a> and
    # <html:a rel="attr">edgeURL</html:a> allow control of various parts of an
    # edge. Also note that, if active areas of two edges overlap, it is unspecified
    # which area dominates.
    URL = url_trait

    #--------------------------------------------------------------------------
    #  Views:
    #--------------------------------------------------------------------------

    traits_view = View(
        Tabbed(
            VGroup(
                Item("ID"),
                Tabbed(nodes_item, edges_item, dock="tab"),
                label="Cluster"
            ),
            Group(["label", "fontname", "fontsize", "nojustify", "labeljust",
                   "labelloc", "lp"],
                label="Label"
            ),
            Group(["bgcolor", "color", "colorscheme", "fillcolor", "fontcolor",
                   "pencolor"],
                label="Color"),
            Group(["fixedsize", "K", "style", "tooltip", "target", "URL"],
                Group(["_draw_", "_ldraw_"], label="Xdot", show_border=True),
                label="Misc"
            )
        ),
        title="Cluster", id="godot.cluster", buttons=["OK", "Cancel", "Help"],
        resizable=True
    )

    #--------------------------------------------------------------------------
    #  "object" interface:
    #--------------------------------------------------------------------------

    def __str__(self):
        """ Returns a string representation of the cluster in dot language.
        """
        s = "subgraph"

        return "%s %s" % ( s, super(Cluster, self).__str__() )

    #--------------------------------------------------------------------------
    #  Trait initialisers:
    #--------------------------------------------------------------------------

    def _labelloc_default(self):
        """ Trait initialiser.
        """
        return "Top"

    #--------------------------------------------------------------------------
    #  Event handlers :
    #--------------------------------------------------------------------------

    def _ID_changed(self, new):
        """ Handles the ID changing by ensuring that it has a 'cluster' prefix.
        """
        if new[:7].lower() != "cluster":
            self.ID = "cluster_%s" % new
Пример #12
0
class TableViewer(ContentViewer):
    """ A viewer for tabular data. """


    # The content provider provides the actual table data.
    content_provider = Instance(TableContentProvider)

    # The label provider provides, err, the labels for the items in the table
    # (a label can have text and/or an image).
    label_provider = Instance(TableLabelProvider, factory = TableLabelProvider)

    # The column provider provides information about the columns in the table
    # (column headers, width etc).
    column_provider=Trait(TableColumnProvider(),Instance(TableColumnProvider))

    # The colours used to render odd and even numbered rows.
    even_row_background = Color("white")
    odd_row_background  = Color((245, 245, 255))

    # A row has been selected.
    row_selected = Event

    # A row has been activated.
    row_activated = Event

    # A drag operation was started on a node.
    row_begin_drag = Event


    def __init__(self, parent, image_size=(16, 16), **traits):
        """ Creates a new table viewer.

	'parent' is the toolkit-specific control that is the table's parent.

        'image_size' is a tuple in the form (int width, int height) that
        specifies the size of the images (if any) displayed in the table.

        """

        # Base-class constructor.
        super(TableViewer, self).__init__(**traits)

        # Create the toolkit-specific control.
        self.control = table = _Table(parent, image_size, self)

        # Get our actual id.
        wxid = table.GetId()
        
        # Table events.
        wx.EVT_LIST_ITEM_SELECTED(table, wxid, self._on_item_selected)
        wx.EVT_LIST_ITEM_ACTIVATED(table, wxid, self._on_item_activated)
        wx.EVT_LIST_BEGIN_DRAG(table, wxid, self._on_list_begin_drag)
        wx.EVT_LIST_BEGIN_RDRAG(table, wxid, self._on_list_begin_rdrag)

        wx.EVT_LIST_BEGIN_LABEL_EDIT(
            table, wxid, self._on_list_begin_label_edit
        )

        wx.EVT_LIST_END_LABEL_EDIT(
            table, wxid, self._on_list_end_label_edit
        )

        # fixme: Bug[732104] indicates that this event does not get fired
        # in a virtual list control (it *does* get fired in a regular list
        # control 8^().
        wx.EVT_LIST_ITEM_DESELECTED(table, wxid, self._on_item_deselected)
        
        # Create the widget!
        self._create_widget(parent)

        # We use a dynamic handler instead of a static handler here, as we
        # don't want to react if the input is set in the constructor.
        self.on_trait_change(self._on_input_changed, 'input')
        
        return

    ###########################################################################
    # 'TableViewer' interface.
    ###########################################################################

    def select_row(self, row):
        """ Select the specified row. """

        self.control.SetItemState(
            row, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED
        )

        self.control.SetItemState(
            row, wx.LIST_STATE_FOCUSED, wx.LIST_STATE_FOCUSED
        )

        # Make sure that the selected row is visible.
        fudge = max(0, row - 5)
        self.EnsureVisible(fudge)

        # Trait event notification.
        self.row_selected = row
        
        return

    ###########################################################################
    # Trait event handlers.
    ###########################################################################

    def _on_input_changed(self, obj, trait_name, old, new):
        """ Called when the input is changed. """

        # Update the table contents.
        self._update_contents()

        if old is None:
            self._update_column_widths()

        return
    
    ###########################################################################
    # wx event handlers.
    ###########################################################################

    def _on_item_selected(self, event):
        """ Called when an item in the list is selected. """

        # Get the index of the row that was selected (nice wx interface huh?!).
        row = event.m_itemIndex
        
        # Trait event notification.
        self.row_selected = row
        
        return

    # fixme: Bug[732104] indicates that this event does not get fired in a
    # virtual list control (it *does* get fired in a regular list control 8^().
    def _on_item_deselected(self, event):
        """ Called when an item in the list is selected. """

        # Trait event notification.
        self.row_selected = -1
        
        return

    def _on_item_activated(self, event):
        """ Called when an item in the list is activated. """

        # Get the index of the row that was activated (nice wx interface!).
        row = event.m_itemIndex
        
        # Trait event notification.
        self.row_activated = row

        return

    def _on_list_begin_drag(self, event=None, is_rdrag=False):
        """ Called when a drag operation is starting on a list item. """

        # Trait notification.
        self.row_begin_drag = event.GetIndex()
            
        return

    def _on_list_begin_rdrag(self, event=None):
        """ Called when a drag operation is starting on a list item. """

        self._on_list_begin_drag(event, True)

        return

    def _on_list_begin_label_edit(self, event=None):
        """ Called when a label edit is started. """

        event.Veto()

        return

    def _on_list_end_label_edit(self, event=None):
        """ Called when a label edit is completed. """

        return
    
    ###########################################################################
    # Private interface.
    ###########################################################################

    FORMAT_MAP = {
        'left'   : wx.LIST_FORMAT_LEFT,
        'right'  : wx.LIST_FORMAT_RIGHT,
        'center' : wx.LIST_FORMAT_CENTRE,
        'centre' : wx.LIST_FORMAT_CENTRE
    }
        
    def _create_widget(self, parent):
        """ Creates the widget. """
        
        # Set up a default list item descriptor.
        info = wx.ListItem()
        info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_FORMAT

        # Set the column headers.
        for index in range(self.column_provider.column_count):
            # Header text.
            info.m_text = self.column_provider.get_label(self, index)

            # Alignment of header text AND ALL cells in the column.
            alignment = self.column_provider.get_alignment(self, index)
            info.m_format = self.FORMAT_MAP.get(alignment, wx.LIST_FORMAT_LEFT)
            
            self.control.InsertColumnInfo(index, info)

        # Update the table contents and the column widths.
        self._update_contents()
        self._update_column_widths()
                
        return

    def _update_contents(self):
        """ Updates the table content. """

        self._elements = []
        if self.input is not None:
            # Filtering...
            for element in  self.content_provider.get_elements(self.input):
                for filter in self.filters:
                    if not filter.select(self, self.input, element):
                        break

                else:
                    self._elements.append(element)

            # Sorting...
            if self.sorter is not None:
                self.sorter.sort(self, self.input, self._elements)

        # Setting this causes a refresh!
        self.control.SetItemCount(len(self._elements))

        return

    def _update_column_widths(self):
        """ Updates the column widths. """

        # Set all columns to be the size of their largest item, or the size of
        # their header whichever is the larger.
        for column in range(self.control.GetColumnCount()):
            width = self.column_provider.get_width(self, column)
            if width == -1:
                width = self._get_column_width(column)

            self.control.SetColumnWidth(column, width)

        return
    
    def _get_column_width(self, column):
        """ Return an appropriate width for the specified column. """

        self.control.SetColumnWidth(column, wx.LIST_AUTOSIZE_USEHEADER)
        header_width = self.control.GetColumnWidth(column)
        
        if self.control.GetItemCount() == 0:
            width = header_width

        else:
            self.control.SetColumnWidth(column, wx.LIST_AUTOSIZE)
            data_width = self.control.GetColumnWidth(column)

            width = max(header_width, data_width)
            
        return width