<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 設計自己的Trait編輯器 在前面的章節中我們知道,每種trait屬性都對應有缺省的trait編輯器,如果在View中不指定編輯器的話,將使用缺省的編輯器構成界面。每個編輯器都可以對應有多個后臺,目前支持的后臺界面庫有pyQt和wxPython。每種編輯器都可以有四種樣式:simple, custom, text, readonly。 traitsUI為我們提供了很豐富的編輯器庫,以至于我們很少有自己設計編輯器的需求,然而如果我們能方便地設計自己的編輯器,將能制作出更加專業的程序界面。 本章節將簡要介紹trait編輯器的工作原理;并且制作一個新的trait編輯器,用以顯示matplotlib提供的繪圖控件;然后以此控件制作一個通用的繪制CSV文件數據圖像的小工具。 ## Trait編輯器的工作原理 我們先來看下面這個小程序,它定義了一個TestStrEditor類,其中有一個名為test的trait屬性,其類型為Str,在view中用Item定義要在界面中顯示test屬性,但是沒有指定它所使用的編輯器(通過editor參數)。當執行t.configure_traits()時,traits庫將自動為我們挑選文本編輯框控件作為test屬性的編輯器: ``` from enthought.traits.api import * from enthought.traits.ui.api import * class TestStrEditor(HasTraits): test = Str view = View(Item("test")) t = TestStrEditor() t.configure_traits() ``` ![](https://box.kancloud.cn/2016-03-19_56ed1bae75de9.png) 使用文本編輯框控件編輯test屬性 Traits庫的路徑 下面的介紹需要查看traits庫的源程序,因此首先你需要知道它們在哪里: **traits**: site-packages\Traits-3.2.0-py2.6-win32.egg\enthought\traits, 以下簡稱 %traits% **traitsUI**: site-packages\Traits-3.2.0-py2.6-win32.egg\enthought\traits\UI, 以下簡稱 %ui% **wx后臺界面庫**: site-packages\TraitsBackendWX-3.2.0-py2.6.egg\enthought\traitsui\wx, 以下簡稱 %wx% Str對象的缺省編輯器通過其create_editor方法獲得: ``` >>> from enthought.traits.api import * >>> s = Str() >>> ed = s.create_editor() >>> type(ed) <class 'enthought.traits.ui.editors.text_editor.ToolkitEditorFactory'> >>> ed.get() {'auto_set': True, 'custom_editor_class': <class 'enthought.traits.ui.wx.text_editor.CustomEditor'>, 'enabled': True, 'enter_set': False, 'evaluate': <enthought.traits.ui.editors.text_editor._Identity object at 0x0427F1B0>, 'evaluate_name': '', 'format_func': None, 'format_str': '', 'invalid': '', 'is_grid_cell': False, 'mapping': {}, 'multi_line': True, 'password': False, 'readonly_editor_class': <class 'enthought.traits.ui.wx.text_editor.ReadonlyEditor'>, 'simple_editor_class': <class 'enthought.traits.ui.wx.text_editor.SimpleEditor'>, 'text_editor_class': <class 'enthought.traits.ui.wx.text_editor.SimpleEditor'>, 'view': None} ``` create_editor方法的源代碼可以在%traits%trait_types.py中的BaseStr類的定義中找到。create_editor方法得到的是一個text_editor.ToolkitEditorFactory類: ``` enthought.traits.ui.editors.text_editor.ToolkitEditorFactory ``` 在%ui%editorstext_editor.py中你可以找到它的定義,它繼承于EditorFactory類。EditorFactory類的代碼在%ui%editor_factory.py中。EditorFactory類是Traits編輯器的核心,通過它和后臺界面庫聯系起來。讓我們來詳細看看EditorFactory類中關于控件生成方面的代碼: ``` class EditorFactory ( HasPrivateTraits ): # 下面四個屬性描述四個類型的編輯器的類 simple_editor_class = Property custom_editor_class = Property text_editor_class = Property readonly_editor_class = Property # 用simple_editor_class創建實際的控件 def simple_editor ( self, ui, object, name, description, parent ): return self.simple_editor_class( parent, factory = self, ui = ui, object = object, name = name, description = description ) # 這是類的方法,它通過類的以及父類自動找到與其匹配的后臺界面庫中的控件類 @classmethod def _get_toolkit_editor(cls, class_name): editor_factory_classes = [factory_class for factory_class in cls.mro() if issubclass(factory_class, EditorFactory)] for index in range(len( editor_factory_classes )): try: factory_class = editor_factory_classes[index] editor_file_name = os.path.basename( sys.modules[factory_class.__module__].__file__) return toolkit_object(':'.join([editor_file_name.split('.')[0], class_name]), True) except Exception, e: if index == len(editor_factory_classes)-1: raise e return None # simple_editor_class屬性的get方法,獲取屬性值 def _get_simple_editor_class(self): try: SimpleEditor = self._get_toolkit_editor('SimpleEditor') except: SimpleEditor = toolkit_object('editor_factory:SimpleEditor') return SimpleEditor ``` EditorFactory的對象有四個屬性保存后臺編輯器控件的類:simple_editor_class, custom_editor_class, text_editor_class, readonly_editor_class。例如前面例子中的ed對象的simple_editor_class為&lt;class 'enthought.traits.ui.wx.text_editor.SimpleEditor'&gt;,我們看到它用的是wx后臺界面庫中的text_editor中的SimpleEditor類,稍后我們將看看其內容。 EditorFactory是通過其類方法_get_toolkit_editor計算出所要用后臺界面庫中的類的。由于_get_toolkit_editor是類方法,它的第一個參數cls就是類本身。當調用text_editor.ToolkitEditorFactory._get_toolkit_editor()時,cls就是text_editor.ToolkitEditorFactory類。通過調用cls.mro獲得cls以及其所有父類,然后一個一個地查找,從后臺界面庫中找到與之匹配的類,這個工作由toolkit_object函數完成。其源代碼可以在%ui%toolkit.py中找到。 因為后臺界面庫中的類的組織結構和traits.ui是一樣的,因此不需要額外的配置文件,只需要幾個字符串替代操作就可以將traits.ui中的EditorFactory類和后臺界面庫中的實際的編輯器類聯系起來。下圖顯示了traits.ui中的EditorFactory和后臺界面庫的關系。 ![](https://box.kancloud.cn/2016-03-19_56ed1bae85b53.png) traits.ui中的EditorFactory和后臺界面庫的關系 wx后臺界面庫中定義了所有編輯器控件,在 %wx%text_editor.py 中你可以找到產生文本框控件的類 text_editor.SimpleEditor。類名表示了控件的樣式:simple, custom, text, readonly,而其文件名(模塊名)則表示了控件的類型。下面是 text_editor.SimpleEditor的部分代碼: ``` class SimpleEditor ( Editor ): # Flag for window styles: base_style = 0 # Background color when input is OK: ok_color = OKColor # Function used to evaluate textual user input: evaluate = evaluate_trait def init ( self, parent ): """ Finishes initializing the editor by creating the underlying toolkit widget. """ factory = self.factory style = self.base_style self.evaluate = factory.evaluate self.sync_value( factory.evaluate_name, 'evaluate', 'from' ) if (not factory.multi_line) or factory.password: style &= ~wx.TE_MULTILINE if factory.password: style |= wx.TE_PASSWORD multi_line = ((style & wx.TE_MULTILINE) != 0) if multi_line: self.scrollable = True if factory.enter_set and (not multi_line): control = wx.TextCtrl( parent, -1, self.str_value, style = style | wx.TE_PROCESS_ENTER ) wx.EVT_TEXT_ENTER( parent, control.GetId(), self.update_object ) else: control = wx.TextCtrl( parent, -1, self.str_value, style = style ) wx.EVT_KILL_FOCUS( control, self.update_object ) if factory.auto_set: wx.EVT_TEXT( parent, control.GetId(), self.update_object ) self.control = control self.set_tooltip() ``` 真正產生控件的程序是在init方法中,此方法在產生界面時自動被調用,注意方法名是init,不要和對象初始化方法\_\_init\_\_搞混淆了。 ## 制作matplotlib的編輯器 Enthought的官方繪圖庫是采用Chaco,不過如果你對matplotlib庫更加熟悉的話,將matplotlib的繪圖控件嵌入TraitsUI界面中將是非常有用的。下面先來看一下嵌入matplotlib控件的完整源代碼: ``` # -*- coding: utf-8 -*- # file name: mpl_figure_editor.py import wx import matplotlib # matplotlib采用WXAgg為后臺,這樣才能將繪圖控件嵌入以wx為后臺界面庫的traitsUI窗口中 matplotlib.use("WXAgg") from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas from matplotlib.backends.backend_wx import NavigationToolbar2Wx from enthought.traits.ui.wx.editor import Editor from enthought.traits.ui.basic_editor_factory import BasicEditorFactory class _MPLFigureEditor(Editor): """ 相當于wx后臺界面庫中的編輯器,它負責創建真正的控件 """ scrollable = True def init(self, parent): self.control = self._create_canvas(parent) self.set_tooltip() print dir(self.item) def update_editor(self): pass def _create_canvas(self, parent): """ 創建一個Panel, 布局采用垂直排列的BoxSizer, panel中中添加 FigureCanvas, NavigationToolbar2Wx, StaticText三個控件 FigureCanvas的鼠標移動事件調用mousemoved函數,在StaticText 顯示鼠標所在的數據坐標 """ panel = wx.Panel(parent, -1, style=wx.CLIP_CHILDREN) def mousemoved(event): panel.info.SetLabel("%s, %s" % (event.xdata, event.ydata)) panel.mousemoved = mousemoved sizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(sizer) mpl_control = FigureCanvas(panel, -1, self.value) mpl_control.mpl_connect("motion_notify_event", mousemoved) toolbar = NavigationToolbar2Wx(mpl_control) sizer.Add(mpl_control, 1, wx.LEFT | wx.TOP | wx.GROW) sizer.Add(toolbar, 0, wx.EXPAND|wx.RIGHT) panel.info = wx.StaticText(parent, -1) sizer.Add(panel.info) self.value.canvas.SetMinSize((10,10)) return panel class MPLFigureEditor(BasicEditorFactory): """ 相當于traits.ui中的EditorFactory,它返回真正創建控件的類 """ klass = _MPLFigureEditor if __name__ == "__main__": from matplotlib.figure import Figure from enthought.traits.api import HasTraits, Instance from enthought.traits.ui.api import View, Item from numpy import sin, cos, linspace, pi class Test(HasTraits): figure = Instance(Figure, ()) view = View( Item("figure", editor=MPLFigureEditor(), show_label=False), width = 400, height = 300, resizable = True) def __init__(self): super(Test, self).__init__() axes = self.figure.add_subplot(111) t = linspace(0, 2*pi, 200) axes.plot(sin(t)) Test().configure_traits() ``` 此程序的運行結果如下: ![](https://box.kancloud.cn/2016-03-19_56ed1baea865a.png) 在TraitsUI界面中嵌入的matplotlib繪圖控件 由于我們的編輯器沒有simple等四種樣式,也不會放到wx后臺界面庫的模塊中,因此不能采用上節所介紹的自動查找編輯器類的辦法。traits.ui為我們提供一個一個方便的類來完成這些操作:BasicEditorFactory。它的源程序可以在 %ui%basic_editor_factory.py中找到。下面是其中的一部分: ``` class BasicEditorFactory ( EditorFactory ): klass = Any def _get_simple_editor_class ( self ): return self.klass ... ``` 它通過重載EditorFactory中的simple_editor_class屬性,直接返回創建控件的庫klass。MPLFigureEditor繼承于BasicEditorFactory,指定創建控件的類為_MPLFigureEditor。 和text_editor.SimpleEditor一樣,從Editor類繼承,在_MPLFigureEditor類的init方法中,創建實際的控件。因為Editor類中有一個update_editor方法,在其對應的trait屬性改變是會被調用,而我們的繪圖控件不需要這個功能,所以重載update_editor,讓它不做任何事情。 matplotlib中,在創建FigureCanvas時需要指定與其對應的Figure對象: ``` mpl_control = FigureCanvas(panel, -1, self.value) ``` 這里self.value就是這個Figure對象,它在MVC的模型類Test中被定義為: ``` figure = Instance(Figure, ()) ``` 控件類可以通過self.value獲得與其對應的模型類中的對象。因此_MPLFigureEditor中的self.value和Test類中的self.figure是同一個對象。 _create_canvas方法中的程序編寫和在一個標準的wx窗口中添加控件是一樣的,界面庫相關的細節不是本書的重點,因此不再詳細解釋了。讀者可以參照matplotlib和wxPython的相應文檔。 ## CSV數據繪圖工具 下面用前面介紹的matplotlib編輯器制作一個CSV數據繪圖工具。用此工具打開一個CSV數據文檔之后,可以繪制多個X-Y坐標圖。用戶可以自由地添加新的坐標圖,修改坐標圖的標題,選擇坐標圖的X軸和Y軸的數據。 下面是此程序的界面截圖: ![](https://box.kancloud.cn/2016-03-19_56ed1baed4140.png) CSV數據繪圖工具的界面 圖中以標簽頁的形式顯示多個繪圖,用戶可以從左側的數據選擇欄中選擇X軸和Y軸的數據。標簽頁可以自由的拖動,構成上下左右分欄,并且可以隱藏左側的數據選擇欄: ![](https://box.kancloud.cn/2016-03-19_56ed1baeebc0d.png) 使用可調整DOCK的多標簽頁界面方便用戶對比數據 由于繪圖控件是matplotlib所提供的,因此平移、縮放、保存文件等功能也一應俱全。由于所有的界面都是采用TraitsUI設計的,因此主窗口既可以用來單獨顯示,也可以嵌入到一個更大的界面中,運用十分靈活。 下面是完整的源程序,運行時需要和mpl_figure_editor.py放在一個文件夾下。包括注釋程序一共約170行,編寫時間少于一小時。 ``` # -*- coding: utf-8 -*- from matplotlib.figure import Figure from mpl_figure_editor import MPLFigureEditor from enthought.traits.ui.api import * from enthought.traits.api import * import csv class DataSource(HasTraits): """ 數據源,data是一個字典,將字符串映射到列表 names是data中的所有字符串的列表 """ data = DictStrAny names = List(Str) def load_csv(self, filename): """ 從CSV文件讀入數據,更新data和names屬性 """ f = file(filename) reader = csv.DictReader(f) self.names = reader.fieldnames for field in reader.fieldnames: self.data[field] = [] for line in reader: for k, v in line.iteritems(): self.data[k].append(float(v)) f.close() class Graph(HasTraits): """ 繪圖組件,包括左邊的數據選擇控件和右邊的繪圖控件 """ name = Str # 繪圖名,顯示在標簽頁標題和繪圖標題中 data_source = Instance(DataSource) # 保存數據的數據源 figure = Instance(Figure) # 控制繪圖控件的Figure對象 selected_xaxis = Str # X軸所用的數據名 selected_items = List # Y軸所用的數據列表 clear_button = Button(u"清除") # 快速清除Y軸的所有選擇的數據 view = View( HSplit( # HSplit分為左右兩個區域,中間有可調節寬度比例的調節手柄 # 左邊為一個組 VGroup( Item("name"), # 繪圖名編輯框 Item("clear_button"), # 清除按鈕 Heading(u"X軸數據"), # 靜態文本 # X軸選擇器,用EnumEditor編輯器,即ComboBox控件,控件中的候選數據從 # data_source的names屬性得到 Item("selected_xaxis", editor= EnumEditor(name="object.data_source.names", format_str=u"%s")), Heading(u"Y軸數據"), # 靜態文本 # Y軸選擇器,由于Y軸可以多選,因此用CheckBox列表編輯,按兩列顯示 Item("selected_items", style="custom", editor=CheckListEditor(name="object.data_source.names", cols=2, format_str=u"%s")), show_border = True, # 顯示組的邊框 scrollable = True, # 組中的控件過多時,采用滾動條 show_labels = False # 組中的所有控件都不顯示標簽 ), # 右邊繪圖控件 Item("figure", editor=MPLFigureEditor(), show_label=False, width=600) ) ) def _name_changed(self): """ 當繪圖名發生變化時,更新繪圖的標題 """ axe = self.figure.axes[0] axe.set_title(self.name) self.figure.canvas.draw() def _clear_button_fired(self): """ 清除按鈕的事件處理 """ self.selected_items = [] self.update() def _figure_default(self): """ figure屬性的缺省值,直接創建一個Figure對象 """ figure = Figure() figure.add_axes([0.05, 0.1, 0.9, 0.85]) #添加繪圖區域,四周留有邊距 return figure def _selected_items_changed(self): """ Y軸數據選擇更新 """ self.update() def _selected_xaxis_changed(self): """ X軸數據選擇更新 """ self.update() def update(self): """ 重新繪制所有的曲線 """ axe = self.figure.axes[0] axe.clear() try: xdata = self.data_source.data[self.selected_xaxis] except: return for field in self.selected_items: axe.plot(xdata, self.data_source.data[field], label=field) axe.set_xlabel(self.selected_xaxis) axe.set_title(self.name) axe.legend() self.figure.canvas.draw() class CSVGrapher(HasTraits): """ 主界面包括繪圖列表,數據源,文件選擇器和添加繪圖按鈕 """ graph_list = List(Instance(Graph)) # 繪圖列表 data_source = Instance(DataSource) # 數據源 csv_file_name = File(filter=[u"*.csv"]) # 文件選擇 add_graph_button = Button(u"添加繪圖") # 添加繪圖按鈕 view = View( # 整個窗口分為上下兩個部分 VGroup( # 上部分橫向放置控件,因此用HGroup HGroup( # 文件選擇控件 Item("csv_file_name", label=u"選擇CSV文件", width=400), # 添加繪圖按鈕 Item("add_graph_button", show_label=False) ), # 下部分是繪圖列表,采用ListEditor編輯器顯示 Item("graph_list", style="custom", show_label=False, editor=ListEditor( use_notebook=True, # 是用多標簽頁格式顯示 deletable=True, # 可以刪除標簽頁 dock_style="tab", # 標簽dock樣式 page_name=".name") # 標題頁的文本使用Graph對象的name屬性 ) ), resizable = True, height = 0.8, width = 0.8, title = u"CSV數據繪圖器" ) def _csv_file_name_changed(self): """ 打開新文件時的處理,根據文件創建一個DataSource """ self.data_source = DataSource() self.data_source.load_csv(self.csv_file_name) del self.graph_list[:] def _add_graph_button_changed(self): """ 添加繪圖按鈕的事件處理 """ if self.data_source != None: self.graph_list.append( Graph(data_source = self.data_source) ) if __name__ == "__main__": csv_grapher = CSVGrapher() csv_grapher.configure_traits() ``` 程序中已經有比較詳細的注釋,這里就不再重復。如果你對traits庫的某項用法還不太了解的話,可以直接查看其源代碼,代碼中都有詳細的注釋。下面是幾個比較重點的部分: * 整個程序的界面處理都只是組裝View對象,看不到任何關于控件操作的代碼,因此大大地節省了程序的開發時間。 * 通過配置141行的ListEditor,使其用標簽頁的方式顯示graph_list中的每個元素,以此管理多個Graph對象。 * 在43行中,Graph類用HSplit將其數據選擇部分和繪圖控件部分分開,HSplit提供的更改左右部分的比例和隱藏的功能。 * 本書寫作時所采用的traitsUI庫版本為3.2,如果在標簽頁標題中輸入中文,會出現錯誤,這是因為TraitsUI中還有些代碼對unicode的支持不夠,希望日后會有所改善。目前可以通過分析錯誤提示信息,修改TraitsUI庫的源代碼,只需要將下面提示中的770行中的str改為unicode既可以修復。 > ``` > &gt;&gt;&gt; from visual import * > > ``` 之后就可以隨心所欲的調用visual庫通過的函數。需要注意的是如果你關閉了visual彈出的場景窗口的話,ipython對話也隨之結束。如果你需要關閉場景窗口可以用下面的語句: ``` >>> scene.visible = False ``` ![](https://box.kancloud.cn/2016-03-19_56ed1baf2d568.png) 在IPython中交互式地觀察visual的運行結果 上圖是用IPython交互式的使用visual的一個例子,可以看到通過IPython能夠控制多個場景窗口。 * [場景窗口](visual_manual_display.html) * [控制場景窗口](visual_manual_display.html#id2) * [控制照相機](visual_manual_display.html#id3)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看