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
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
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
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
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()
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)
#) 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,
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())
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)))
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
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
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