class Model(HasTraits): #Traits view definitions: traits_view = View(Group( Item('function'), HGroup(Item('npts_x', label="Number X Points"), Item('npts_y', label="Number Y Points")), HGroup(Item('min_x', label="Min X value"), Item('max_x', label="Max X value")), HGroup(Item('min_y', label="Min Y value"), Item('max_y', label="Max Y value"))), buttons=["OK", "Cancel"]) function = Str("tanh(x**2+y)*cos(y)*jn(0,x+y*2)") npts_x = CInt(400) npts_y = CInt(200) min_x = CFloat(-2 * pi) max_x = CFloat(2 * pi) min_y = CFloat(-1.5 * pi) max_y = CFloat(1.5 * pi) xs = Array ys = Array zs = Array minz = Float maxz = Float model_changed = Event def __init__(self, *args, **kwargs): super(Model, self).__init__(*args, **kwargs) self.compute_model() def compute_model(self): # The xs and ys used for the image plot range need to be the # edges of the cells. self.xs = linspace(self.min_x, self.max_x, self.npts_x + 1) self.ys = linspace(self.min_y, self.max_y, self.npts_y + 1) # The grid of points at which we will evaluate the 2D function # is located at cell centers, so use halfsteps from the # min/max values (which are edges) xstep = (self.max_x - self.min_x) / self.npts_x #ystep = (self.max_y - self.min_y) / self.npts_y gridx = linspace(self.min_x + xstep / 2, self.max_x - xstep / 2, self.npts_x) gridy = linspace(self.min_y + xstep / 2, self.max_y - xstep / 2, self.npts_y) x, y = meshgrid(gridx, gridy) try: d = dict(x=x, y=y) exec("from scipy import *", d) exec("from scipy.special import *", d) self.zs = eval(self.function, d) self.minz = nanmin(self.zs) self.maxz = nanmax(self.zs) self.model_changed = True self._function = self.function except: self.set(function=self._function, trait_change_notify=False) def _anytrait_changed(self, name, value): if name in [ 'function', 'npts_x', 'npts_y', 'min_x', 'max_x', 'min_y', 'max_y' ]: self.compute_model()
class ProfileResults(HasTraits): """ Display profiling results. """ # The sorted list of Records that mirrors this dictionary. records = List() selected_record = Any() dclicked = Event() column_clicked = Event() # The total time in seconds for the set of records. total_time = Float(1.0) # The column name to sort on. sort_key = Str('inline_time') sort_ascending = Bool(False) adapter = Instance(ProfileAdapter) basenames = Bool(True) percentages = Bool(True) def trait_view(self, name=None, view_element=None): if name or view_element is not None: return super(ProfileResults, self).trait_view(name=name, view_element=view_element) view = tui.View( tui.Group(tui.Item('total_time', style='readonly'), ), tui.Item('records', editor=get_profile_editor(self.adapter), show_label=False), width=1024, height=768, resizable=True, ) return view def sorter(self, record): """ Return the appropriate sort key for sorting the records. """ return getattr(record, self.sort_key) def sort_records(self, records): """ Resort the records according to the current settings. """ records = sorted(records, key=self.sorter) if not self.sort_ascending: records = records[::-1] return records def _adapter_default(self): return ProfileAdapter(basenames=self.basenames, percentages=self.percentages, total_time=self.total_time) @on_trait_change('total_time,percentages,basenames') def _adapter_traits_changed(self, object, name, old, new): setattr(self.adapter, name, new) @on_trait_change('sort_key,sort_ascending') def _resort(self): self.records = self.sort_records(self.records) def _column_clicked_changed(self, new): if new is None: return if isinstance(new.column, int): key = profile_columns[new.column][1] else: key = new.column if key == self.sort_key: # Just flip the order. self.sort_ascending = not self.sort_ascending else: self.trait_set(sort_ascending=False, sort_key=key)
class RuleTableFilter(TableFilter): """ A table filter based on rules. """ #------------------------------------------------------------------------- # Trait definitions: #------------------------------------------------------------------------- # Overrides the default **name** trait name = 'Default rule-based filter' # List of the filter rules to be applied rules = List(GenericTableFilterRule) # Event fired when the contents of the filter have changed modified = Event # Persistence ID of the view view_id = Str('traitsui.table_filter.RuleTableFilter') # Sample object that the filter will apply to _object = Any # Map of trait names and default values _trait_values = Any #------------------------------------------------------------------------- # Traits view definitions: #------------------------------------------------------------------------- error_view = View( Item(label='A menu or rule based filter can only be created for ' 'tables with at least one entry'), title='Error Creating Filter', kind='livemodal', close_result=False, buttons=['Cancel']) #------------------------------------------------------------------------- # Returns whether a specified object meets the filter/search criteria: # (Should normally be overridden) #------------------------------------------------------------------------- def filter(self, object): """ Returns whether a specified object meets the filter or search criteria. """ is_first = is_true = True for rule in self.rules: if rule.and_or == 'or': if is_true and (not is_first): return True is_true = True if is_true: is_true = rule.is_true(object) is_first = False return is_true #------------------------------------------------------------------------- # Returns a user readable description of what kind of object will # satisfy the filter: # (Should normally be overridden): #------------------------------------------------------------------------- def description(self): """ Returns a user-readable description of the kind of object that satisfies the filter. """ ors = [] ands = [] if len(self.rules) > 0: for rule in self.rules: if rule.and_or == 'or': if len(ands) > 0: ors.append(' and '.join(ands)) ands = [] ands.append(rule.description()) if len(ands) > 0: ors.append(' and '.join(ands)) if len(ors) == 1: return ors[0] if len(ors) > 1: return ' or '.join(['(%s)' % t for t in ors]) return super(RuleTableFilter, self).description() #------------------------------------------------------------------------- # Edits the contents of the filter: #------------------------------------------------------------------------- def edit_view(self, object): """ Return a view to use for editing the filter. The ''object'' parameter is a sample object for the table that the filter will be applied to. It is supplied in case the filter needs to extract data or metadata from the object. If the table is empty, the ''object'' argument is None. """ self._object = object if object is None: return self.edit_traits(view='error_view') names = object.editable_traits() self._trait_values = object.get(names) return View([['name{Filter name}', '_'], [ Item('rules', id='rules_table', editor=self._get_table_editor(names)), '|<>' ]], id=self.view_id, title='Edit Filter', kind='livemodal', resizable=True, buttons=['OK', 'Cancel'], width=0.4, height=0.3) #------------------------------------------------------------------------- # Returns a table editor to use for editing the filter: #------------------------------------------------------------------------- def _get_table_editor(self, names): """ Returns a table editor to use for editing the filter. """ from .api import TableEditor return TableEditor(columns=generic_table_filter_rule_columns, orientation='vertical', deletable=True, sortable=False, configurable=False, auto_size=False, auto_add=True, row_factory=GenericTableFilterRule, row_factory_kw={ 'filter': self, 'name_editor': EnumEditor(values=names) }) #------------------------------------------------------------------------- # Returns the state to be pickled (override of object): #------------------------------------------------------------------------- def __getstate__(self): """ Returns the state to be pickled. This definition overrides **object**. """ dict = self.__dict__.copy() if '_object' in dict: del dict['_object'] del dict['_trait_values'] return dict #------------------------------------------------------------------------- # Handles the 'rules' trait being changed: #------------------------------------------------------------------------- def _rules_changed(self, rules): """ Handles a change to the **rules** trait. """ for rule in rules: rule.filter = self
class Company(HasTraits): name = Str('<unknown>') departments = List(Department) employees = List(Employee)
class IC(BaseModule): """IC""" TestFactors = ListStr(arg_type="MultiOption", label="测试因子", order=0, option_range=()) FactorOrder = Dict(key_trait=Str(), value_trait=Enum("降序", "升序"), arg_type="ArgDict", label="排序方向", order=1) #PriceFactor = Enum(None, arg_type="SingleOption", label="价格因子", order=2) #IndustryFactor = Enum("无", arg_type="SingleOption", label="行业因子", order=3) #WeightFactor = Enum("等权", arg_type="SingleOption", label="权重因子", order=4) CalcDTs = List(dt.datetime, arg_type="DateList", label="计算时点", order=5) LookBack = Int(1, arg_type="Integer", label="回溯期数", order=6) CorrMethod = Enum("spearman", "pearson", "kendall", arg_type="SingleOption", label="相关性算法", order=7) IDFilter = Str(arg_type="IDFilter", label="筛选条件", order=8) RollAvgPeriod = Int(12, arg_type="Integer", label="滚动平均期数", order=9) def __init__(self, factor_table, name="IC", sys_args={}, **kwargs): self._FactorTable = factor_table super().__init__(name=name, sys_args=sys_args, **kwargs) def __QS_initArgs__(self): DefaultNumFactorList, DefaultStrFactorList = getFactorList(dict(self._FactorTable.getFactorMetaData(key="DataType"))) self.add_trait("TestFactors", ListStr(arg_type="MultiOption", label="测试因子", order=0, option_range=tuple(DefaultNumFactorList))) self.TestFactors.append(DefaultNumFactorList[0]) self.add_trait("PriceFactor", Enum(*DefaultNumFactorList, arg_type="SingleOption", label="价格因子", order=2)) self.PriceFactor = searchNameInStrList(DefaultNumFactorList, ['价','Price','price']) self.add_trait("IndustryFactor", Enum(*(["无"]+DefaultStrFactorList), arg_type="SingleOption", label="行业因子", order=3)) self.add_trait("WeightFactor", Enum(*(["等权"]+DefaultNumFactorList), arg_type="SingleOption", label="权重因子", order=4)) @on_trait_change("TestFactors[]") def _on_TestFactors_changed(self, obj, name, old, new): self.FactorOrder = {iFactorName:self.FactorOrder.get(iFactorName, "降序") for iFactorName in self.TestFactors} def getViewItems(self, context_name=""): Items, Context = super().getViewItems(context_name=context_name) Items[0].editor = SetEditor(values=self.trait("TestFactors").option_range) return (Items, Context) def __QS_start__(self, mdl, dts, **kwargs): if self._isStarted: return () super().__QS_start__(mdl=mdl, dts=dts, **kwargs) self._Output = {} self._Output["IC"] = {iFactorName:[] for iFactorName in self.TestFactors} self._Output["股票数"] = {iFactorName:[] for iFactorName in self.TestFactors} self._Output["时点"] = [] self._CurCalcInd = 0 return (self._FactorTable, ) def __QS_move__(self, idt, **kwargs): if self._iDT==idt: return 0 self._iDT = idt if self.CalcDTs: if idt not in self.CalcDTs[self._CurCalcInd:]: return 0 self._CurCalcInd = self.CalcDTs[self._CurCalcInd:].index(idt) + self._CurCalcInd PreInd = self._CurCalcInd - self.LookBack LastInd = self._CurCalcInd - 1 PreDateTime = self.CalcDTs[PreInd] LastDateTime = self.CalcDTs[LastInd] else: self._CurCalcInd = self._Model.DateTimeIndex PreInd = self._CurCalcInd - self.LookBack LastInd = self._CurCalcInd - 1 PreDateTime = self._Model.DateTimeSeries[PreInd] LastDateTime = self._Model.DateTimeSeries[LastInd] if (PreInd<0) or (LastInd<0): for iFactorName in self.TestFactors: self._Output["IC"][iFactorName].append(np.nan) self._Output["股票数"][iFactorName].append(np.nan) self._Output["时点"].append(idt) return 0 PreIDs = self._FactorTable.getFilteredID(idt=PreDateTime, id_filter_str=self.IDFilter) FactorExpose = self._FactorTable.readData(dts=[PreDateTime], ids=PreIDs, factor_names=list(self.TestFactors)).iloc[:, 0, :] Price = self._FactorTable.readData(dts=[LastDateTime, idt], ids=PreIDs, factor_names=[self.PriceFactor]).iloc[0, :, :] Ret = Price.iloc[-1] / Price.iloc[0] - 1 if self.IndustryFactor!="无":# 进行收益率的行业调整 IndustryData = self._FactorTable.readData(dts=[LastDateTime], ids=PreIDs, factor_names=[self.IndustryFactor]).iloc[0, 0, :] AllIndustry = IndustryData.unique() if self.WeightFactor=="等权": for iIndustry in AllIndustry: iMask = (IndustryData==iIndustry) Ret[iMask] -= Ret[iMask].mean() else: WeightData = self._FactorTable.readData(dts=[LastDateTime], ids=PreIDs, factor_names=[self.WeightFactor]).iloc[0, 0, :] for iIndustry in AllIndustry: iMask = (IndustryData==iIndustry) iWeight = WeightData[iMask] iRet = Ret[iMask] Ret[iMask] -= (iRet*iWeight).sum() / iWeight[pd.notnull(iWeight) & pd.notnull(iRet)].sum(skipna=False) for iFactorName in self.TestFactors: self._Output["IC"][iFactorName].append(FactorExpose[iFactorName].corr(Ret, method=self.CorrMethod)) self._Output["股票数"][iFactorName].append(pd.notnull(FactorExpose[iFactorName]).sum()) self._Output["时点"].append(idt) return 0 def __QS_end__(self): if not self._isStarted: return 0 super().__QS_end__() CalcDateTimes = self._Output.pop("时点") self._Output["股票数"] = pd.DataFrame(self._Output["股票数"], index=CalcDateTimes) self._Output["IC"] = pd.DataFrame(self._Output["IC"], index=CalcDateTimes) for i, iFactorName in enumerate(self.TestFactors): if self.FactorOrder[iFactorName]=="升序": self._Output["IC"][iFactorName] = -self._Output["IC"][iFactorName] self._Output["IC的移动平均"] = self._Output["IC"].copy() for i in range(len(CalcDateTimes)): if i<self.RollAvgPeriod-1: self._Output["IC的移动平均"].iloc[i,:] = np.nan else: self._Output["IC的移动平均"].iloc[i,:] = self._Output["IC"].iloc[i-self.RollAvgPeriod+1:i+1, :].mean() self._Output["统计数据"] = pd.DataFrame(index=self._Output["IC"].columns) self._Output["统计数据"]["平均值"] = self._Output["IC"].mean() self._Output["统计数据"]["标准差"] = self._Output["IC"].std() self._Output["统计数据"]["最小值"] = self._Output["IC"].min() self._Output["统计数据"]["最大值"] = self._Output["IC"].max() self._Output["统计数据"]["IC_IR"] = self._Output["统计数据"]["平均值"] / self._Output["统计数据"]["标准差"] self._Output["统计数据"]["t统计量"] = np.nan self._Output["统计数据"]["平均股票数"] = self._Output["股票数"].mean() self._Output["统计数据"]["IC×Sqrt(N)"] = self._Output["统计数据"]["平均值"]*np.sqrt(self._Output["统计数据"]["平均股票数"]) self._Output["统计数据"]["有效期数"] = 0.0 for iFactor in self._Output["IC"]: self._Output["统计数据"].loc[iFactor,"有效期数"] = pd.notnull(self._Output["IC"][iFactor]).sum() self._Output["统计数据"]["t统计量"] = (self._Output["统计数据"]["有效期数"]**0.5)*self._Output["统计数据"]["IC_IR"] return 0 def genMatplotlibFig(self, file_path=None): nRow, nCol = self._Output["IC"].shape[1]//3+(self._Output["IC"].shape[1]%3!=0), min(3, self._Output["IC"].shape[1]) Fig = plt.figure(figsize=(min(32, 16+(nCol-1)*8), 8*nRow)) AxesGrid = gridspec.GridSpec(nRow, nCol) xData = np.arange(0, self._Output["IC"].shape[0]) xTicks = np.arange(0, self._Output["IC"].shape[0], max(1, int(self._Output["IC"].shape[0]/10))) xTickLabels = [self._Output["IC"].index[i].strftime("%Y-%m-%d") for i in xTicks] yMajorFormatter = FuncFormatter(_QS_formatMatplotlibPercentage) for i in range(self._Output["IC"].shape[1]): iAxes = plt.subplot(AxesGrid[i//nCol, i%nCol]) iAxes.yaxis.set_major_formatter(yMajorFormatter) iAxes.plot(xData, self._Output["IC的移动平均"].iloc[:, i].values, label="IC的移动平均", color="r", alpha=0.6, lw=3) iAxes.bar(xData, self._Output["IC"].iloc[:, i].values, label="IC", color="b") iAxes.set_xticks(xTicks) iAxes.set_xticklabels(xTickLabels) iAxes.legend(loc='best') iAxes.set_title(self._Output["IC"].columns[i]) if file_path is not None: Fig.savefig(file_path, dpi=150, bbox_inches='tight') return Fig def _repr_html_(self): if len(self.ArgNames)>0: HTML = "参数设置: " HTML += '<ul align="left">' for iArgName in self.ArgNames: if iArgName!="计算时点": HTML += "<li>"+iArgName+": "+str(self.Args[iArgName])+"</li>" elif self.Args[iArgName]: HTML += "<li>"+iArgName+": 自定义时点</li>" else: HTML += "<li>"+iArgName+": 所有时点</li>" HTML += "</ul>" else: HTML = "" Formatters = [_QS_formatPandasPercentage]*4+[lambda x:'{0:.4f}'.format(x)]+[lambda x:'{0:.2f}'.format(x)]*3+[lambda x:'{0:.0f}'.format(x)] iHTML = self._Output["统计数据"].to_html(formatters=Formatters) Pos = iHTML.find(">") HTML += iHTML[:Pos]+' align="center"'+iHTML[Pos:] Fig = self.genMatplotlibFig() # figure 保存为二进制文件 Buffer = BytesIO() plt.savefig(Buffer, bbox_inches='tight') PlotData = Buffer.getvalue() # 图像数据转化为 HTML 格式 ImgStr = "data:image/png;base64,"+base64.b64encode(PlotData).decode() HTML += ('<img src="%s">' % ImgStr) return HTML
class Person(HasTraits): name = Str() age = Int() person_view = View('name', Include('extra'), 'age')
class LUTManager(Base): # The version of this class. Used for persistence. __version__ = 0 # The lookup table. lut = Instance(tvtk.LookupTable, (), record=False) # The scalar bar. scalar_bar = Instance(tvtk.ScalarBarActor, (), record=True) # The scalar_bar_widget scalar_bar_widget = Instance(tvtk.ScalarBarWidget, ()) # The representation associated with the scalar_bar_widget. This # only exists in VTK versions about around 5.2. scalar_bar_representation = Instance(tvtk.Object, allow_none=True, record=True) # The title text property of the axes. title_text_property = Property(record=True) # The label text property of the axes. label_text_property = Property(record=True) # The current mode of the LUT. lut_mode = Enum('blue-red', lut_mode_list(), desc='the type of the lookup table') # File name of the LUT file to use. file_name = Str('', editor=FileEditor, desc='the filename containing the LUT') # Reverse the colors of the LUT. reverse_lut = Bool(False, desc='if the lut is to be reversed') # Turn on/off the visibility of the scalar bar. show_scalar_bar = Bool(False, desc='if scalar bar is shown or not') # This is an alias for show_scalar_bar. show_legend = Property(Bool, desc='if legend is shown or not') # The number of labels to use for the scalar bar. number_of_labels = Range(0, 64, 8, enter_set=True, auto_set=False, desc='the number of labels to display') # Number of colors for the LUT. number_of_colors = Range(2, 2147483647, 256, enter_set=True, auto_set=False, desc='the number of colors for the LUT') # Enable shadowing of the labels and text. shadow = Bool(False, desc='if the labels and text have shadows') # Use the default data name or the user specified one. use_default_name = Bool(True, desc='if the default data name is to be used') # The default data name -- set by the module manager. default_data_name = Str('data', enter_set=True, auto_set=False, desc='the default data name') # The optionally user specified name of the data. data_name = Str('', enter_set=True, auto_set=False, desc='the title of the legend') # Use the default range or user specified one. use_default_range = Bool(True, desc='if the default data range is to be used') # The default data range -- this is computed and set by the # module manager. default_data_range = Array(shape=(2, ), value=[0.0, 1.0], dtype=float, enter_set=True, auto_set=False, desc='the default range of the data mapped') # The optionally user defined range of the data. data_range = Array(shape=(2, ), value=[0.0, 1.0], dtype=float, enter_set=True, auto_set=False, desc='the range of the data mapped') # Create a new LUT. create_lut = Button('Launch LUT editor', desc='if we launch a Lookup table editor in' ' a separate process') ######################################## ## Private traits. # The original range of the data. _orig_data_range = Array(shape=(2, ), value=[0.0, 1.0], dtype=float) _title_text_property = Instance(tvtk.TextProperty) _label_text_property = Instance(tvtk.TextProperty) ###################################################################### # `object` interface ###################################################################### def __init__(self, **traits): super(LUTManager, self).__init__(**traits) # Initialize the scalar bar. sc_bar = self.scalar_bar sc_bar.set(lookup_table=self.lut, title=self.data_name, number_of_labels=self.number_of_labels, orientation='horizontal', width=0.8, height=0.17) pc = sc_bar.position_coordinate pc.set(coordinate_system='normalized_viewport', value=(0.1, 0.01, 0.0)) self._shadow_changed(self.shadow) # Initialize the lut. self._lut_mode_changed(self.lut_mode) # Set the private traits. ttp = self._title_text_property = sc_bar.title_text_property ltp = self._label_text_property = sc_bar.label_text_property # Call render when the text properties are changed. ttp.on_trait_change(self.render) ltp.on_trait_change(self.render) # Initialize the scalar_bar_widget self.scalar_bar_widget.set(scalar_bar_actor=self.scalar_bar, key_press_activation=False) self._number_of_colors_changed(self.number_of_colors) ###################################################################### # `Base` interface ###################################################################### def start(self): """This is invoked when this object is added to the mayavi pipeline. """ # Do nothing if we are already running. if self.running: return # Show the legend if necessary. self._show_scalar_bar_changed(self.show_scalar_bar) # Call parent method to set the running state. super(LUTManager, self).start() def stop(self): """Invoked when this object is removed from the mayavi pipeline. """ if not self.running: return # Hide the scalar bar. sbw = self.scalar_bar_widget if sbw.interactor is not None: sbw.off() # Call parent method to set the running state. super(LUTManager, self).stop() ###################################################################### # Non-public interface ###################################################################### def _lut_mode_changed(self, value): if value == 'file': if self.file_name: self.load_lut_from_file(self.file_name) #self.lut.force_build() return reverse = self.reverse_lut if value in pylab_luts: lut = pylab_luts[value] if reverse: lut = lut[::-1, :] n_total = len(lut) n_color = self.number_of_colors if not n_color >= n_total: lut = lut[::round(n_total / float(n_color))] self.load_lut_from_list(lut.tolist()) #self.lut.force_build() return elif value == 'blue-red': if reverse: hue_range = 0.0, 0.6667 saturation_range = 1.0, 1.0 value_range = 1.0, 1.0 else: hue_range = 0.6667, 0.0 saturation_range = 1.0, 1.0 value_range = 1.0, 1.0 elif value == 'black-white': if reverse: hue_range = 0.0, 0.0 saturation_range = 0.0, 0.0 value_range = 1.0, 0.0 else: hue_range = 0.0, 0.0 saturation_range = 0.0, 0.0 value_range = 0.0, 1.0 lut = self.lut lut.set(hue_range=hue_range, saturation_range=saturation_range, value_range=value_range, number_of_table_values=self.number_of_colors, ramp='sqrt') lut.modified() lut.force_build() self.render() def _scene_changed(self, value): sbw = self.scalar_bar_widget if value is None: return if sbw.interactor is not None: sbw.off() value.add_widgets(sbw, enabled=False) if self.show_scalar_bar: sbw.on() self._foreground_changed_for_scene(None, value.foreground) def _foreground_changed_for_scene(self, old, new): # Change the default color for the text. self.title_text_property.color = new self.label_text_property.color = new self.render() def _number_of_colors_changed(self, value): if self.lut_mode == 'file': return elif self.lut_mode in pylab_luts: # We can't interpolate these LUTs, as they are defined from a # table. We hack around this limitation reverse = self.reverse_lut lut = pylab_luts[self.lut_mode] if reverse: lut = lut[::-1, :] n_total = len(lut) if value > n_total: return lut = lut[::round(n_total / float(value))] self.load_lut_from_list(lut.tolist()) else: lut = self.lut lut.number_of_table_values = value lut.modified() lut.build() self.render() # necessary to flush. sc_bar = self.scalar_bar sc_bar.maximum_number_of_colors = value sc_bar.modified() self.render() def _number_of_labels_changed(self, value): sc_bar = self.scalar_bar sc_bar.number_of_labels = value sc_bar.modified() self.render() def _file_name_changed(self, value): if self.lut_mode == 'file': self.load_lut_from_file(value) else: # This will automagically load the LUT from the file. self.lut_mode = 'file' def _reverse_lut_changed(self, value): # This will do the needful. self._lut_mode_changed(self.lut_mode) def _show_scalar_bar_changed(self, value): if self.scene is not None: self.scalar_bar_widget.enabled = value self.render() def _get_show_legend(self): return self.show_scalar_bar def _set_show_legend(self, value): old = self.show_scalar_bar if value != old: self.show_scalar_bar = value self.trait_property_changed('show_legend', old, value) def _shadow_changed(self, value): sc_bar = self.scalar_bar sc_bar.title_text_property.shadow = self.shadow sc_bar.label_text_property.shadow = self.shadow self.render() def _use_default_name_changed(self, value): self._default_data_name_changed(self.default_data_name) def _data_name_changed(self, value): sc_bar = self.scalar_bar sc_bar.title = value sc_bar.modified() self.render() def _default_data_name_changed(self, value): if self.use_default_name: self.data_name = value def _use_default_range_changed(self, value): self._default_data_range_changed(self.default_data_range) def _data_range_changed(self, value): try: self.lut.set_range(value[0], value[1]) except TypeError: self.lut.set_range((value[0], value[1])) except AttributeError: self.lut.range = value self.scalar_bar.modified() self.render() def _default_data_range_changed(self, value): if self.use_default_range: self.data_range = value def _visible_changed(self, value): state = self.show_scalar_bar and value self._show_scalar_bar_changed(state) super(LUTManager, self)._visible_changed(value) def load_lut_from_file(self, file_name): lut_list = [] if len(file_name) > 0: try: f = open(file_name, 'r') except IOError: msg = "Cannot open Lookup Table file: %s\n" % file_name error(msg) else: f.close() try: lut_list = parse_lut_file(file_name) except IOError, err_msg: msg = "Sorry could not parse LUT file: %s\n" % file_name msg += err_msg error(msg) else: if self.reverse_lut: lut_list.reverse() self.lut = set_lut(self.lut, lut_list) self.render()
class EditedInstance(HasTraits): value = Str() traits_view = View(Item("value"), buttons=["OK"])
class TVTKClassChooser(HasTraits): # The selected object, is None if no valid class_name was made. object = Property # The TVTK class name to choose. class_name = Str('', desc='class name of TVTK class (case sensitive)') # The string to search for in the class docs -- the search supports # 'and' and 'or' keywords. search = Str('', desc='string to search in TVTK class documentation '\ 'supports the "and" and "or" keywords. '\ 'press <Enter> to start search. '\ 'This is case insensitive.') clear_search = Button # The class documentation. doc = Str(_search_help_doc) # Completions for the choice of class. completions = List(Str) # List of available class names as strings. available = List(TVTK_CLASSES) ######################################## # Private traits. finder = Instance(DocSearch) n_completion = Int(25) ######################################## # View related traits. view = View(Group( Item(name='class_name', editor=EnumEditor(name='available')), Item(name='class_name', has_focus=True), Item(name='search', editor=TextEditor(enter_set=True, auto_set=False)), Item(name='clear_search', show_label=False), Item('_'), Item(name='completions', editor=ListEditor(columns=3), style='readonly'), Item(name='doc', resizable=True, label='Documentation', style='custom')), id='tvtk_doc', resizable=True, width=800, height=600, title='TVTK class chooser', buttons=["OK", "Cancel"]) ###################################################################### # `object` interface. ###################################################################### def __init__(self, **traits): super(TVTKClassChooser, self).__init__(**traits) self._orig_available = list(self.available) ###################################################################### # Non-public interface. ###################################################################### def _get_object(self): o = None if len(self.class_name) > 0: try: o = getattr(tvtk, self.class_name)() except (AttributeError, TypeError): pass return o def _class_name_changed(self, value): av = self.available comp = [x for x in av if x.startswith(value)] self.completions = comp[:self.n_completion] if len(comp) == 1 and value != comp[0]: self.class_name = comp[0] o = self.object if o is not None: self.doc = get_tvtk_class_doc(o) else: self.doc = _search_help_doc def _finder_default(self): return DocSearch() def _clear_search_fired(self): self.search = '' def _search_changed(self, value): if len(value) < 3: self.available = self._orig_available return f = self.finder result = f.search(str(value)) if len(result) == 0: self.available = self._orig_available elif len(result) == 1: self.class_name = result[0] else: self.available = result self.completions = result[:self.n_completion]
class BCDofList(BMCSTreeNode, Vis2D): ''' Implements the IBC functionality for a constrained dof. ''' tree_node_list = List tree_node_list = Property(depends_on='bcdof_list,bcdof_list_items') @cached_property def _get_tree_node_list(self): return self.bcdof_list name = Str('<unnamed>') node_name = Property def _get_node_name(self): s = '%s:%s=%s' % (self.var, self.slice, self.value) return s var = Enum('u', 'f', 'eps', 'sig') def is_essential(self): return self.var == 'u' def is_linked(self): return self.link_dofs != [] def is_constrained(self): ''' Return true if a DOF is either explicitly prescribed or it depends on other DOFS. ''' return self.is_essential() or self.is_linked() def is_natural(self): return self.var == 'f' or self.var == 'eps' or self.var == 'sig' bcdof_list = List(BCDof) def reset(self): self.bcdof_list = [] integ_domain = Enum(['global', 'local']) def setup(self, sctx): ''' Locate the spatial context.f ''' def apply_essential(self, K): for bcond in self.bcdof_list: bcond.apply_essential(K) def apply(self, step_flag, sctx, K, R, t_n, t_n1): if self.is_essential(): for bcond in self.bcdof_list: bcond.apply(step_flag, sctx, K, R, t_n, t_n1) else: self.apply_natural(step_flag, sctx, K, R, t_n, t_n1) def apply_natural(self, step_flag, sctx, K, R, t_n, t_n1): raise NotImplementedError traits_view = View( VGroup( VSplit( Item('bcdof_list', style='custom', editor=bcond_list_editor, show_label=False), ), )) tree_view = traits_view
class UserPerspectiveManager(HasTraits): """ Manages a set of user perspectives. """ # 'UserPerspective' interface -----------------------------------------# # A directory on the local file system that we can read and write to at # will. This is used to persist window layout information, etc. state_location = Str() # Next available user perspective id. next_id = Property(Int) # Dictionary mapping perspective id to user defined perspective definition. id_to_perspective = Property(Dict) # The list of user defined perspective definitions. perspectives = Property(List) # The name of the user defined perspectives definition file. file_name = Property(Str) # Private interface ---------------------------------------------------- # Shadow trait for the 'id_to_perspective' property. _id_to_perspective = Any() # ------------------------------------------------------------------------ # 'UserPerspective' interface. # ------------------------------------------------------------------------ # Properties ----------------------------------------------------------- def _get_next_id(self): """ Property getter. """ # Get all of the current perspective ids: ids = list(self.id_to_perspective.keys()) # If there are none: if len(ids) == 0: # Return the starting id: return 1 # Else return the current highest id + 1 as the next id: ids.sort() return int(ids[-1][19:-2]) + 1 def _get_id_to_perspective(self): """ Property getter. """ if self._id_to_perspective is None: self._id_to_perspective = dic = {} try: fh = open(self.file_name, "r") for line in fh: data = line.split(":", 1) if len(data) == 2: id, name = data[0].strip(), data[1].strip() dic[id] = Perspective( id=id, name=name, show_editor_area=False ) fh.close() except: pass return self._id_to_perspective def _get_perspectives(self): """ Property getter. """ return list(self.id_to_perspective.values()) def _get_file_name(self): """ Property getter. """ return os.path.join(self.state_location, "__user_perspective__") # Methods -------------------------------------------------------------# def create_perspective(self, name, show_editor_area=True): """ Create a new (and empty) user-defined perspective. """ perspective = Perspective( id="__user_perspective_%09d__" % self.next_id, name=name, show_editor_area=show_editor_area, ) # Add the perspective to the map. self.id_to_perspective[perspective.id] = perspective # Update the persistent file information. self._update_persistent_data() return perspective def clone_perspective(self, window, perspective, **traits): """ Clone a perspective as a user perspective. """ clone = perspective.clone_traits() # Give the clone a special user perspective Id! clone.id = "__user_perspective_%09d__" % self.next_id # Set any traits specified as keyword arguments. clone.trait_set(**traits) # Add the perspective to the map. self.id_to_perspective[clone.id] = clone # fixme: This needs to be pushed into the window API!!!!!!! window._memento.perspective_mementos[clone.id] = ( window.layout.get_view_memento(), window.active_view and window.active_view.id or None, window.layout.is_editor_area_visible(), ) # Update the persistent file information. self._update_persistent_data() return clone def save(self): """ Persist the current state of the user perspectives. """ self._update_persistent_data() def add(self, perspective, name=None): """ Add a perspective with an optional name. """ # Define the id for the new perspective: perspective.id = id = "__user_perspective_%09d__" % self.next_id # Save the new name (if specified): if name is not None: perspective.name = name # Create the perspective: self.id_to_perspective[id] = perspective # Update the persistent file information: self._update_persistent_data() # Return the new perspective created: return perspective def rename(self, perspective, name): """ Rename the user perspective with the specified id. """ perspective.name = name self.id_to_perspective[perspective.id].name = name # Update the persistent file information: self._update_persistent_data() def remove(self, id): """ Remove the user perspective with the specified id. This method also updates the persistent data. """ if id in self.id_to_perspective: del self.id_to_perspective[id] # Update the persistent file information: self._update_persistent_data() # Try to delete the associated perspective layout file: try: os.remove(os.path.join(self.state_location, id)) except: pass return # ------------------------------------------------------------------------ # Private interface. # ------------------------------------------------------------------------ def _update_persistent_data(self): """ Update the persistent file information. """ try: fh = open(self.file_name, "w") fh.write( "\n".join( ["%s: %s" % (p.id, p.name) for p in self.perspectives] ) ) fh.close() except: logger.error( "Could not write the user defined perspective " "definition file: " + self.file_name ) return
class ExpressionContext(ListenableMixin, PersistableMixin, DictMixin): """Provide a context wrapper that adds the ability to request expressions on variables in the underlying context, and re-evaluate those expressions and fire events when the underlying dependencies of the variables in the expression changes""" name = Str('ExpressionContext') # The underlying context which contains variables # From which the expressions are calculated. underlying_context = AdaptsTo(IListenableContext) # The currently evaluated expressions, mapped to their last cached value. None means # that the value is not cached. _expressions = Dict(Str, Any) # A list of variable dependencies for each expression _dependencies = Dict(Str, List(Str)) def __init__(self, underlying_context, **traits): super(ExpressionContext, self).__init__(underlying_context=underlying_context) def __delitem__(self, key): """Delete an item from the ExpressionContext -- either in the underlying context, or if an already cached expression, delete it.""" # if item is an expression, delete it from the list of dependencies, otherwise pass it down # to underlying if key in self._expressions: self._expressions.remove(key) for dep in list(Block(key).inputs): self._dependencies[dep].remove(key) else: del self.underlying_context[key] def __contains__(self, key): # FIXME: not sure what to do here -- maybe eval the expression in the context # and see if it works as an expression on this context if it's not in the underly # -ing return key in self.underlying_context def __getitem__(self, key): if key in self.underlying_context.keys(): return self.underlying_context[key] else: try: # FIXME imports need to be more configurable # FIXME we may want to have cache rules on sizes so that we # don't keep around huge values that aren't needed anymore # however, there is a time/space tradeoff that really should be # used. We currently support del so that is available if the user # wants to manually manage this. eval_globals = {} for lib in (__builtins__, __import__('numpy')): for sym in dir(lib): if not key.startswith('__'): eval_globals[sym] = getattr(lib, sym) result = eval(key, eval_globals, self.underlying_context) self._expressions[key] = result for dep in Block(key).inputs: self._dependencies.setdefault(dep, list()) self._dependencies[dep].append(key) return result except: return None def __setitem__(self, key, value): """We don't allow setting an expression per se, so it passes through to the underlying context. Hopefully this won't be a problem in the future due to plots or other listeners trying to change values. Theoretically one could store the inverse of a function and allow this if the function was invertable""" self.underlying_context[key] = value return get = DictMixin.get def __str__(self): underlying_str = str(self.underlying_context) return '%s(%s)' % (type(self).__name__, underlying_str) @on_trait_change('underlying_context:items_modified') def _underlying_context_items_modifed(self, event): new_event = ItemsModified(context=self, added=[x for x in event.added], removed=[x for x in event.removed], modified=[x for x in event.modified]) for event_list in (new_event.added, new_event.modified, new_event.removed): for item in event_list: if item in self._dependencies.keys(): for dep_item in self._dependencies[item]: if dep_item not in event_list: event_list.append(dep_item) # Remove the cached value for the item self._expressions[item] = None self.items_modified = new_event return def _underlying_context_changed(self): self._expressions = {} self._dependencies = {} return
class LogViewer(Controller): search_entry = Str(enter_set=True, auto_set=False) refresh_needed = Event use_fuzzy = Bool(True) use_filter = Bool(True) levels = List(('DEBUG', 'INFO', 'WARNING')) available_levels = List(('DEBUG', 'INFO', 'WARNING')) def init(self, info): info.ui.title = 'Log Viewer - {}'.format(os.path.basename(self.model.path)) return True # def controller_levels_changed(self, info): # if self.levels: # items = filter(lambda x: x.level in self.levels, self.model.oitems) # regex = self._make_search_regex() # self._set_found(regex, items) def controller_search_entry_changed(self, info): regex = self._make_search_regex() if regex: self._set_found(regex, self.model.oitems) else: for i in self.model.items: i.found = False self.refresh_needed = True def _set_found(self, regex, items): if not regex: self.model.items = items else: if self.use_filter: self.model.items = filter(lambda x: regex.search(x.message), items) else: for i in items: if regex.search(i.message): i.found = True else: i.found = False self.model.items = items def _make_search_regex(self): v = self.search_entry if v: if self.use_fuzzy: pat = '.*?'.join(map(re.escape, v)) else: pat = '^{}'.format(v) regex = re.compile(pat) return regex def traits_view(self): ctrlgrp = VGroup(HGroup(UItem('controller.search_entry'), Item('controller.use_fuzzy'), Item('controller.use_filter')), HGroup(UItem('controller.levels', style='custom', editor=CheckListEditor(name='controller.available_levels', cols=3)))) v = View(VGroup(ctrlgrp, UItem('items', editor=TabularEditor(adapter=LogAdapter(), refresh='controller.refresh_needed', operations=[]))), title='Log Viewer', resizable=True, width=800, height=600) return v
class ListStrAdapter(HasPrivateTraits): """ The base class for adapting list items to values that can be edited by a ListStrEditor. """ # Trait Definitions ------------------------------------------------------ #: Specifies the default value for a new list item. default_value = Any("") #: Specifies the default text for a new list item. default_text = Str #: The default text color for even list items. even_text_color = Color(None, update=True) #: The default text color for odd list items. odd_text_color = Color(None, update=True) #: The default text color for list items. text_color = Color(None, update=True) #: The default background color for even list items. even_bg_color = Color(None, update=True) #: The default background color for odd list items. odd_bg_color = Color(None, update=True) #: The default background color for list items. bg_color = Color(None, update=True) #: The name of the default image to use for list items. image = Str(None, update=True) #: Can the text value of each list item be edited. can_edit = Bool(True) #: Specifies where a dropped item should be placed in the list relative to #: the item it is dropped on. dropped = Enum("after", "before") #: The index of the current item being adapter. index = Int #: The current item being adapted. item = Any #: The current value (if any). value = Any #: List of optional delegated adapters. adapters = List(IListStrAdapter, update=True) # -- Private Trait Definitions -------------------------------------------- #: Cache of attribute handlers. cache = Any({}) #: Event fired when the cache is flushed. cache_flushed = Event(update=True) # -- Adapter methods that are sensitive to item type ---------------------- def get_can_edit(self, object, trait, index): """ Returns whether the user can edit a specified *object.trait[index]* list item. A True result indicates the value can be edited, while a False result indicates that it cannot be edited. """ return self._result_for("get_can_edit", object, trait, index) def get_drag(self, object, trait, index): """ Returns the 'drag' value for a specified *object.trait[index]* list item. A result of *None* means that the item cannot be dragged. """ return self._result_for("get_drag", object, trait, index) def get_can_drop(self, object, trait, index, value): """ Returns whether the specified *value* can be dropped on the specified *object.trait[index]* list item. A value of **True** means the *value* can be dropped; and a value of **False** indicates that it cannot be dropped. """ return self._result_for("get_can_drop", object, trait, index, value) def get_dropped(self, object, trait, index, value): """ Returns how to handle a specified *value* being dropped on a specified *object.trait[index]* list item. The possible return values are: 'before' Insert the specified *value* before the dropped on item. 'after' Insert the specified *value* after the dropped on item. """ return self._result_for("get_dropped", object, trait, index, value) def get_text_color(self, object, trait, index): """ Returns the text color for a specified *object.trait[index]* list item. A result of None means use the default list item text color. """ return self._result_for("get_text_color", object, trait, index) def get_bg_color(self, object, trait, index): """ Returns the background color for a specified *object.trait[index]* list item. A result of None means use the default list item background color. """ return self._result_for("get_bg_color", object, trait, index) def get_image(self, object, trait, index): """ Returns the name of the image to use for a specified *object.trait[index]* list item. A result of None means no image should be used. Otherwise, the result should either be the name of the image, or an ImageResource item specifying the image to use. """ return self._result_for("get_image", object, trait, index) def get_item(self, object, trait, index): """ Returns the value of the *object.trait[index]* list item. """ return self._result_for("get_item", object, trait, index) def get_text(self, object, trait, index): """ Returns the text to display for a specified *object.trait[index]* list item. """ return self._result_for("get_text", object, trait, index) # -- Adapter methods that are not sensitive to item type ------------------ def len(self, object, trait): """ Returns the number of items in the specified *object.trait* list. """ # Sometimes, during shutdown, the object has been set to None. if object is None: return 0 else: return len(getattr(object, trait)) def get_default_value(self, object, trait): """ Returns a new default value for the specified *object.trait* list. """ return self.default_value def get_default_text(self, object, trait): """ Returns the default text for the specified *object.trait* list. """ return self.default_text def get_default_image(self, object, trait): """ Returns the default image for the specified *object.trait* list. """ return self.image def get_default_bg_color(self, object, trait): """ Returns the default background color for the specified *object.trait* list. """ return self._get_bg_color() def get_default_text_color(self, object, trait): """ Returns the default text color for the specified *object.trait* list. """ return self._get_text_color() def set_text(self, object, trait, index, text): """ Sets the text for a specified *object.trait[index]* list item to *text*. """ getattr(object, trait)[index] = text def delete(self, object, trait, index): """ Deletes the specified *object.trait[index]* list item. """ del getattr(object, trait)[index] def insert(self, object, trait, index, value): """ Inserts a new value at the specified *object.trait[index]* list index. """ getattr(object, trait)[index:index] = [value] # -- Private Adapter Implementation Methods ------------------------------- def _get_can_edit(self): return self.can_edit def _get_drag(self): return six.text_type(self.item) def _get_can_drop(self): return isinstance(self.value, six.string_types) def _get_dropped(self): return self.dropped def _get_text_color(self): if (self.index % 2) == 0: return self.even_text_color_ or self.text_color_ return self.odd_text_color or self.text_color_ def _get_bg_color(self): if (self.index % 2) == 0: return self.even_bg_color_ or self.bg_color_ return self.odd_bg_color or self.bg_color_ def _get_image(self): return self.image def _get_item(self): return self.item def _get_text(self): return six.text_type(self.item) # -- Private Methods ------------------------------------------------------ def _result_for(self, name, object, trait, index, value=None): """ Returns/Sets the value of the specified *name* attribute for the specified *object.trait[index]* list item. """ self.index = index self.value = value items = getattr(object, trait) if index >= len(items): self.item = item = None else: self.item = item = items[index] item_class = item.__class__ key = "%s:%s" % (item_class.__name__, name) handler = self.cache.get(key) if handler is not None: return handler() trait_name = name[4:] for adapter in self.adapters: adapter.index = index adapter.item = item adapter.value = value if adapter.accepts and (adapter.trait(trait_name) is not None): handler = lambda: getattr( adapter.trait_set( index=self.index, item=self.item, value=self.value), trait_name, ) if adapter.is_cacheable: break return handler() else: for klass in item_class.__mro__: cname = "%s_%s" % (klass.__name__, trait_name) if self.trait(cname) is not None: handler = lambda: getattr(self, cname) break else: handler = getattr(self, "_" + name) self.cache[key] = handler return handler() @on_trait_change("adapters.+update") def _flush_cache(self): """ Flushes the cache when any trait on any adapter changes. """ self.cache = {} self.cache_flushed = True
class TabularAdapter(HasPrivateTraits): """ The base class for adapting list items to values that can be edited by a TabularEditor. """ #-- Public Trait Definitions --------------------------------------------- # A list of columns that should appear in the table. Each entry can have one # of two forms: string or ( string, any ), where *string* is the UI name of # the column, and *any* is a value that identifies that column to the # adapter. Normally this value is either a trait name or an index, but it # can be any value that the adapter wants. If only *string* is specified, # then *any* is the index of the *string* within *columns*. columns = List() # Maps UI name of column to value identifying column to the adapter, if # different. column_dict = Property() # Specifies the default value for a new row: default_value = Any('') # The default text color for table rows (even, odd, any rows): odd_text_color = Color(None, update=True) even_text_color = Color(None, update=True) default_text_color = Color(None, update=True) # The default background color for table rows (even, odd, any rows): odd_bg_color = Color(None, update=True) even_bg_color = Color(None, update=True) default_bg_color = Color(None, update=True) # Alignment to use for a specified column: alignment = Enum('left', 'center', 'right') # The Python format string to use for a specified column: format = Str('%s') # Width of a specified column: width = Float(-1) # Can the text value of each item be edited: can_edit = Bool(True) # The value to be dragged for a specified row item: drag = Property # Can any arbitrary value be dropped onto the tabular view: can_drop = Bool(False) # Specifies where a dropped item should be placed in the table relative to # the item it is dropped on: dropped = Enum('after', 'before') # The font for a row item: font = Font(None) # The text color for a row item: text_color = Property # The background color for a row item: bg_color = Property # The name of the default image to use for column items: image = Str(None, update=True) # The text of a row/column item: text = Property # The content of a row/column item (may be any Python value): content = Property # The tooltip information for a row/column item: tooltip = Str # The context menu for a row/column item: menu = Any # The context menu for column header: column_menu = Any # List of optional delegated adapters: adapters = List(ITabularAdapter, update=True) #-- Traits Set by the Editor --------------------------------------------- # The object whose trait is being edited: object = Instance(HasTraits) # The name of the trait being edited: name = Str # The row index of the current item being adapted: row = Int # The column index of the current item being adapted: column = Int # The current column id being adapted (if any): column_id = Any # Current item being adapted: item = Any # The current value (if any): value = Any #-- Private Trait Definitions -------------------------------------------- # Cache of attribute handlers: cache = Any({}) # Event fired when the cache is flushed: cache_flushed = Event(update=True) # The mapping from column indices to column identifiers (defined by the # *columns* trait): column_map = Property(depends_on='columns') # The mapping from column indices to column labels (defined by the *columns* # trait): label_map = Property(depends_on='columns') # The name of the trait on a row item containing the value to use # as a row label. If None, the label will be the empty string. row_label_name = Either(None, Str) # For each adapter, specifies the column indices the adapter handles: adapter_column_indices = Property(depends_on='adapters,columns') # For each adapter, specifies the mapping from column index to column id: adapter_column_map = Property(depends_on='adapters,columns') #### TabularAdapter interface #### def cleanup(self): """ Clean up the adapter to remove references to objects. """ self.trait_setq( object=None, item=None, value=None, ) #-- Adapter methods that are sensitive to item type ---------------------- def get_alignment(self, object, trait, column): """ Returns the alignment style to use for a specified column. """ return self._result_for('get_alignment', object, trait, 0, column) def get_width(self, object, trait, column): """ Returns the width to use for a specified column. """ return self._result_for('get_width', object, trait, 0, column) def get_can_edit(self, object, trait, row): """ Returns whether the user can edit a specified *object.trait[row]* item. A True result indicates the value can be edited, while a False result indicates that it cannot be edited. """ return self._result_for('get_can_edit', object, trait, row, 0) def get_drag(self, object, trait, row): """ Returns the 'drag' value for a specified *object.trait[row]* item. A result of *None* means that the item cannot be dragged. """ return self._result_for('get_drag', object, trait, row, 0) def get_can_drop(self, object, trait, row, value): """ Returns whether the specified *value* can be dropped on the specified *object.trait[row]* item. A value of **True** means the *value* can be dropped; and a value of **False** indicates that it cannot be dropped. """ return self._result_for('get_can_drop', object, trait, row, 0, value) def get_dropped(self, object, trait, row, value): """ Returns how to handle a specified *value* being dropped on a specified *object.trait[row]* item. The possible return values are: 'before' Insert the specified *value* before the dropped on item. 'after' Insert the specified *value* after the dropped on item. """ return self._result_for('get_dropped', object, trait, row, 0, value) def get_font(self, object, trait, row, column=0): """ Returns the font for a specified *object.trait[row]* item. A result of None means use the default font. """ return self._result_for('get_font', object, trait, row, column) def get_text_color(self, object, trait, row, column=0): """ Returns the text color for a specified *object.trait[row]* item. A result of None means use the default text color. """ return self._result_for('get_text_color', object, trait, row, column) def get_bg_color(self, object, trait, row, column=0): """ Returns the background color for a specified *object.trait[row]* item. A result of None means use the default background color. """ return self._result_for('get_bg_color', object, trait, row, column) def get_image(self, object, trait, row, column): """ Returns the name of the image to use for a specified *object.trait[row].column* item. A result of None means no image should be used. Otherwise, the result should either be the name of the image, or an ImageResource item specifying the image to use. """ return self._result_for('get_image', object, trait, row, column) def get_format(self, object, trait, row, column): """ Returns the Python format string to use for a specified column. """ return self._result_for('get_format', object, trait, row, column) def get_text(self, object, trait, row, column): """ Returns the text to display for a specified *object.trait[row].column* item. """ return self._result_for('get_text', object, trait, row, column) def get_content(self, object, trait, row, column): """ Returns the content to display for a specified *object.trait[row].column* item. """ return self._result_for('get_content', object, trait, row, column) def set_text(self, object, trait, row, column, text): """ Sets the text for a specified *object.trait[row].column* item to *text*. """ self._result_for('set_text', object, trait, row, column, text) def get_tooltip(self, object, trait, row, column): """ Returns the tooltip for a specified row. """ return self._result_for('get_tooltip', object, trait, row, column) def get_menu(self, object, trait, row, column): """ Returns the context menu for a specified cell. """ return self._result_for('get_menu', object, trait, row, column) def get_column_menu(self, object, trait, row, column): """ Returns the context menu for a specified column. """ return self._result_for('get_column_menu', object, trait, row, column) #-- Adapter methods that are not sensitive to item type ------------------ def get_item(self, object, trait, row): """ Returns the value of the *object.trait[row]* item. """ try: return getattr(object, trait)[row] except: return None def len(self, object, trait): """ Returns the number of items in the specified *object.trait* list. """ # Sometimes, during shutdown, the object has been set to None. if object is None: return 0 else: return len(getattr(object, trait)) def get_default_value(self, object, trait): """ Returns a new default value for the specified *object.trait* list. """ return self.default_value def delete(self, object, trait, row): """ Deletes the specified *object.trait[row]* item. """ del getattr(object, trait)[row] def insert(self, object, trait, row, value): """ Inserts a new value at the specified *object.trait[row]* index. """ getattr(object, trait)[row:row] = [value] def get_column(self, object, trait, index): """ Returns the column id corresponding to a specified column index. """ self.object, self.name = object, trait return self.column_map[index] #-- Property Implementations --------------------------------------------- def _get_drag(self): return self.item def _get_text_color(self): if (self.row % 2) == 1: return self.even_text_color_ or self.default_text_color return self.odd_text_color or self.default_text_color_ def _get_bg_color(self): if (self.row % 2) == 1: return self.even_bg_color_ or self.default_bg_color_ return self.odd_bg_color or self.default_bg_color_ def _get_text(self): return self.get_format( self.object, self.name, self.row, self.column) % self.get_content( self.object, self.name, self.row, self.column) def _set_text(self, value): if isinstance(self.column_id, int): self.item[self.column_id] = self.value else: # Convert value to the correct trait type. try: trait_handler = self.item.trait(self.column_id).handler setattr(self.item, self.column_id, trait_handler.evaluate(self.value)) except: setattr(self.item, self.column_id, value) def _get_content(self): if isinstance(self.column_id, int): return self.item[self.column_id] return getattr(self.item, self.column_id) #-- Property Implementations --------------------------------------------- @cached_property def _get_column_dict(self): cols = {} for i, value in enumerate(self.columns): if isinstance(value, basestring): cols.update({value: value}) else: cols.update({value[0]: value[1]}) return cols @cached_property def _get_column_map(self): map = [] for i, value in enumerate(self.columns): if isinstance(value, basestring): map.append(i) else: map.append(value[1]) return map def get_label(self, section, obj=None): """Override this method if labels will vary from object to object.""" return self.label_map[section] def get_row_label(self, section, obj=None): if self.row_label_name is None: return None rows = getattr(obj, self.name, None) if rows is None: return None item = rows[section] return getattr(item, self.row_label_name, None) @cached_property def _get_label_map(self): map = [] for i, value in enumerate(self.columns): if isinstance(value, basestring): map.append(value) else: map.append(value[0]) return map @cached_property def _get_adapter_column_indices(self): labels = self.label_map map = [] for adapter in self.adapters: indices = [] for label in adapter.columns: if not isinstance(label, basestring): label = label[0] indices.append(labels.index(label)) map.append(indices) return map @cached_property def _get_adapter_column_map(self): labels = self.label_map map = [] for adapter in self.adapters: mapping = {} for label in adapter.columns: id = None if not isinstance(label, basestring): label, id = label key = labels.index(label) if id is None: id = key mapping[key] = id map.append(mapping) return map #-- Private Methods ------------------------------------------------------ def _result_for(self, name, object, trait, row, column, value=None): """ Returns/Sets the value of the specified *name* attribute for the specified *object.trait[row].column* item. """ self.object = object self.name = trait self.row = row self.column = column self.column_id = column_id = self.column_map[column] self.value = value self.item = item = self.get_item(object, trait, row) item_class = item.__class__ key = '%s:%s:%d' % (item_class.__name__, name, column) handler = self.cache.get(key) if handler is not None: return handler() prefix = name[:4] trait_name = name[4:] for i, adapter in enumerate(self.adapters): if column in self.adapter_column_indices[i]: adapter.row = row adapter.item = item adapter.value = value adapter.column = column_id = self.adapter_column_map[i][column] if adapter.accepts: get_name = '%s_%s' % (column_id, trait_name) if adapter.trait(get_name) is not None: if prefix == 'get_': handler = lambda: getattr( adapter.set(row=self.row, column=column_id, item=self.item), get_name) else: handler = lambda: setattr( adapter.set(row=self.row, column=column_id, item=self.item), get_name, self. value) if adapter.is_cacheable: break return handler() else: if item is not None and hasattr(item_class, '__mro__'): for klass in item_class.__mro__: handler = (self._get_handler_for( '%s_%s_%s' % (klass.__name__, column_id, trait_name), prefix) or self._get_handler_for( '%s_%s' % (klass.__name__, trait_name), prefix)) if handler is not None: break if handler is None: handler = (self._get_handler_for( '%s_%s' % (column_id, trait_name), prefix) or self._get_handler_for(trait_name, prefix)) self.cache[key] = handler return handler() def _get_handler_for(self, name, prefix): """ Returns the handler for a specified trait name (or None if not found). """ if self.trait(name) is not None: if prefix == 'get_': return lambda: getattr(self, name) return lambda: setattr(self, name, self.value) return None @on_trait_change('columns,adapters.+update') def _flush_cache(self): """ Flushes the cache when the columns or any trait on any adapter changes. """ self.cache = {} self.cache_flushed = True
class ListeningAction(Action): """ An Action that listens and makes a callback to an object. """ # ListeningAction interface ---------------------------------------------- #: The (extended) name of the method to call. By default, the on_perform #: function will be called with the event. method = Str() #: The (extended) name of the attribute that determines whether the action #: is enabled. By default, the action is always enabled when an object is #: set. enabled_name = Str() #: The (extended) name of the attribute that determines whether the action #: is visible. By default, the action is always visible. visible_name = Str() #: The object to which the names above apply. object = Any() # ------------------------------------------------------------------------- # 'Action' interface. # ------------------------------------------------------------------------- def destroy(self): """ Called when the action is no longer required. Removes all the task listeners. """ if self.object: self.object.on_trait_change(self._enabled_update, self.enabled_name, remove=True) self.object.on_trait_change(self._visible_update, self.visible_name, remove=True) def perform(self, event=None): """ Call the appropriate function. This looks for a method to call based on the extended method name stored in the :py:attr:`method` trait. If the method is empty, then this follows the usual Action method resolution. """ if self.method != "": method = self._get_attr(self.object, self.method) if method: method() else: super(ListeningAction, self).perform(event) # ------------------------------------------------------------------------- # Protected interface. # ------------------------------------------------------------------------- def _get_attr(self, obj, name, default=None): """ Perform an extended look up of a dotted name. """ try: for attr in name.split("."): # Perform the access in the Trait name style: if the object is # None, assume it simply hasn't been initialized and don't show # the warning. if obj is None: return default else: obj = getattr(obj, attr) except AttributeError: logger.error("Did not find name %r on %r" % (attr, obj)) return default return obj # Trait change handlers -------------------------------------------------- def _enabled_name_changed(self, old, new): obj = self.object if obj is not None: if old: obj.on_trait_change(self._enabled_update, old, remove=True) if new: obj.on_trait_change(self._enabled_update, new) self._enabled_update() def _visible_name_changed(self, old, new): obj = self.object if obj is not None: if old: obj.on_trait_change(self._visible_update, old, remove=True) if new: obj.on_trait_change(self._visible_update, new) self._visible_update() def _object_changed(self, old, new): for kind in ("enabled", "visible"): method = getattr(self, "_%s_update" % kind) name = getattr(self, "%s_name" % kind) if name: if old: old.on_trait_change(method, name, remove=True) if new: new.on_trait_change(method, name) method() def _enabled_update(self): if self.enabled_name: if self.object: self.enabled = bool( self._get_attr(self.object, self.enabled_name, False)) else: self.enabled = False else: self.enabled = bool(self.object) def _visible_update(self): if self.visible_name: if self.object: self.visible = bool( self._get_attr(self.object, self.visible_name, False)) else: self.visible = False else: self.visible = True
class Item(ViewSubElement): """ An element in a Traits-based user interface. Magic: - Items are rendered as layout elements if :attr:`name` is set to special values: * ``name=''``, the item is rendered as a static label * ``name='_'``, the item is rendered as a separator * ``name=' '``, the item is rendered as a 5 pixel spacer * ``name='23'`` (any number), the item is rendered as a spacer of the size specified (number of pixels) """ # FIXME: all the logic for the name = '', '_', ' ', '23' magic is in # _GroupPanel._add_items in qt/ui_panel.py, which is a very unlikely place # to look for it. Ideally, that logic should be in this class. # ------------------------------------------------------------------------- # Trait definitions: # ------------------------------------------------------------------------- #: A unique identifier for the item. If not set, it defaults to the value #: of **name**. id = Str() #: User interface label for the item in the GUI. If this attribute is not #: set, the label is the value of **name** with slight modifications: #: underscores are replaced by spaces, and the first letter is capitalized. #: If an item's **name** is not specified, its label is displayed as #: static text, without any editor widget. label = Str() #: Name of the trait the item is editing: name = Str() #: Style-sheet to apply to item / group (Qt only) style_sheet = Str() #: Help text describing the purpose of the item. The built-in help handler #: displays this text in a pop-up window if the user clicks the widget's #: label. View-level help displays the help text for all items in a view. #: If this attribute is not set, the built-in help handler generates a #: description based on the trait definition. help = Str() #: The HasTraits object whose trait attribute the item is editing: object = ContainerDelegate #: Presentation style for the item: style = ContainerDelegate #: Docking style for the item: dock = ContainerDelegate #: Image to display on notebook tabs: image = ContainerDelegate #: Category of elements dragged from view: export = ContainerDelegate #: Should a label be displayed for the item? show_label = Delegate("container", "show_labels") #: Editor to use for the item: editor = ItemEditor #: Additional editor traits to be set if default traits editor to be used: editor_args = Dict() #: Should the item use extra space along its Group's non-layout axis? If set to #: True, the widget expands to fill any extra space that is available in the #: display. If set to True for more than one item in the same View, any extra #: space is divided between them. If set to False, the widget uses only #: whatever space it is explicitly (or implicitly) assigned. The default #: value of Undefined means that the use (or non-use) of extra space will be #: determined by the editor associated with the item. resizable = Bool(Undefined) #: Should the item use extra space along its Group's layout axis? For #: example, it a vertical group, should an item expand vertically to use #: any extra space available in the group? springy = Bool(False) #: Should the item use any extra space along its Group's non-layout #: orientation? For example, in a vertical group, should an item expand #: horizontally to the full width of the group? If left to the default value #: of Undefined, the decision will be left up to the associated item editor. full_size = Bool(Undefined) #: Should the item's label use emphasized text? If the label is not shown, #: this attribute is ignored. emphasized = Bool(False) #: Should the item receive focus initially? has_focus = Bool(False) #: Pre-condition for including the item in the display. If the expression #: evaluates to False, the item is not defined in the display. Conditions #: for **defined_when** are evaluated only once, when the display is first #: constructed. Use this attribute for conditions based on attributes that #: vary from object to object, but that do not change over time. For example, #: displaying a 'maiden_name' item only for female employees in a company #: database. defined_when = Str() #: Pre-condition for showing the item. If the expression evaluates to False, #: the widget is not visible (and disappears if it was previously visible). #: If the value evaluates to True, the widget becomes visible. All #: **visible_when** conditions are checked each time that any trait value #: is edited in the display. Therefore, you can use **visible_when** #: conditions to hide or show widgets in response to user input. visible_when = Str() #: Pre-condition for enabling the item. If the expression evaluates to False, #: the widget is disabled, that is, it does not accept input. All #: **enabled_when** conditions are checked each time that any trait value #: is edited in the display. Therefore, you can use **enabled_when** #: conditions to enable or disable widgets in response to user input. enabled_when = Str() #: Amount of extra space, in pixels, to add around the item. Values must be #: integers between -15 and 15. Use negative values to subtract from the #: default spacing. padding = Padding #: Tooltip to display over the item, when the mouse pointer is left idle #: over the widget. Make this text as concise as possible; use the **help** #: attribute to provide more detailed information. tooltip = Str() #: A Callable to use for formatting the contents of the item. This function #: or method is called to create the string representation of the trait value #: to be edited. If the widget does not use a string representation, this #: attribute is ignored. format_func = Callable() #: Python format string to use for formatting the contents of the item. #: The format string is applied to the string representation of the trait #: value before it is displayed in the widget. This attribute is ignored if #: the widget does not use a string representation, or if the #: **format_func** is set. format_str = Str() #: Requested width of the editor (in pixels or fraction of available width). #: For pixel values (i.e. values not in the range from 0.0 to 1.0), the #: actual displayed width is at least the maximum of **width** and the #: optimal width of the widget as calculated by the GUI toolkit. Specify a #: negative value to ignore the toolkit's optimal width. For example, use #: -50 to force a width of 50 pixels. The default value of -1 ensures that #: the toolkit's optimal width is used. #: #: A value in the range from 0.0 to 1.0 specifies the fraction of the #: available width to assign to the editor. Note that the value is not an #: absolute value, but is relative to other item's whose **width** is also #: in the 0.0 to 1.0 range. For example, if you have two item's with a width #: of 0.1, and one item with a width of 0.2, the first two items will each #: receive 25% of the available width, while the third item will receive #: 50% of the available width. The available width is the total width of the #: view minus the width of any item's with fixed pixel sizes (i.e. width #: values not in the 0.0 to 1.0 range). width = Float(-1.0) #: Requested height of the editor (in pixels or fraction of available #: height). For pixel values (i.e. values not in the range from 0.0 to 1.0), #: the actual displayed height is at least the maximum of **height** and the #: optimal height of the widget as calculated by the GUI toolkit. Specify a #: negative value to ignore the toolkit's optimal height. For example, use #: -50 to force a height of 50 pixels. The default value of -1 ensures that #: the toolkit's optimal height is used. #: #: A value in the range from 0.0 to 1.0 specifies the fraction of the #: available height to assign to the editor. Note that the value is not an #: absolute value, but is relative to other item's whose **height** is also #: in the 0.0 to 1.0 range. For example, if you have two item's with a height #: of 0.1, and one item with a height of 0.2, the first two items will each #: receive 25% of the available height, while the third item will receive #: 50% of the available height. The available height is the total height of #: the view minus the height of any item's with fixed pixel sizes (i.e. #: height values not in the 0.0 to 1.0 range). height = Float(-1.0) #: The extended trait name of the trait containing the item's invalid state #: status (passed through to the item's editor): invalid = Str() def __init__(self, value=None, **traits): """ Initializes the item object. """ super(Item, self).__init__(**traits) if value is None: return if not isinstance(value, str): raise TypeError( "The argument to Item must be a string of the " "form: [id:][object.[object.]*][name]['['label']']`tooltip`" "[<width[,height]>][#^][$|@|*|~|;style]") value, empty = self._parse_label(value) if empty: self.show_label = False value = self._parse_style(value) value = self._parse_size(value) value = self._parse_tooltip(value) value = self._option(value, "#", "resizable", True) value = self._option(value, "^", "emphasized", True) value = self._split("id", value, ":", str.find, 0, 1) value = self._split("object", value, ".", str.rfind, 0, 1) if value != "": self.name = value def is_includable(self): """ Returns a Boolean indicating whether the object is replaceable by an Include object. """ return self.id != "" def is_spacer(self): """ Returns True if the item represents a spacer or separator. """ name = self.name.strip() return ((name == "") or (name == "_") or (all_digits.match(name) is not None)) def get_help(self, ui): """ Gets the help text associated with the Item in a specified UI. """ # Return 'None' if the Item is a separator or spacer: if self.is_spacer(): return None # Otherwise, it must be a trait Item: if self.help != "": return self.help object = eval(self.object_, globals(), ui.context) return object.base_trait(self.name).get_help() def get_label(self, ui): """ Gets the label to use for a specified Item. If not specified, the label is set as the name of the corresponding trait, replacing '_' with ' ', and capitalizing the first letter (see :func:`user_name_for`). This is called the *user name*. Magic: - if attr:`item.label` is specified, and it begins with '...', the final label is the user name followed by the item label - if attr:`item.label` is specified, and it ends with '...', the final label is the item label followed by the user name """ # Return 'None' if the Item is a separator or spacer: if self.is_spacer(): return None label = self.label if label != "": return label name = self.name object = eval(self.object_, globals(), ui.context) trait = object.base_trait(name) label = user_name_for(name) tlabel = trait.label if tlabel is None: return label if isinstance(tlabel, str): if tlabel[0:3] == "...": return label + tlabel[3:] if tlabel[-3:] == "...": return tlabel[:-3] + label if self.label != "": return self.label return tlabel return tlabel(object, name, label) def get_id(self): """ Returns an ID used to identify the item. """ if self.id != "": return self.id return self.name def _parse_size(self, value): """ Parses a '<width,height>' value from the string definition. """ match = size_pat.match(value) if match is not None: data = match.group(2) value = match.group(1) + match.group(3) col = data.find(",") if col < 0: self._set_float("width", data) else: self._set_float("width", data[:col]) self._set_float("height", data[col + 1:]) return value def _parse_tooltip(self, value): """ Parses a *tooltip* value from the string definition. """ match = tooltip_pat.match(value) if match is not None: self.tooltip = match.group(2) value = match.group(1) + match.group(3) return value def _set_float(self, name, value): """ Sets a specified trait to a specified string converted to a float. """ value = value.strip() if value != "": setattr(self, name, float(value)) def __repr__(self): """ Returns a "pretty print" version of the Item. """ options = self._repr_options("id", "object", "label", "style", "show_label", "width", "height") if options is None: return "Item( '%s' )" % self.name return "Item( '%s'\n%s\n)" % ( self.name, self._indent(options, " "), )
class PythonEditor(MPythonEditor, Widget): """ The toolkit specific implementation of a PythonEditor. See the IPythonEditor interface for the API documentation. """ # 'IPythonEditor' interface -------------------------------------------- dirty = Bool(False) path = Str() show_line_numbers = Bool(True) # Events ---- changed = Event() key_pressed = Event(KeyPressedEvent) # ------------------------------------------------------------------------ # 'object' interface. # ------------------------------------------------------------------------ def __init__(self, parent, **traits): """ Creates a new pager. """ # Base class constructor. super(PythonEditor, self).__init__(**traits) # Create the toolkit-specific control that represents the widget. self.control = self._create_control(parent) return # ------------------------------------------------------------------------ # 'PythonEditor' interface. # ------------------------------------------------------------------------ def load(self, path=None): """ Loads the contents of the editor. """ if path is None: path = self.path # We will have no path for a new script. if len(path) > 0: f = open(self.path, "r") text = f.read() f.close() else: text = "" self.control.SetText(text) self.dirty = False def save(self, path=None): """ Saves the contents of the editor. """ if path is None: path = self.path f = open(path, "w") f.write(self.control.GetText()) f.close() self.dirty = False def set_style(self, n, fore, back): self.control.StyleSetForeground(n, fore) # self.StyleSetBackground(n, '#c0c0c0') # self.StyleSetBackground(n, '#ffffff') self.control.StyleSetBackground(n, back) self.control.StyleSetFaceName(n, "courier new") self.control.StyleSetSize(n, faces["size"]) # self.StyleSetForeground(n, "#f0f0f0") ##self.StyleSetBackground(n, "#000000") # self.StyleSetFaceName(n, "courier new") # self.StyleSetSize(n, 20) # self.StyleSetUnderline(n, 1) # self.StyleSetItalic(n, 1) # self.StyleSetBold(n, 1) # StyleClearAll # StyleResetDefault # StyleSetCase # StyleSetChangeable # StyleSetCharacterSet # StyleSetEOLFilled # StyleSetFont # StyleSetFontAttr # StyleSetHotSpot # StyleSetSpec --- batch # StyleSetVisible def select_line(self, lineno): """ Selects the specified line. """ start = self.control.PositionFromLine(lineno) end = self.control.GetLineEndPosition(lineno) self.control.SetSelection(start, end) return # ------------------------------------------------------------------------ # Trait handlers. # ------------------------------------------------------------------------ def _path_changed(self): """ Handle a change to path. """ self._changed_path() return # ------------------------------------------------------------------------ # Private interface. # ------------------------------------------------------------------------ def _create_control(self, parent): """ Creates the toolkit-specific control for the widget. """ # Base-class constructor. self.control = stc = PythonSTC(parent, -1) # No folding! stc.SetProperty("fold", "0") # Mark the maximum line size. stc.SetEdgeMode(wx.stc.STC_EDGE_LINE) stc.SetEdgeColumn(79) # Display line numbers in the margin. if self.show_line_numbers: stc.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER) stc.SetMarginWidth(1, 45) self.set_style(wx.stc.STC_STYLE_LINENUMBER, "#000000", "#c0c0c0") else: stc.SetMarginWidth(1, 4) self.set_style(wx.stc.STC_STYLE_LINENUMBER, "#ffffff", "#ffffff") # Create 'tabs' out of spaces! stc.SetUseTabs(False) # One 'tab' is 4 spaces. stc.SetIndent(4) # Line ending mode. stc.SetEOLMode(wx.stc.STC_EOL_LF) # Unix # self.SetEOLMode(wx.stc.STC_EOL_CR) # Apple Mac # self.SetEOLMode(wx.stc.STC_EOL_CRLF) # Windows # ------------------------------------------------------------------------ # Global styles for all languages. # ------------------------------------------------------------------------ self.set_style(wx.stc.STC_STYLE_DEFAULT, "#000000", "#ffffff") self.set_style(wx.stc.STC_STYLE_CONTROLCHAR, "#000000", "#ffffff") self.set_style(wx.stc.STC_STYLE_BRACELIGHT, "#000000", "#ffffff") self.set_style(wx.stc.STC_STYLE_BRACEBAD, "#000000", "#ffffff") # ------------------------------------------------------------------------ # Python styles. # ------------------------------------------------------------------------ # White space self.set_style(wx.stc.STC_P_DEFAULT, "#000000", "#ffffff") # Comment self.set_style(wx.stc.STC_P_COMMENTLINE, "#007f00", "#ffffff") # Number self.set_style(wx.stc.STC_P_NUMBER, "#007f7f", "#ffffff") # String self.set_style(wx.stc.STC_P_STRING, "#7f007f", "#ffffff") # Single quoted string self.set_style(wx.stc.STC_P_CHARACTER, "#7f007f", "#ffffff") # Keyword self.set_style(wx.stc.STC_P_WORD, "#00007f", "#ffffff") # Triple quotes self.set_style(wx.stc.STC_P_TRIPLE, "#7f0000", "#ffffff") # Triple double quotes self.set_style(wx.stc.STC_P_TRIPLEDOUBLE, "#ff0000", "#ffffff") # Class name definition self.set_style(wx.stc.STC_P_CLASSNAME, "#0000ff", "#ffffff") # Function or method name definition self.set_style(wx.stc.STC_P_DEFNAME, "#007f7f", "#ffffff") # Operators self.set_style(wx.stc.STC_P_OPERATOR, "#000000", "#ffffff") # Identifiers self.set_style(wx.stc.STC_P_IDENTIFIER, "#000000", "#ffffff") # Comment-blocks self.set_style(wx.stc.STC_P_COMMENTBLOCK, "#007f00", "#ffffff") # End of line where string is not closed self.set_style(wx.stc.STC_P_STRINGEOL, "#000000", "#ffffff") # ------------------------------------------------------------------------ # Events. # ------------------------------------------------------------------------ # By default, the will fire EVT_STC_CHANGE evented for all mask values # (STC_MODEVENTMASKALL). This generates too many events. stc.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT | wx.stc.STC_MOD_DELETETEXT | wx.stc.STC_PERFORMED_UNDO | wx.stc.STC_PERFORMED_REDO) # Listen for changes to the file. stc.Bind(wx.stc.EVT_STC_CHANGE, self._on_stc_changed) # Listen for key press events. stc.Bind(wx.EVT_CHAR, self._on_char) # Load the editor's contents. self.load() return stc def destroy(self): """ Destroy the toolkit control. """ if self.control is not None: self.control.Unbind(wx.stc.EVT_STC_CHANGE) self.control.Unbind(wx.EVT_CHAR) super().destroy() # wx event handlers ---------------------------------------------------- def _on_stc_changed(self, event): """ Called whenever a change is made to the text of the document. """ self.dirty = True self.changed = True # Give other event handlers a chance. event.Skip() def _on_char(self, event): """ Called whenever a change is made to the text of the document. """ self.key_pressed = KeyPressedEvent( alt_down=event.altDown, control_down=event.controlDown, shift_down=event.shiftDown, key_code=event.KeyCode, event=event, ) # Give other event handlers a chance. event.Skip() return
class RegexValidator(Validator): """ A concrete Validator which handles text input. This validator ensures that the text matches a provided regular expression string. """ #: The regular expression string to use for validation. The default #: regex matches everything. regex = Str(r'.*') #: A read only cached property which holds the compiled regular #: expression object. _regex = Property(depends_on='regex') @cached_property def _get__regex(self): """ The getter for the '_regex' property. Returns ------- result : sre object A compiled regular expression object for the current regex string. """ return re.compile(self.regex, re.UNICODE) def validate(self, text, component): """ Validates the given text matches the regular expression. Parameters ---------- text : unicode The unicode text edited by the client widget. component : Declarative The declarative component currently making use of the validator. Returns ------- result : (unicode, bool) A 2-tuple of (optionally modified) unicode text, and whether or not that text should be considered valid. """ return (text, bool(self._regex.match(text))) def client_validator(self): """ The client side regex validator. Returns ------- result : dict The dict representation of the client side regex validator. """ res = {} res['type'] = 'regex' res['message'] = self.message res['arguments'] = {'regex': self.regex} return res
class _TickMarketInfo(_BarMarketInfo): """Tick 因子映照""" Bid = Str(arg_type="String", label="买盘价格", order=6) BidVol = Str(arg_type="String", label="买盘数量", order=7) Ask = Str(arg_type="String", label="卖盘价格", order=8) AskVol = Str(arg_type="String", label="卖盘数量", order=9)
class Department(HasTraits): name = Str('<unknown>') employees = List(Employee)
class PolygonOp(HasStrictTraits): """ Apply a polygon gate to a cytometry experiment. Attributes ---------- name : Str The operation name. Used to name the new metadata field in the experiment that's created by :meth:`apply` xchannel, ychannel : Str The names of the x and y channels to apply the gate. xscale, yscale : {'linear', 'log', 'logicle'} (default = 'linear') The scales applied to the data before drawing the polygon. vertices : List((Float, Float)) The polygon verticies. An ordered list of 2-tuples, representing the x and y coordinates of the vertices. Notes ----- This module uses :meth:`matplotlib.path.Path` to represent the polygon, because membership testing is very fast. You can set the verticies by hand, I suppose, but it's much easier to use the interactive view you get from :meth:`default_view` to do so. Examples -------- .. plot:: :context: close-figs Make a little data set. >>> import cytoflow as flow >>> import_op = flow.ImportOp() >>> import_op.tubes = [flow.Tube(file = "Plate01/RFP_Well_A3.fcs", ... conditions = {'Dox' : 10.0}), ... flow.Tube(file = "Plate01/CFP_Well_A4.fcs", ... conditions = {'Dox' : 1.0})] >>> import_op.conditions = {'Dox' : 'float'} >>> ex = import_op.apply() Create and parameterize the operation. .. plot:: :context: close-figs >>> p = flow.PolygonOp(name = "Polygon", ... xchannel = "V2-A", ... ychannel = "Y2-A") >>> p.vertices = [(23.411982294776319, 5158.7027015021222), ... (102.22182270573683, 23124.058843387455), ... (510.94519955277201, 23124.058843387455), ... (1089.5215641232173, 3800.3424832180476), ... (340.56382570202402, 801.98947404942271), ... (65.42597937575897, 1119.3133482602157)] Show the default view. .. plot:: :context: close-figs >>> df = p.default_view(huefacet = "Dox", ... xscale = 'log', ... yscale = 'log') >>> df.plot(ex) .. note:: If you want to use the interactive default view in a Jupyter notebook, make sure you say ``%matplotlib notebook`` in the first cell (instead of ``%matplotlib inline`` or similar). Then call ``default_view()`` with ``interactive = True``:: df = p.default_view(huefacet = "Dox", xscale = 'log', yscale = 'log', interactive = True) df.plot(ex) Apply the gate, and show the result .. plot:: :context: close-figs >>> ex2 = p.apply(ex) >>> ex2.data.groupby('Polygon').size() Polygon False 15875 True 4125 dtype: int64 """ # traits id = Constant('edu.mit.synbio.cytoflow.operations.polygon') friendly_id = Constant("Polygon") name = CStr() xchannel = Str() ychannel = Str() vertices = List((Float, Float)) xscale = util.ScaleEnum() yscale = util.ScaleEnum() _selection_view = Instance('PolygonSelection', transient=True) def apply(self, experiment): """Applies the threshold to an experiment. Parameters ---------- experiment : Experiment the old :class:`Experiment` to which this op is applied Returns ------- Experiment a new :class:'Experiment`, the same as ``old_experiment`` but with a new column of type `bool` with the same as the operation name. The bool is ``True`` if the event's measurement is within the polygon, and ``False`` otherwise. Raises ------ util.CytoflowOpError if for some reason the operation can't be applied to this experiment. The reason is in :attr:`.CytoflowOpError.args` """ if experiment is None: raise util.CytoflowOpError('experiment', "No experiment specified") if self.name in experiment.data.columns: raise util.CytoflowOpError( 'name', "{} is in the experiment already!".format(self.name)) if self.name != util.sanitize_identifier(self.name): raise util.CytoflowOpError( 'name', "Name can only contain letters, numbers and underscores.". format(self.name)) if not self.xchannel: raise util.CytoflowOpError('xchannel', "Must specify an x channel") if not self.ychannel: raise util.CytoflowOpError('ychannel', "Must specify a y channel") if not self.xchannel in experiment.channels: raise util.CytoflowOpError( 'xchannel', "xchannel {0} is not in the experiment".format(self.xchannel)) if not self.ychannel in experiment.channels: raise util.CytoflowOpError( 'ychannel', "ychannel {0} is not in the experiment".format(self.ychannel)) if len(self.vertices) < 3: raise util.CytoflowOpError('vertices', "Must have at least 3 vertices") if any([len(x) != 2 for x in self.vertices]): return util.CytoflowOpError( 'vertices', "All vertices must be lists or tuples " "of length = 2") # make sure name got set! if not self.name: raise util.CytoflowOpError( 'name', "You have to set the Polygon gate's name " "before applying it!") # make sure old_experiment doesn't already have a column named self.name if (self.name in experiment.data.columns): raise util.CytoflowOpError( 'name', "Experiment already contains a column {0}".format(self.name)) # there's a bit of a subtlety here: if the vertices were # selected with an interactive plot, and that plot had scaled # axes, we need to apply that scale function to both the # vertices and the data before looking for path membership xscale = util.scale_factory(self.xscale, experiment, channel=self.xchannel) yscale = util.scale_factory(self.yscale, experiment, channel=self.ychannel) vertices = [(xscale(x), yscale(y)) for (x, y) in self.vertices] data = experiment.data[[self.xchannel, self.ychannel]].copy() data[self.xchannel] = xscale(data[self.xchannel]) data[self.ychannel] = yscale(data[self.ychannel]) # use a matplotlib Path because testing for membership is a fast C fn. path = mpl.path.Path(np.array(vertices)) xy_data = data[[self.xchannel, self.ychannel]].values new_experiment = experiment.clone() new_experiment.add_condition(self.name, "bool", path.contains_points(xy_data)) new_experiment.history.append( self.clone_traits(transient=lambda _: True)) return new_experiment def default_view(self, **kwargs): self._selection_view = PolygonSelection(op=self) self._selection_view.trait_set(**kwargs) return self._selection_view
class Partner(HasTraits): name = Str('<unknown>') company = Instance(Company)
class AnalysisTable(HasTraits): analyses = List oanalyses = List selected = Any dclicked = Any analysis_filter = Str analysis_filter_values = List analysis_filter_comparator = Enum('=', '<', '>', '>=', '<=', 'not =', 'startswith') analysis_filter_parameter = Str('Record_id') analysis_filter_parameters = List( ['Record_id', 'Tag', 'Age', 'Labnumber', 'Aliquot', 'Step']) omit_invalid = Bool(True) configure_analysis_table = Button # forward = Button # backward = Button # page_width = Int(1000) # page = Int(1, enter_set=True, auto_set=False) # # forward_enabled = Bool # backward_enabled = Bool # n_all_analyses = Int # npages = Property(depends_on='n_all_analyses,page_width') no_update = False scroll_to_row = Event refresh_needed = Event # def load(self): # p = os.path.join(paths.hidden_dir, 'analysis_table') # if os.path.isfile(p): # d={} # with open(p, 'r') as fp: # try: # d=pickle.load(fp) # except (pickle.PickleError, OSError, EOFError): # pass # # self.trait_set(**d) # # def dump(self): # p=os.path.join(paths.hidden_dir, 'analysis_table') # with open(p,'w') as fp: # pickle.dump({'page_width':self.page_width}, fp) # def _forward_fired(self): # if self.page < self.npages: # self.page += 1 # #if self.oanalyses: # # self.page+=1 # # def _backward_fired(self): # p = self.page # p -= 1 # self.page = max(1, p) def set_analyses(self, ans, tc=None, page=None, reset_page=False): self.analyses = ans self.oanalyses = ans if tc is None: tc = len(ans) self.n_all_analyses = tc # if reset_page: # self.no_update = True # if page<0: # self.page=self.npages # self.scroll_to_row=self.page_width # else: # self.page = 1 # self.no_update = False def _analysis_filter_changed(self, new): if new: name = self.analysis_filter_parameter comp = self.analysis_filter_comparator if name == 'Step': new = new.upper() self.analyses = filter(self._filter_func(new, name, comp), self.oanalyses) else: self.analyses = self.oanalyses def _configure_analysis_table_fired(self): c = TableConfigurer(adapter=self.tabular_adapter, title='Configure Analysis Table') c.edit_traits() # def _get_npages(self): # try: # return int(math.ceil(self.n_all_analyses / float(self.page_width))) # except ZeroDivisionError: # return 0 def _get_analysis_filter_parameter(self): p = self.analysis_filter_parameter return p.lower() def _analysis_filter_comparator_changed(self): self._analysis_filter_changed(self.analysis_filter) def _analysis_filter_parameter_changed(self, new): if new: vs = [] p = self._get_analysis_filter_parameter() for si in self.oanalyses: v = getattr(si, p) if not v in vs: vs.append(v) self.analysis_filter_values = vs
class ICDecay(BaseModule): """IC 衰减""" #TestFactor = Enum(None, arg_type="SingleOption", label="测试因子", order=0) FactorOrder = Enum("降序","升序", arg_type="SingleOption", label="排序方向", order=1) #PriceFactor = Enum(None, arg_type="SingleOption", label="价格因子", order=2) #IndustryFactor = Enum("无", arg_type="SingleOption", label="行业因子", order=3) #WeightFactor = Enum("等权", arg_type="SingleOption", label="权重因子", order=4) CalcDTs = List(dt.datetime, arg_type="DateList", label="计算时点", order=5) LookBack = ListInt(np.arange(1,13).tolist(), arg_type="NultiOpotion", label="回溯期数", order=6) CorrMethod = Enum("spearman", "pearson", "kendall", arg_type="SingleOption", label="相关性算法", order=7) IDFilter = Str(arg_type="IDFilter", label="筛选条件", order=8) def __init__(self, factor_table, name="IC 衰减", sys_args={}, **kwargs): self._FactorTable = factor_table super().__init__(name=name, sys_args=sys_args, **kwargs) def __QS_initArgs__(self): DefaultNumFactorList, DefaultStrFactorList = getFactorList(dict(self._FactorTable.getFactorMetaData(key="DataType"))) self.add_trait("TestFactor", Enum(*DefaultNumFactorList, arg_type="SingleOption", label="测试因子", order=0)) self.add_trait("PriceFactor", Enum(*DefaultNumFactorList, arg_type="SingleOption", label="价格因子", order=2)) self.PriceFactor = searchNameInStrList(DefaultNumFactorList, ['价','Price','price']) self.add_trait("IndustryFactor", Enum(*(["无"]+DefaultStrFactorList), arg_type="SingleOption", label="行业因子", order=3)) self.add_trait("WeightFactor", Enum(*(["等权"]+DefaultNumFactorList), arg_type="SingleOption", label="权重因子", order=4)) def __QS_start__(self, mdl, dts, **kwargs): if self._isStarted: return () super().__QS_start__(mdl=mdl, dts=dts, **kwargs) self._Output = {"IC":[[] for i in self.LookBack]} self._Output["时点"] = [] self._CurCalcInd = 0 return (self._FactorTable, ) def __QS_move__(self, idt, **kwargs): if self._iDT==idt: return 0 self._iDT = idt if self.CalcDTs: if idt not in self.CalcDTs[self._CurCalcInd:]: return 0 self._CurCalcInd = self.CalcDTs[self._CurCalcInd:].index(idt) + self._CurCalcInd LastInd = self._CurCalcInd - 1 LastDateTime = self.CalcDTs[LastInd] else: self._CurCalcInd = self._Model.DateTimeIndex LastInd = self._CurCalcInd - 1 LastDateTime = self._Model.DateTimeSeries[LastInd] if (LastInd<0): for i, iRollBack in enumerate(self.LookBack): self._Output["IC"][i].append(np.nan) self._Output["时点"].append(idt) return 0 Price = self._FactorTable.readData(dts=[LastDateTime, idt], ids=self._FactorTable.getID(ifactor_name=self.PriceFactor), factor_names=[self.PriceFactor]).iloc[0] Ret = Price.iloc[1] / Price.iloc[0] - 1 for i, iRollBack in enumerate(self.LookBack): iPreInd = self._CurCalcInd - iRollBack if iPreInd<0: self._Output["IC"][i].append(np.nan) continue iPreDT = self.CalcDTs[iPreInd] iPreIDs = self._FactorTable.getFilteredID(idt=iPreDT, id_filter_str=self.IDFilter) iRet = Ret.loc[iPreIDs].copy() if self.IndustryFactor!="无": IndustryData = self._FactorTable.readData(dts=[iPreDT], ids=iPreIDs, factor_names=[self.IndustryFactor]).iloc[0,0,:] AllIndustry = IndustryData.unique() # 进行收益率的行业调整 if self.WeightFactor=="等权": for iIndustry in AllIndustry: iRet[IndustryData==iIndustry] -= iRet[IndustryData==iIndustry].mean() else: WeightData = self._FactorTable.readData(dts=[iPreDT], ids=iPreIDs, factor_names=[self.WeightFactor]).iloc[0,0,:] for iIndustry in AllIndustry: iWeight = WeightData[IndustryData==iIndustry] iiRet = iRet[IndustryData==iIndustry] iRet[IndustryData==iIndustry] -= (iiRet * iWeight).sum() / iWeight[pd.notnull(iWeight) & pd.notnull(iiRet)].sum(skipna=False) iFactorExpose = self._FactorTable.readData(dts=[iPreDT], ids=iPreIDs, factor_names=[self.TestFactor]).iloc[0,0,:] self._Output["IC"][i].append(iFactorExpose.corr(iRet, method=self.CorrMethod)) self._Output["时点"].append(idt) return 0 def __QS_end__(self): if not self._isStarted: return 0 super().__QS_end__() self._Output["IC"] = pd.DataFrame(np.array(self._Output["IC"]).T, index=self._Output.pop("时点"), columns=list(self.LookBack)) if self.FactorOrder=="升序": self._Output["IC"] = -self._Output["IC"] self._Output["统计数据"] = pd.DataFrame(index=self._Output["IC"].columns) self._Output["统计数据"]["IC平均值"] = self._Output["IC"].mean() nDT = pd.notnull(self._Output["IC"]).sum() self._Output["统计数据"]["标准差"] = self._Output["IC"].std() self._Output["统计数据"]["IC_IR"] = self._Output["统计数据"]["IC平均值"] / self._Output["统计数据"]["标准差"] self._Output["统计数据"]["t统计量"] = self._Output["统计数据"]["IC_IR"] * nDT**0.5 self._Output["统计数据"]["胜率"] = (self._Output["IC"]>0).sum() / nDT return 0 def genMatplotlibFig(self, file_path=None): Fig, Axes = plt.subplots(figsize=(16, 8)) xData = np.arange(0, self._Output["统计数据"].shape[0]) xTickLabels = [str(i) for i in self._Output["统计数据"].index] yMajorFormatter = FuncFormatter(_QS_formatMatplotlibPercentage) Axes.yaxis.set_major_formatter(yMajorFormatter) Axes.bar(xData, self._Output["统计数据"]["IC平均值"].values, label="IC", color="b") Axes.set_xticks(xData) Axes.set_xticklabels(xTickLabels) Axes.legend(loc='upper left') RAxes = Axes.twinx() RAxes.yaxis.set_major_formatter(yMajorFormatter) RAxes.plot(xData, self._Output["统计数据"]["胜率"].values, label="胜率", color="r", alpha=0.6, lw=3) RAxes.legend(loc="upper right") plt.setp(Axes.get_xticklabels(), visible=True, rotation=0, ha='center') if file_path is not None: Fig.savefig(file_path, dpi=150, bbox_inches='tight') return Fig def _repr_html_(self): if len(self.ArgNames)>0: HTML = "参数设置: " HTML += '<ul align="left">' for iArgName in self.ArgNames: if iArgName!="计算时点": HTML += "<li>"+iArgName+": "+str(self.Args[iArgName])+"</li>" elif self.Args[iArgName]: HTML += "<li>"+iArgName+": 自定义时点</li>" else: HTML += "<li>"+iArgName+": 所有时点</li>" HTML += "</ul>" else: HTML = "" Formatters = [_QS_formatPandasPercentage]*2+[lambda x:'{0:.4f}'.format(x), lambda x:'{0:.2f}'.format(x), _QS_formatPandasPercentage] iHTML = self._Output["统计数据"].to_html(formatters=Formatters) Pos = iHTML.find(">") HTML += iHTML[:Pos]+' align="center"'+iHTML[Pos:] Fig = self.genMatplotlibFig() # figure 保存为二进制文件 Buffer = BytesIO() plt.savefig(Buffer, bbox_inches='tight') PlotData = Buffer.getvalue() # 图像数据转化为 HTML 格式 ImgStr = "data:image/png;base64,"+base64.b64encode(PlotData).decode() HTML += ('<img src="%s">' % ImgStr) return HTML
ViewStatus) from .handler import Handler, default_handler from .group import Group from .item import Item from .include import Include #------------------------------------------------------------------------- # Trait definitions: #------------------------------------------------------------------------- # Name of the view trait: AnId = Str(desc='the name of the view') # Contents of the view trait (i.e., a single Group object): Content = Instance(Group, desc='the content of the view') # An optional model/view factory for converting the model into a viewable # 'model_view' object AModelView = Callable(desc='the factory function for converting a model ' 'into a model/view object') # Reference to a Handler object trait: AHandler = Any(desc='the handler for the view') # Dialog window title trait: ATitle = Str(desc='the window title for the view')
class Sike(HasTraits): """ Tie several profile-related widgets together. Sike is like Gotcha, only less mature. """ # The main pstats.Stats() object providing the data. stats = Any() # The main results and the subcalls. main_results = Instance(ProfileResults, args=()) caller_results = Instance(ProfileResults, args=()) callee_results = Instance(ProfileResults, args=()) # The records have list of callers. Invert this to give a map from function # to callee. callee_map = Dict() # Map from the (file, lineno, name) tuple to the record. record_map = Dict() #### GUI traits ############################################################ basenames = Bool(True) percentages = Bool(True) filename = Str() line = Int(1) code = Str() traits_view = tui.View( tui.VGroup( tui.HGroup( tui.Item('basenames'), tui.Item('percentages'), ), tui.HGroup( tui.UItem('main_results'), tui.VGroup( tui.Label('Callees'), tui.UItem('callee_results'), tui.Label('Callers'), tui.UItem('caller_results'), tui.UItem('filename', style='readonly'), tui.UItem('code', editor=tui.CodeEditor(line='line')), ), style='custom', ), ), width=1024, height=768, resizable=True, title='Profiling results', ) @classmethod def fromstats(cls, stats, **traits): """ Instantiate an Sike from a Stats object, Stats.stats dictionary, or Profile object, or a filename of the saved Stats data. """ stats = SillyStatsWrapper.getstats(stats) self = cls(stats=stats, **traits) self._refresh_stats() return self def add_stats(self, stats): """ Add new statistics. """ stats = SillyStatsWrapper.getstats(stats) self.stats.add(stats) self._refresh_stats() def records_from_stats(self, stats): """ Create a list of records from a stats dictionary. """ records = [] for file_line_name, (ncalls, nonrec_calls, inline_time, cum_time, calls) in list(stats.items()): newcalls = [] for sub_file_line_name, sub_call in list(calls.items()): newcalls.append(Subrecord((sub_file_line_name, ) + sub_call)) records.append( Record((file_line_name, ncalls, nonrec_calls, inline_time, cum_time, newcalls))) return records def get_callee_map(self, records): """ Create a callee map. """ callees = defaultdict(list) for record in records: for caller in record.callers: callees[caller.file_line_name].append( Subrecord((record.file_line_name, ) + caller[1:])) return callees @on_trait_change('percentages,basenames') def _adapter_traits_changed(self, object, name, old, new): for obj in [ self.main_results, self.callee_results, self.caller_results ]: setattr(obj, name, new) @on_trait_change('main_results:selected_record') def update_sub_results(self, new): if new is None: return self.caller_results.total_time = new.cum_time self.caller_results.records = new.callers self.callee_results._resort() self.caller_results.selected_record = self.caller_results.activated_record = None self.callee_results.total_time = new.cum_time self.callee_results.records = self.callee_map.get( new.file_line_name, []) self.callee_results._resort() self.callee_results.selected_record = self.callee_results.activated_record = None filename, line, name = new.file_line_name if os.path.exists(filename): with open(filename, 'ru') as f: code = f.read() self.code = code self.filename = filename self.line = line else: self.trait_set( code='', filename='', line=1, ) @on_trait_change('caller_results:dclicked,' 'callee_results:dclicked') def goto_record(self, new): if new is None: return if new.item.file_line_name in self.record_map: record = self.record_map[new.item.file_line_name] self.main_results.selected_record = record @on_trait_change('stats') def _refresh_stats(self): """ Refresh the records from the stored Stats object. """ self.main_results.records = self.main_results.sort_records( self.records_from_stats(self.stats.stats)) self.callee_map = self.get_callee_map(self.main_results.records) self.record_map = {} total_time = 0.0 for record in self.main_results.records: self.record_map[record.file_line_name] = record total_time += record.inline_time self.main_results.total_time = total_time
class TabularAdapter(HasPrivateTraits): """ The base class for adapting list items to values that can be edited by a TabularEditor. """ # -- Public Trait Definitions --------------------------------------------- #: A list of columns that should appear in the table. Each entry can have #: one of two forms: ``string`` or ``(string, id)``, where ``string`` is #: the UI name of the column, and ``id`` is a value that identifies that #: column to the adapter. Normally this value is either a trait name or an #: index, but it can be any value that the adapter wants. If only #: ``string`` is specified, then ``id`` is the index of the ``string`` #: within :py:attr:`columns`. columns = List() #: Maps UI name of column to value identifying column to the adapter, if #: different. column_dict = Property() #: Specifies the default value for a new row. This will usually need to be #: overridden. default_value = Any("") #: The default text color for odd table rows. odd_text_color = Color(None, update=True) #: The default text color for even table rows. even_text_color = Color(None, update=True) #: The default text color for table rows. default_text_color = Color(None, update=True) #: The default background color for odd table rows. odd_bg_color = Color(None, update=True) #: The default background color for even table rows. even_bg_color = Color(None, update=True) #: The default background color for table rows. default_bg_color = Color(None, update=True) #: Horizontal alignment to use for a specified column. alignment = Enum("left", "center", "right") #: The Python format string to use for a specified column. format = Str("%s") #: Width of a specified column. width = Float(-1) #: Can the text value of each item be edited? can_edit = Bool(True) #: The value to be dragged for a specified row item. drag = Property() #: Can any arbitrary value be dropped onto the tabular view. can_drop = Bool(False) #: Specifies where a dropped item should be placed in the table relative to #: the item it is dropped on. dropped = Enum("after", "before") #: The font for a row item. font = Font(None) #: The text color for a row item. text_color = Property() #: The background color for a row item. bg_color = Property() #: The name of the default image to use for column items. image = Str(None, update=True) #: The text of a row/column item. text = Property() #: The content of a row/column item (may be any Python value). content = Property() #: The tooltip information for a row/column item. tooltip = Str() #: The context menu for a row/column item. menu = Any() #: The context menu for column header. column_menu = Any() #: List of optional delegated adapters. adapters = List(ITabularAdapter, update=True) # -- Traits Set by the Editor --------------------------------------------- #: The object whose trait is being edited. object = Instance(HasTraits) #: The name of the trait being edited. name = Str() #: The row index of the current item being adapted. row = Int() #: The column index of the current item being adapted. column = Int() #: The current column id being adapted (if any). column_id = Any() #: Current item being adapted. item = Any() #: The current value (if any). value = Any() # -- Private Trait Definitions -------------------------------------------- #: Cache of attribute handlers. cache = Any({}) #: Event fired when the cache is flushed. cache_flushed = Event(update=True) #: The mapping from column indices to column identifiers (defined by the #: :py:attr:`columns` trait). column_map = Property(depends_on="columns") #: The mapping from column indices to column labels (defined by the #: :py:attr:`columns` trait). label_map = Property(depends_on="columns") #: The name of the trait on a row item containing the value to use #: as a row label. If ``None``, the label will be the empty string. row_label_name = Either(None, Str) #: For each adapter, specifies the column indices the adapter handles. adapter_column_indices = Property(depends_on="adapters,columns") #: For each adapter, specifies the mapping from column index to column id. adapter_column_map = Property(depends_on="adapters,columns") # ------------------------------------------------------------------------- # TabularAdapter interface # ------------------------------------------------------------------------- def cleanup(self): """ Clean up the adapter to remove references to objects. """ self.trait_setq(object=None, item=None, value=None) # -- Adapter methods that are sensitive to item type ---------------------- def get_alignment(self, object, trait, column): """ Returns the alignment style to use for a specified column. The possible values that can be returned are: ``'left'``, ``'center'`` or ``'right'``. All table items share the same alignment for a specified column. """ return self._result_for("get_alignment", object, trait, 0, column) def get_width(self, object, trait, column): """ Returns the width to use for a specified column. If the value is <= 0, the column will have a *default* width, which is the same as specifying a width of 0.1. If the value is > 1.0, it is converted to an integer and the result is the width of the column in pixels. This is referred to as a *fixed width* column. If the value is a float such that 0.0 < value <= 1.0, it is treated as the *unnormalized fraction of the available space* that is to be assigned to the column. What this means requires a little explanation. To arrive at the size in pixels of the column at any given time, the editor adds together all of the *unnormalized fraction* values returned for all columns in the table to arrive at a total value. Each *unnormalized fraction* is then divided by the total to create a *normalized fraction*. Each column is then assigned an amount of space in pixels equal to the maximum of 30 or its *normalized fraction* multiplied by the *available space*. The *available space* is defined as the actual width of the table minus the width of all *fixed width* columns. Note that this calculation is performed each time the table is resized in the user interface, thus allowing columns of this type to increase or decrease their width dynamically, while leaving *fixed width* columns unchanged. """ return self._result_for("get_width", object, trait, 0, column) def get_can_edit(self, object, trait, row): """ Returns whether the user can edit a specified row. A ``True`` result indicates that the value can be edited, while a ``False`` result indicates that it cannot. """ return self._result_for("get_can_edit", object, trait, row, 0) def get_drag(self, object, trait, row): """ Returns the value to be *dragged* for a specified row. A result of ``None`` means that the item cannot be dragged. Note that the value returned does not have to be the actual row item. It can be any value that you want to drag in its place. In particular, if you want the drag target to receive a copy of the row item, you should return a copy or clone of the item in its place. Also note that if multiple items are being dragged, and this method returns ``None`` for any item in the set, no drag operation is performed. """ return self._result_for("get_drag", object, trait, row, 0) def get_can_drop(self, object, trait, row, value): """ Returns whether the specified ``value`` can be dropped on the specified row. A value of ``True`` means the ``value`` can be dropped; and a value of ``False`` indicates that it cannot be dropped. The result is used to provide the user positive or negative drag feedback while dragging items over the table. ``value`` will always be a single value, even if multiple items are being dragged. The editor handles multiple drag items by making a separate call to :py:meth:`get_can_drop` for each item being dragged. """ return self._result_for("get_can_drop", object, trait, row, 0, value) def get_dropped(self, object, trait, row, value): """ Returns how to handle a specified ``value`` being dropped on a specified row. The possible return values are: - ``'before'``: Insert the specified ``value`` before the dropped on item. - ``'after'``: Insert the specified ``value`` after the dropped on item. Note there is no result indicating *do not drop* since you will have already indicated that the ``object`` can be dropped by the result returned from a previous call to :py:meth:`get_can_drop`. """ return self._result_for("get_dropped", object, trait, row, 0, value) def get_font(self, object, trait, row, column=0): """ Returns the font to use for displaying a specified row or cell. A result of ``None`` means use the default font; otherwise a toolkit font object should be returned. Note that all columns for the specified table row will use the font value returned. """ return self._result_for("get_font", object, trait, row, column) def get_text_color(self, object, trait, row, column=0): """ Returns the text color to use for a specified row or cell. A result of ``None`` means use the default text color; otherwise a toolkit-compatible color should be returned. Note that all columns for the specified table row will use the text color value returned. """ return self._result_for("get_text_color", object, trait, row, column) def get_bg_color(self, object, trait, row, column=0): """ Returns the background color to use for a specified row or cell. A result of ``None`` means use the default background color; otherwise a toolkit-compatible color should be returned. Note that all columns for the specified table row will use the background color value returned. """ return self._result_for("get_bg_color", object, trait, row, column) def get_image(self, object, trait, row, column): """ Returns the image to display for a specified cell. A result of ``None`` means no image will be displayed in the specified table cell. Otherwise the result should either be the name of the image, or an :py:class:`~pyface.image_resource.ImageResource` object specifying the image to display. A name is allowed in the case where the image is specified in the :py:class:`~traitsui.editors.tabular_editor.TabularEditor` :py:attr:`~traitsui.editors.tabular_editor.TabularEditor.images` trait. In that case, the name should be the same as the string specified in the :py:class:`~pyface.image_resource.ImageResource` constructor. """ return self._result_for("get_image", object, trait, row, column) def get_format(self, object, trait, row, column): """ Returns the Python formatting string to apply to the specified cell. The resulting of formatting with this string will be used as the text to display it in the table. The return can be any Python string containing exactly one old-style Python formatting sequence, such as ``'%.4f'`` or ``'(%5.2f)'``. """ return self._result_for("get_format", object, trait, row, column) def get_text(self, object, trait, row, column): """ Returns a string containing the text to display for a specified cell. If the underlying data representation for a specified item is not a string, then it is your responsibility to convert it to one before returning it as the result. """ return self._result_for("get_text", object, trait, row, column) def get_content(self, object, trait, row, column): """ Returns the content to display for a specified cell. """ return self._result_for("get_content", object, trait, row, column) def set_text(self, object, trait, row, column, text): """ Sets the value for the specified cell. This method is called when the user completes an editing operation on a table cell. The string specified by ``text`` is the value that the user has entered in the table cell. If the underlying data does not store the value as text, it is your responsibility to convert ``text`` to the correct representation used. """ self._result_for("set_text", object, trait, row, column, text) def get_tooltip(self, object, trait, row, column): """ Returns a string containing the tooltip to display for a specified cell. You should return the empty string if you do not wish to display a tooltip. """ return self._result_for("get_tooltip", object, trait, row, column) def get_menu(self, object, trait, row, column): """ Returns the context menu for a specified cell. """ return self._result_for("get_menu", object, trait, row, column) def get_column_menu(self, object, trait, row, column): """ Returns the context menu for a specified column. """ return self._result_for("get_column_menu", object, trait, row, column) # -- Adapter methods that are not sensitive to item type ------------------ def get_item(self, object, trait, row): """ Returns the specified row item. The value returned should be the value that exists (or *logically* exists) at the specified ``row`` in your data. If your data is not really a list or array, then you can just use ``row`` as an integer *key* or *token* that can be used to retrieve a corresponding item. The value of ``row`` will always be in the range: 0 <= row < ``len(object, trait)`` (i.e. the result returned by the adapter :py:meth:`len` method). The default implementation assumes the trait defined by ``object.trait`` is a *sequence* and attempts to return the value at index ``row``. If an error occurs, it returns ``None`` instead. This definition should work correctly for lists, tuples and arrays, or any other object that is indexable, but will have to be overridden for all other cases. """ try: return getattr(object, trait)[row] except: return None def len(self, object, trait): """ Returns the number of row items in the specified ``object.trait``. The result should be an integer greater than or equal to 0. The default implementation assumes the trait defined by ``object.trait`` is a *sequence* and attempts to return the result of calling ``len(object.trait)``. It will need to be overridden for any type of data which for which :py:func:`len` will not work. """ # Sometimes, during shutdown, the object has been set to None. if object is None: return 0 else: return len(getattr(object, trait)) def get_default_value(self, object, trait): """ Returns a new default value for the specified ``object.trait`` list. This method is called when *insert* or *append* operations are allowed and the user requests that a new item be added to the table. The result should be a new instance of whatever underlying representation is being used for table items. The default implementation simply returns the value of the adapter's :py:attr:`default_value` trait. """ return self.default_value def delete(self, object, trait, row): """ Deletes the specified row item. This method is only called if the *delete* operation is specified in the :py:class:`~traitsui.editors.tabular_editor.TabularEditor` :py:attr:`~traitsui.editors.tabular_editor.TabularEditor.operation` trait, and the user requests that the item be deleted from the table. The adapter can still choose not to delete the specified item if desired, although that may prove confusing to the user. The default implementation assumes the trait defined by ``object.trait`` is a mutable sequence and attempts to perform a ``del object.trait[row]`` operation. """ del getattr(object, trait)[row] def insert(self, object, trait, row, value): """ Inserts ``value`` at the specified ``object.trait[row]`` index. The specified ``value`` can be: - An item being moved from one location in the data to another. - A new item created by a previous call to :py:meth:`~TabularAdapter.get_default_value`. - An item the adapter previously approved via a call to :py:meth:`~TabularAdapter.get_can_drop`. The adapter can still choose not to insert the item into the data, although that may prove confusing to the user. The default implementation assumes the trait defined by ``object.trait`` is a mutable sequence and attempts to perform an ``object.trait[row:row] = [value]`` operation. """ getattr(object, trait)[row:row] = [value] def get_column(self, object, trait, index): """ Returns the column id corresponding to a specified column index. """ self.object, self.name = object, trait return self.column_map[index] # -- Property Implementations --------------------------------------------- def _get_drag(self): return self.item def _get_text_color(self): if (self.row % 2) == 1: return self.even_text_color_ or self.default_text_color return self.odd_text_color or self.default_text_color_ def _get_bg_color(self): if (self.row % 2) == 1: return self.even_bg_color_ or self.default_bg_color_ return self.odd_bg_color or self.default_bg_color_ def _get_text(self): return self.get_format( self.object, self.name, self.row, self.column) % self.get_content( self.object, self.name, self.row, self.column) def _set_text(self, value): if isinstance(self.column_id, int): self.item[self.column_id] = self.value else: # Convert value to the correct trait type. try: trait_handler = self.item.trait(self.column_id).handler setattr( self.item, self.column_id, trait_handler.evaluate(self.value), ) except: setattr(self.item, self.column_id, value) def _get_content(self): if isinstance(self.column_id, int): return self.item[self.column_id] return getattr(self.item, self.column_id) # -- Property Implementations --------------------------------------------- @cached_property def _get_column_dict(self): cols = {} for i, value in enumerate(self.columns): if isinstance(value, str): cols.update({value: value}) else: cols.update({value[0]: value[1]}) return cols @cached_property def _get_column_map(self): map = [] for i, value in enumerate(self.columns): if isinstance(value, str): map.append(i) else: map.append(value[1]) return map def get_label(self, section, obj=None): """Override this method if labels will vary from object to object.""" return self.label_map[section] def get_row_label(self, section, obj=None): if self.row_label_name is None: return None rows = getattr(obj, self.name, None) if rows is None: return None item = rows[section] return getattr(item, self.row_label_name, None) @cached_property def _get_label_map(self): map = [] for i, value in enumerate(self.columns): if isinstance(value, str): map.append(value) else: map.append(value[0]) return map @cached_property def _get_adapter_column_indices(self): labels = self.label_map map = [] for adapter in self.adapters: indices = [] for label in adapter.columns: if not isinstance(label, str): label = label[0] indices.append(labels.index(label)) map.append(indices) return map @cached_property def _get_adapter_column_map(self): labels = self.label_map map = [] for adapter in self.adapters: mapping = {} for label in adapter.columns: id = None if not isinstance(label, str): label, id = label key = labels.index(label) if id is None: id = key mapping[key] = id map.append(mapping) return map # -- Private Methods ------------------------------------------------------ def _result_for(self, name, object, trait, row, column, value=None): """ Returns/Sets the value of the specified *name* attribute for the specified *object.trait[row].column* item. """ self.object = object self.name = trait self.row = row self.column = column self.column_id = column_id = self.column_map[column] self.value = value self.item = item = self.get_item(object, trait, row) item_class = item.__class__ key = "%s:%s:%d" % (item_class.__name__, name, column) handler = self.cache.get(key) if handler is not None: return handler() prefix = name[:4] trait_name = name[4:] for i, adapter in enumerate(self.adapters): if column in self.adapter_column_indices[i]: adapter.row = row adapter.item = item adapter.value = value adapter.column = column_id = self.adapter_column_map[i][column] if adapter.accepts: get_name = "%s_%s" % (column_id, trait_name) if adapter.trait(get_name) is not None: if prefix == "get_": handler = lambda: getattr( adapter.trait_set( row=self.row, column=column_id, item=self.item, ), get_name, ) else: handler = lambda: setattr( adapter.trait_set( row=self.row, column=column_id, item=self.item, ), get_name, self.value, ) if adapter.is_cacheable: break return handler() else: if item is not None and hasattr(item_class, "__mro__"): for klass in item_class.__mro__: handler = self._get_handler_for( "%s_%s_%s" % (klass.__name__, column_id, trait_name), prefix, ) or self._get_handler_for( "%s_%s" % (klass.__name__, trait_name), prefix) if handler is not None: break if handler is None: handler = self._get_handler_for( "%s_%s" % (column_id, trait_name), prefix) or self._get_handler_for(trait_name, prefix) self.cache[key] = handler return handler() def _get_handler_for(self, name, prefix): """ Returns the handler for a specified trait name (or None if not found). """ if self.trait(name) is not None: if prefix == "get_": return lambda: getattr(self, name) return lambda: setattr(self, name, self.value) return None @on_trait_change("columns,adapters.+update") def _flush_cache(self): """ Flushes the cache when the columns or any trait on any adapter changes. """ self.cache = {} self.cache_flushed = True
class TableFilter(HasPrivateTraits): """ Filter for items displayed in a table. """ #------------------------------------------------------------------------- # Trait definitions: #------------------------------------------------------------------------- # UI name of this filter (so the user can identify it in the UI) name = Str('Default filter') # Default name that can be automatically overridden _name = Str('Default filter') # A user-readable description of what kind of object satisfies the filter desc = Str('All items') # A callable function that returns whether the passed object is allowed # by the filter allowed = Callable(lambda object: True, transient=True) # Is the filter a template (i.e., non-deletable, non-editable)? template = Bool(False) #------------------------------------------------------------------------- # Class constants: #------------------------------------------------------------------------- # Traits that are ignored by the _anytrait_changed() handler ignored_traits = ['_name', 'template', 'desc'] #------------------------------------------------------------------------- # Traits view definitions: #------------------------------------------------------------------------- traits_view = View( 'name{Filter name}', '_', Include('filter_view'), title='Edit Filter', width=0.2, buttons=[ 'OK', 'Cancel', Action(name='Help', action='show_help', defined_when="ui.view_elements.content['filter_view']" ".help_id != ''") ]) searchable_view = View( [[Include('search_view'), '|[]'], ['handler.status~', '|[]<>'], [ 'handler.find_next`Find the next matching item`', 'handler.find_previous`Find the previous matching item`', 'handler.select`Select all matching items`', 'handler.OK`Exit search`', '-<>' ], '|<>'], title='Search for', kind='livemodal', width=0.25) search_view = Group(Include('filter_view')) filter_view = Group() #------------------------------------------------------------------------- # Returns whether a specified object meets the filter/search criteria: # (Should normally be overridden) #------------------------------------------------------------------------- def filter(self, object): """ Returns whether a specified object meets the filter or search criteria. """ return self.allowed(object) #------------------------------------------------------------------------- # Returns a user readable description of what kind of object will # satisfy the filter: # (Should normally be overridden): #------------------------------------------------------------------------- def description(self): """ Returns a user-readable description of what kind of object satisfies the filter. """ return self.desc #------------------------------------------------------------------------- # Edits the contents of the filter: #------------------------------------------------------------------------- def edit(self, object): """ Edits the contents of the filter. """ return self.edit_traits(view=self.edit_view(object), kind='livemodal') def edit_view(self, object): """ Return a view to use for editing the filter. The ''object'' parameter is a sample object for the table that the filter will be applied to. It is supplied in case the filter needs to extract data or metadata from the object. If the table is empty, the ''object'' argument is None. """ return None #------------------------------------------------------------------------- # 'object' interface: #------------------------------------------------------------------------- def __str__(self): return self.name #------------------------------------------------------------------------- # Event handlers: #------------------------------------------------------------------------- def _anytrait_changed(self, name, old, new): if ((name not in self.ignored_traits) and ((self.name == self._name) or (self.name == ''))): self.name = self._name = self.description()
class ThresholdOp(HasStrictTraits): """Apply a threshold to a cytometry experiment. Attributes ---------- name : Str The operation name. Used to name the new metadata field in the experiment that's created by apply() channel : Str The name of the channel to apply the threshold on. threshold : Float The value at which to threshold this channel. Examples -------- >>> thresh = flow.ThresholdOp() >>> thresh.name = "Y2-A+" >>> thresh.channel = 'Y2-A' >>> thresh.threshold = 0.3 >>> >>> ex3 = thresh.apply(ex2) Alternately, in an IPython notebook with `%matplotlib notebook` >>> h = flow.HistogramView() >>> h.channel = 'Y2-A' >>> h.huefacet = 'Dox' >>> ts = flow.ThresholdSelection(view = h) >>> ts.plot(ex2) >>> ts.interactive = True >>> # .... draw a threshold on the plot >>> thresh = flow.ThresholdOp(name = "Y2-A+", ... channel = "Y2-A", ... thresh.threshold = ts.threshold) >>> ex3 = thresh.apply(ex2) """ # traits id = Constant('edu.mit.synbio.cytoflow.operations.threshold') friendly_id = Constant("Threshold") name = CStr() channel = Str() threshold = CFloat() def apply(self, experiment): """Applies the threshold to an experiment. Parameters ---------- old_experiment : Experiment the experiment to which this op is applied Returns ------- a new experiment, the same as old_experiment but with a new column the same as the operation name. The bool is True if the event's measurement in self.channel is greater than self.threshold; it is False otherwise. """ if not experiment: raise util.CytoflowOpError("No experiment specified") # make sure name got set! if not self.name: raise util.CytoflowOpError("You have to set the gate's name " "before applying it!") # make sure old_experiment doesn't already have a column named self.name if (self.name in experiment.data.columns): raise util.CytoflowOpError( "Experiment already contains a column {0}".format(self.name)) if self.channel not in experiment.channels: raise util.CytoflowOpError( "{0} isn't a channel in the experiment".format(self.channel)) gate = pd.Series(experiment[self.channel] > self.threshold) new_experiment = experiment.clone() new_experiment.add_condition(self.name, "bool", gate) new_experiment.history.append(self.clone_traits()) return new_experiment def default_view(self, **kwargs): return ThresholdSelection(op=self, **kwargs)