# 定義Traits
在Python程序中按照下面的步驟使用Traits庫:
1. 從 enthought.traits.api 中載入你所需要的對象
2. 定義你想使用的traits
3. 從HasTraits類繼承一個新類,在其中使用你定義的traits聲明trait屬性
通常第2、3步是放在一起的,也就是說定義traits的同時定義trait屬性,在本手冊中的大部分例子都是采用這種方式:
```
from enthought.traits.api import HasTraits, Float
class Person(HasTraits):
weight = Float(50.0)
```
這段程序定義了一個從HasTraits類繼承的Person類,在其內部聲明了一個名為weight的trait屬性,其類型為浮點數,初始值為50.0。trait屬性像類的屬性一樣定義,像實例的屬性一樣使用。下面我們來看看如何使用trait屬性:
```
>>> joe = Person()
>>> joe.weight
50.0
>>> joe.weight = 70.5
>>> joe.weight = 70
>>> joe.weight = "89"
Traceback (most recent call last):
File "...trait_handlers.py", line 175, in error value )
TraitError: The 'weight' trait of a Person instance must be a float,
but a value of '89' <type 'str'> was specified.
```
由于joe是Person類的實例,因此它有一個名為weight的trait屬性,并且初始值為50.0。由于weight是使用Float聲明的,我們能將浮點數賦值給它,由于整數可以不丟失精度的轉換為浮點數,因此整數也可以賦值給它。然而,把浮點數賦值給整數trait屬性將會產生錯誤。由于字符串無法轉換為浮點數,因此賦值為字符串產生錯誤,錯誤的提示信息告訴我們它需要浮點數。
有時候我們希望trait屬性能夠自動的進行強制類型轉換,這樣我們就可以將字符串賦值給類型為float的trait屬性,省去了手工轉換的麻煩。這種強制類型轉換的trait屬性都用Casting trait聲明,所有的Casting trait都是以 **C** 開頭的:
```
from enthought.traits.api import HasTraits, CFloat
class Person(HasTraits):
cweight = CFloat(50.0)
```
```
>>> bill = Person()
>>> bill.cweight = "90"
>>> bill.cweight
90.0
>>> bill.cweight = "abc"
Traceback (most recent call last)
...
```
這段程序用CFloat聲明了一個強制類型轉換的trait屬性cweight。我們可以將能轉換為浮點數的字符串"90"賦值給它,但是 "abc" 這樣的字符串賦值仍然會拋出異常。 我們可以想象CFloat的內部處理:它先將傳入的值用內部函數float()進行強制類型轉換,然后把結果賦值給trait屬性。
我們也可以先單獨定義一個traits,然后用它來聲明多個類的多個trait屬性,下面是一個例子:
```
from enthought.traits.api import HasTraits, Range
coefficient = Range(-1.0, 1.0, 0.0)
class quadratic(HasTraits):
c2 = coefficient
c1 = coefficient
c0 = coefficient
x = Range(-100.0, 100.0, 0.0)
```
在這個例子中,我們需要定義多個trait屬性,其類型都為Range(具有取值范圍的浮點值),并且范圍都是-1.0到1.0,初始值為0.0。為了體現代碼重用,我們先用coefficient = Range(-1.0, 1.0, 0.0)定義了一個traits,然后在quadratic類中使用它定義三個trait屬性:c0, c1, c2。
## 預定義的Traits
Traits庫為Python的許多數據類型提供了預定義的trait類型。HasTraits派生的類中用trait類型名直接定義trait屬性,這個類的所有實例都將擁有一個初始化為缺省值的屬性,例如:
```
class Person(HasTraits):
age = Float
```
上面的例子為Person類定義了一個age屬性,其類型為浮點數,并且被初始化為0.0(Float的缺省值)。如果你希望用別的值初始化trait屬性的話,可以把這個值當作參數傳遞給trait類型:
```
age = Float(10.0)
```
幾乎所有的trait類型都是可以帶括號調用的,它可以接受缺省值或者其它的參數;還可以通過關鍵字參數接受元數據。 .. TODO:: 插入元數據鏈接
### 簡單類型
對于每個Python的簡單數據類型都對應兩種trait類型:強制類型和自動轉換類型。它們的區別在于:
* **強制型Trait** : 當這樣trait屬性被賦值為類型不匹配的數據時,會產生錯誤
* **自動型Trait** : 類型不匹配時會自動調用此類型對應的Pyton內置的轉換函數進行類型轉換
| 強制型Trait | 自動型Trait | Python對應的數據類型 | 內置缺省值 | 自動轉換函數 |
| --- | --- | --- | --- | --- |
| Bool | CBool | Boolean | False | bool() |
| Complex | CComplex | Complex number | 0+0j | complex() |
| Float | CFloat | Floating point number | 0.0 | float() |
| Int | CInt | Plain integer | 0 | int() |
| Long | CLong | Long integer | 0L | int() |
| Str | CStr | String | '' | str() |
| Unicode | CUnicode | Unicode | u'' | unicode() |
下面的例子演示了強制型Trait和自動型Trait之間的區別:
```
>>> from enthought.traits.api import HasTraits, Float, CFloat
>>> class Person ( HasTraits ):
... weight = Float
... cweight = CFloat
>>> bill = Person()
>>> bill.weight = 180 # OK, 整數和浮點數匹配(轉換為浮點數而不丟失信息)
>>> bill.cweight = 180 # OK,
>>> bill.weight = '180' # Error, 字符串和浮點數不匹配
>>> bill.cweight = '180' # OK, 調用float('180')轉換為浮點
>>> print bill.cweight
180.0
```
### 其它類型
除了簡單類型以外,Traits庫還定義了許多其他的常用的數據類型。幾乎所有的Trait類型都可以直接使用其名稱或者當作函數調用,并且可以接受多種參數。
* **Any** : 任何對象;
```
Array( [dtype = None, shape = None, value = None, typecode = None, **metadata] )
```
* **Button** : 按鈕類型,通常用來觸發事件,參數都是用來描述界面中的按鈕的樣式;
```
Callable( [value = None, **metadata] )
```
* **CArray** : 可自動轉換類型的numpy數組; 調用的參數和Array相同
* **Class** : Python老式類;
```
Code( [value = "", minlen = 0, maxlen = sys.maxint, regex = "", **metadata] )
```
* **Color** : 界面庫中所采用的顏色對象;
```
CSet( [trait = None, value = None, items = True, **metadata] )
```
* **Constant** : 常量對象,其值不能改變,必須指定初始值;
```
Dict( [key_trait = None, value_trait = None, value = None, items = True, **metadata] )
```
* **Directory** : 表示某個目錄的路徑的字符串;
```
Either( val1*[, *val2, ..., valN, **metadata] )
```
* **Enum** : 枚舉數據,其值可以是候選值中的一個;
```
Event( [trait = None, **metadata] )
```
* **Expression** : Python的表達式對象;
```
File( [value = "", filter = None, auto_set = False, entries = 10, exists = False, **metadata ] )
```
* **Font** : 界面庫中表示字體的對象;
```
class Employee(HasTraits):
manager = self
```
定義了一個Employee類,它有一個manager屬性,其類型為Employee,缺省值為對象本身:
```
>>> e = Employee()
>>> e.manager
<__main__.Employee object at 0x05DB72A0>
>>> e
<__main__.Employee object at 0x05DB72A0>
```
如果用This定義的話,那么缺省值為None。
一般來說,屬性為某個類的實例時可以這樣定義:
```
manager = Instance(Empolyee)
```
但是對于這個例子中,在定義manager屬性時,Empolyee還不存在,因此無法如此定義。如果你喜歡這種方式的話,可以用Instance("Empolyee")來定義,當兩個類的屬性交叉引用時,可以使用這種字符串的方式來定義。
This和self不但可以表示類本身,還可以表示派生的類:
```
>>> from enthought.traits.api import HasTraits, This
>>> class Employee(HasTraits):
... manager = This
...
>>> class Executive(Employee):
... pass
...
>>> fred = Employee()
>>> mary = Executive()
>>> fred.manager = mary
>>> mary.manager = fred
```
#### 列出可能的值
使用Enum可以定義枚舉類型,在Enum的定義中給出所有可能的值,這些值必須是Python的簡單數據類型,例如字符串、整數、浮點數等等,各個可能的值的類型可以不一樣。可以直接將可能的值作為參數,或者將其包在某個list中,第一個值為缺省值:
```
class Items(HasTraits):
count = Enum(None, 0, 1, 2, 3, "many")
# 或者:
# count = Enum([None, 0, 1, 2, 3, "many"])
```
下面是運行結果:
```
>>> item = Items()
>>> item.count = 2
>>> item.count = "many"
>>> item.count = 5
```
如果你希望候選值是可以變化的話,可以用values關鍵字指定定義侯選值的屬性名:
```
class Items(HasTraits):
count_list = List([None, 0, 1, 2, 3, "many"])
count = Enum(values="count_list")
```
我們定義一個count_list列表,然后在Enum定義中用values關鍵字指定候選值為count_list屬性。
```
>>> item = Items()
>>> item.count = 5
Traceback (most recent call last)
#... 略去錯誤提示,此錯誤提示無法顯示侯選值列表
>>> item.count_list.append(5)
>>> item.count = 5
>>> item.count
5
```
## Trait的元數據
Trait對象可以有元數據屬性,這些屬性保存在HasTraits對象的trait字典中,為了解釋什么是trait字典和元數據,讓我們先來看一個例子:
```
from enthought.traits.api import *
class MetadataTest(HasTraits):
i = Int(99)
s = Str("test", desc="a string trait property")
test = MetadataTest()
```
在IPython中運行了上面的程序之后,我們對test進行如下操作:
> ```
> >>> test.traits()
> {'i': <enthought.traits.traits.CTrait object at 0x05D44EA0>,
> 'trait_added': <enthought.traits.traits.CTrait object at 0x05D17CE8>,
> 's': <enthought.traits.traits.CTrait object at 0x05D44EF8>,
> 'trait_modified': <enthought.traits.traits.CTrait object at 0x05D17C90>}
>
> ```
>
> ```
> >>> test.trait("i")
> <enthought.traits.traits.CTrait object at 0x05D44EA0>
>
> ```
>
> ```
> >>> test.trait("s").desc
> 'a string trait property'
>
> ```
通過調用HasTraits對象的traits方法可以得到一個包含其所有trait對象的字典。請注意,trait屬性和trait對象是兩個東西:
* **trait屬性** : 用于保存實際的值,例如:test.i, test.s
* **trait對象** : 用于描述trait屬性,例如:test.trait("i"), test.trait("s")
也就是說對于每一個trait屬性都有一個與之對應的trait對象描述它。而元數據就是保存在trait對象中的額外的描述屬性用的數據。我們看到test的trait對象除了i和s之外,還有trait_added和trait_modified,著兩個在HasTraits類中定義。
元數據可以分為三類:
* **內部屬性** : 這些屬性是trait對象自帶的,只讀不能寫
* **識別屬性** : 這些屬性是可以自由地設置的,它們可以改變trait的一些行為
* **任意屬性** : 用戶自己添加的屬性,需要自己編寫程序使用它們
### 內部元數據
下面的這些元數據屬性在Traits庫內部使用,用戶可以讀取它們的值。
* **array** : 是否是數組,不是數組的trait對象沒有此屬性
* **default** : 對應的trait屬性的缺省值。也就是說: trait屬性的缺省值是保存在與其對應的trait對象的元數據屬性default中的:
```
>>> test.trait("i").default
99
```
* **default_kind** : 一個描述缺省值的類型的字符串,其值可以是 value, list, dict, self, factory, method等:
```
>>> test.trait("i").default_kind
'value'
```
* **inner_traits** : 內部的trait對象,在List, Dict等中使用,表示List和Dict內部對象的類型
* **trait_type** : 描述trait屬性的數據類型的對象。下面的例子中,得到的就是定義trait屬性時所用的Int類的對象:
```
>>> test.trait("i").trait_type
<enthought.traits.trait_types.Int object at 0x05DBD2D0>
```
* **type** : trait屬性的分類,可以是constant, delegate, event, property, trait
```
>>> test.trait("i").type
'trait'
```
### 能識別的元數據
下面的元數據屬性不是預定義的,但是可以被HasTraits對象使用:
* **desc** : 描述trait屬性用的字符串,在界面中使用
* **editor** : 指定一個生成界面時用何種TraitEditor編輯對應的trait屬性
* **label** : 界面中的trait屬性編輯器的標簽中的字符串
* **rich_compare** : 指定判斷trait屬性值發生變化的方式。True(缺省)表示按值比較;Flase表示按照對象指針比較
* **trait_value** : 指定trait屬性是否接受TraitValu類的對象,缺省值為False。當它為True時,將trait屬性設置為TraitValue(),將重置trait屬性值為缺省值。
* **transient** : 指定當對象被保存(持久化)時是否保存此trait屬性值。對于大多數trait屬性來說,它的缺省值都是True。
- 用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的分形圖