# TraitsUI-輕松制作用戶界面
Python有著豐富的界面開發庫,除了缺省安裝的Tkinter以外,wxPython、pyQt4等都是非常優秀的界面開發庫。但是它們有一個共同的問題:需要開發者掌握眾多的API函數,許多細節,例如配置控件的屬性、位置以及事件響應都需要開發者一一處理。
在開發科學計算程序時,我們希望快速實現一個夠用的界面,讓用戶能夠交互式的處理數據,而又不希望在界面制作上花費過多的精力。以traits為基礎、以Model-View-Controller為設計思想的TraitUI庫就是實現這一理想的最佳伴侶。
## 缺省界面
TraitsUI是一套建立在Traits庫基礎上的用戶界面庫。它和Traits緊密相連,如果你已經設計好了一個繼承于HasTraits的類的話,那么直接調用其configure_traits方法,系統將會使用TraitsUI自動生成一個界面,以供用戶交互式地修改對象的trait屬性。讓我們先來看下面這個例子:
```
from enthought.traits.api import HasTraits, Str, Int
class SimpleEmployee(HasTraits):
first_name = Str
last_name = Str
department = Str
employee_number = Str
salary = Int
sam = SimpleEmployee()
sam.configure_traits()
```
此程序創建一個SimpleEmployee類的對象sam,然后調用sam.configure_traits顯示出如下的缺省界面:

自動生成的SimpleEmployee類的對話框
可以看到此界面是自動根據trait屬性生成。所有的屬性都以文本框的形式編輯,并且每個文本框前面都有一個文字標簽,其文字根據trait屬性名自動生成:第一個字母變為大寫,所有的下劃線變為空格。最下面為我們提供了OK和Cancel按鈕以確定或者取消對trait屬性值的修改。
salary屬性雖然和其它屬性一樣都采用文本框進行編輯,但是由于salary屬性定義為Int類型,所以它將檢查非法輸入,并以紅色背景警示,鼠標左擊Salary標簽,將彈出salary屬性相關的詳細說明,由于我們沒有設置此說明,系統缺省給出salary所能接受的值的類型。

界面中的每個屬性編輯器都有詳細說明,并且能檢查非法輸入
我們連一行界面相關的代碼都沒有寫,卻能得到這樣一個已經夠實用的界面,應該還是很令人滿意的吧。為了人工控制界面的設計和布局,就需要我們添加自己的代碼了。
## 自定義界面
下面的程序在前面的基礎上自定義了一個視圖對象view1,然后將此對象傳遞給configure_traits方法,于是界面就按照視圖中描述的那樣生成了:
```
# -*- coding: utf-8 -*-
from enthought.traits.api import HasTraits, Str, Int
from enthought.traits.ui.api import View, Item
class SimpleEmployee(HasTraits):
first_name = Str
last_name = Str
department = Str
employee_number = Str
salary = Int
view1 = View(
Item(name = 'department', label=u"部門", tooltip=u"在哪個部門干活"),
Item(name = 'last_name', label=u"姓"),
Item(name = 'first_name', label=u"名"))
sam = SimpleEmployee()
sam.configure_traits(view=view1)
```
選擇后臺界面庫
用traits.ui庫創建的界面可以選擇后臺界面庫,目前支持的有qt4和wx兩種。在啟動程序時添加 -toolikt qt4 或者 -toolikt wx 選擇使用何種界面庫生成界面。本文中全部使用wx作為后臺界面庫。

通過label和tooltip手工指定屬性編輯器的標簽和說明
有關界面視圖的對象都在traits.ui庫中,所以首先從其中載入View和Item。View用來生成視圖,而Item則用來描述視圖中的項目(控件)。程序中,用Item依次創建三個視圖項目,都作為參數傳遞給View,于是所生成的界面中按照參數的順序顯示控件,而不是按照trait屬性名排序了。
### Item對象
Item對象是視圖的基本組成單位,每個Item描述界面中的中的一個控件,通常都是用來顯示HasTraits對象中的某一個trait屬性。每個Item由一系列的關鍵字參數來進行配置,這些參數對Item的內容、表現以及行為進行描述。其中最重要的一個參數就是name。我們看到name參數的值都配置為SimpleEmployee類的trait屬性名,于是Item就知道到哪里去尋找真正要顯示的值了。可以看出視圖與數據是通過屬性名聯系起來的。剩下的兩個參數label和tooltip設置Item在界面中的一些顯示相關的屬性。Item對象還有很多屬性其它屬性,請參考TraitsUI的用戶手冊,或者在iPython中輸入Item??直接查看其源代碼。如果你查看了Item的源代碼的話,你就會發現,原來Item的這些屬性也都是用trait定義的:
```
class Item ( ViewSubElement ):
""" An element in a Traits-based user interface.
"""
# 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
```
除了Item之外,TraitsUI庫還定義了下面幾個Item的子類:
* Label
* Heading
* Spring
這些類用來協助View的布局,因此不需要和某個trait屬性關聯。
### Group對象
前面的例子中,我們通過把三個Item對象傳遞給View,創建了一個控件垂直排列的布局。然而在真正的界面開發中,需要更加高級的布局方式,例如,將一組相關的元素組織在一起,放到一個組中,我們可以為此組添加標簽,定義組的幫助文本,通過設置組的屬性使組類的元素同時有效或無效。在TraitUI中,這樣的組的功能通過Group對象實現,讓我們來修改一下前面的例子:
```
# -*- coding: utf-8 -*-
from enthought.traits.api import HasTraits, Str, Int
from enthought.traits.ui.api import View, Item, Group
class SimpleEmployee(HasTraits):
first_name = Str
last_name = Str
department = Str
employee_number = Str
salary = Int
bonus = Int
view1 = View(
Group(
Item(name = 'employee_number', label=u'編號'),
Item(name = 'department', label=u"部門", tooltip=u"在哪個部門干活"),
Item(name = 'last_name', label=u"姓"),
Item(name = 'first_name', label=u"名"),
label = u'個人信息',
show_border = True
),
Group(
Item(name = 'salary', label=u"工資"),
Item(name = 'bonus', label=u"獎金"),
label = u'收入',
show_border = True
)
)
sam = SimpleEmployee()
sam.configure_traits(view=view1)
```
此程序的運行效果如下:

分標簽頁顯示兩個Group的內容
我們分別創建兩個Group傳遞給View,每個Group中仍然通過Item創建控件,通過Group的關鍵字參數指定其label和show_border屬性。由于View中的所有內容都是Group,它自動地將兩個Group放到Tab中,對兩個Group進行分標簽顯示。
如果我們希望能同時看到兩個Group的話,可以另外再創建一個Group將這兩個Group包括起來:
```
view2 = View( Group( view1.content ) )
```
這里我們創建視圖view2,它包括一個Group,此Group的內容則直接使用view1的內容(也就是那兩個Group)。當然也可以把view1中的內容復制進去:
```
view2 = View(Group(
Group(
Item(name = 'employee_number', label=u'編號'),
Item(name = 'department', label=u"部門", tooltip=u"在哪個部門干活"),
Item(name = 'last_name', label=u"姓"),
Item(name = 'first_name', label=u"名"),
label = u'個人信息',
show_border = True
),
Group(
Item(name = 'salary', label=u"工資"),
Item(name = 'bonus', label=u"獎金"),
label = u'收入',
show_border = True
)
))
```
然后我們將view2傳遞給configure_traits,用view2顯示界面:
```
sam.configure_traits(view=view2)
```

豎排顯示兩個Group的內容
在創建Group時,我們可以通過設置其orientation和layout等屬性,改變Group的內容呈現方式。由于某些設置會經常用到,因此還提供了專門的Group子類重載這些屬性的缺省值。例如下面是從Group類繼承的HSplit類的代碼:
```
class HSplit ( Group ):
# ... ...
layout = 'split'
orientation = 'horizontal'
```
HSplit對象將其所包括的內容按照水平排列,并且在每兩個子內容之間添加一個可調整的分隔條,HSplit和如下的代碼是等價的:
```
Group( ... , layout = 'split', orientation = 'horizontal')
```
為了正確顯示分隔條,其子內容中需要有一個具有scrollable屬性,如下面的代碼(省略Item定義等部分)所示:
```
Group(orientation= 'horizontal')
```
* **HFlow** : 內容水平排列,當超過水平寬度時,將自動換行:
```
Group(orientation= 'horizontal', layout='split')
```
* **Tabbed** : 內容分標簽頁顯示:
```
Group(orientation= 'vertical')
```
* **VFlow** : 內容垂直排列,當超過垂直高度時,將自動換列:
```
Group(orientation= 'vertical', layout='flow', show_labels=False)
```
* **VFold** : 內容垂直排列,可折疊 :
```
Group(orientation= 'vertical', layout='fold', show_labels=False)
```
* **VGrid** : 按照兩列的網格進行垂直排列 :
```
Group(orientation= 'vertical', columns=2)
```
* **VSplit** : 內容垂直排列,中間插入分隔條:
```
Group(orientation= 'vertical', layout='split')
```
## 配置視圖
前面介紹了如何使用Item和Group等類組織窗口界面中的內容,這一節我們來看看如何配置窗口本身的屬性。
### 視圖類型
通過kind屬性可以修改View對象的顯示類型:
* 'modal' : 模式窗口, 非即時更新
* 'live' : 非模式窗口,即時更新
* 'livemodal' : 模式窗口,即時更新
* 'nonmodal' : 非模式窗口,非即時更新
* 'wizard' : 向導類型
* 'panel' : 嵌入到其它窗口中的面板,即時更新,非模式
* 'subpanel'
其中 'modal', 'live', 'livemodal', 'nonmodal' 四種類型的View都將采用窗口顯示其內容。所謂模式窗口,表示此窗口關閉之前,程序中的其它窗口都不能被激活。而即時更新則是指當窗口中的控件內容改變時,修改會立即反應到窗口所對應的模型數據上,非即時更新的窗口則會復制模型數據,所有的改變在模型副本上進行,只有當用戶確定修改(通常通過OK或者Apply按鈕)時,才會修改原始數據。
'wizard'由一系列特定的向導窗口組成,屬于模式窗口,并且即時更新數據。
'panel'和'subpanel' 則是嵌入到窗口中的面板,panel可以擁有自己的命令按鈕,而subpanel則沒有命令按鈕。
### 命令按鈕
在對話框中經常可以看到 OK, Cacel, Apply 之類的按鈕,我們稱之為命令按鈕,它們完成所有對話框窗口都共同的操作。在TraitsUI中,這些按鈕可以通過View對象的buttons屬性進行設置,其值為要顯示的按鈕列表。
TraitsUI定義了UndoButton, ApplyButton, RevertButton, OKButton, CancelButton等六個標準的命令按鈕,每個按鈕對應一個名字,在指定buttons屬性時,可以使用按鈕的類名或者其對應的名字。與按鈕類對應的名字就是類名除去Button,例如UndoButton對應為"Undo"。
在 enthought.tratis.ui.menu 中還預定義了一些命令按鈕列表,方便直接使用:
```
OKCancelButtons = ``[OKButton, CancelButton ]``
ModalButtons = ``[ ApplyButton, RevertButton, OKButton, CancelButton, HelpButton ]``
LiveButtons = ``[ UndoButton, RevertButton, OKButton, CancelButton, HelpButton ]``
```
- 用Python做科學計算
- 軟件包的安裝和介紹
- NumPy-快速處理數據
- SciPy-數值計算庫
- matplotlib-繪制精美的圖表
- Traits-為Python添加類型定義
- TraitsUI-輕松制作用戶界面
- Chaco-交互式圖表
- TVTK-三維可視化數據
- Mayavi-更方便的可視化
- Visual-制作3D演示動畫
- OpenCV-圖像處理和計算機視覺
- Traits使用手冊
- 定義Traits
- Trait事件處理
- 設計自己的Trait編輯器
- Visual使用手冊
- 場景窗口
- 聲音的輸入輸出
- 數字信號系統
- FFT演示程序
- 頻域信號處理
- Ctypes和NumPy
- 自適應濾波器和NLMS模擬
- 單擺和雙擺模擬
- 分形與混沌
- 關于本書的編寫
- 最近更新
- 源程序集
- 三角波的FFT演示
- 在traitsUI中使用的matplotlib控件
- CSV文件數據圖形化工具
- NLMS算法的模擬測試
- 三維標量場觀察器
- 頻譜泄漏和hann窗
- FFT卷積的速度比較
- 二次均衡器設計
- 單擺擺動周期的計算
- 雙擺系統的動畫模擬
- 繪制Mandelbrot集合
- 迭代函數系統的分形
- 繪制L-System的分形圖