# 第九章 通過對話框讓用戶選擇
1. [使用模式對話框工作](#A.2BT391KGohXw9b.2BYvdaEZd5U9c-)
2. [使用標準對話框](#A.2BT391KGgHUcZb.2BYvdaEY-)
3. [創建向導](#A.2BUhte.2BlQRW.2Fw-)
4. [顯示啟動提示](#A.2BZj55OlQvUqhj0Hk6-)
5. [使用驗證器(validator)來管理對話框中的數據](#A.2BT391KJqMi8FWaP8I-validator.2B.2FwlnZXuhdAZb.2BYvdaEZOLXaEZXBjbg-)
6. [本章小結](#A.2BZyx64FwPftM-)
模式對話框用于與用戶進行快速的交互或在用戶可以執行程序的下一步之前,對話框中的信息必須被輸入的時候。在`wxPython`中,有幾個標準的函數用來顯示基本的模式對話框。這些對話框包括警告框,單行文本域,和從列表中選擇。在隨后的部分,我們將給你展示這些對話框,以及如何使用這些預定義的函數來減輕你的工作量。
### 如何創建一個模式對話框?
模式對話框阻塞了別的窗口部件接收用戶事件,直到該模式對話框被關閉;換句話說,在它存在期間,用戶一直被置于對話模式中。如圖9.1所示,你不能總是根據外觀來區別對話框和框架。在`wxPython`中,對話框與框架間的區別不是基于它們的外觀的,而主要是它們處理事件的辦法的實質。
**圖9.1** **一個模式對話框**

對話框的創建和配置與框架稍微有些不同。例9.1顯示了產生圖9.1的代碼。所顯示的對話框上的按鈕被敲擊后,該對話框就關閉了,并且一條消息被輸出到`stdout(`標準輸出)。
**例9.1** **定義一個模式對話框**
```
import wx
class SubclassDialog(wx.Dialog):
def __init__(self):#初始化對話框
wx.Dialog.__init__(self, None, -1, 'Dialog Subclass',
size=(300, 100))
okButton = wx.Button(self, wx.ID_OK, "OK", pos=(15, 15))
okButton.SetDefault()
cancelButton = wx.Button(self, wx.ID_CANCEL, "Cancel",
pos=(115, 15))
if __name__ == '__main__':
app = wx.PySimpleApp()
app.MainLoop()
dialog = SubclassDialog()
result = dialog.ShowModal()#顯示模式對話框
if result == wx.ID_OK:
print "OK"
else:
print "Cancel"
dialog.Destroy()
```
與前一章的`wx.Frame`的例子比較,這兒有兩個需要注意的事情。在`__init__`方法中,按鈕是被直接添加到`wx.Dialog`,而非`wx.Panel`。面板在對話框中的使用比在框架中少的多,部分原因是因為對話框與框架相比傾向簡單化,但主要是因為`wx.Panel`特性(標準系統背景和`tab`鍵橫向切換控件焦點)已經默認存在于`wx.Dialog`中。
要顯示為模式對話框,使用`ShowModal()`方法。這與用于框架的的`Show()`方法在對程序的執行上有不同的作用。在調用`ShowModal()`后你的應用程序將處于等待中,直到對話框被取消。
模式將保持到對話框方法`EndModal(retCode)`被調用,該方法關閉對話框。參數`retCode`是由`ShowModal()`方法返回的一個整數值。典型的,應用程序利用這個返回值來知道用戶是如何關閉對話框的,以控制以后的操作。但是結束這個模式并沒有銷毀或甚至關閉對話框。保持對話框的存在可能是一件好事,因為這意味你可以把用戶選擇的信息存儲為對話框實例的數據成員,并且即使在對話框被關閉后也能從對話框重新獲得那些信息。在接下來的部分,我們將看一些我們使用對話框處理程序中用戶輸入的數據的例子。
由于例9.1中沒有定義事件處理器,你可能會驚奇對話框是如何響應按鈕敲擊的。這個行為已經定義在`wxDialog`中了。有兩個預定義的`wxPython` `ID`號,它們在對話框中有特殊的意思。當對話框中的一個使用`wx.ID_OK` `ID`的`wx.Button`被敲擊時,模式就結束了,對話框也關閉了,`wx.ID_OK`就是`ShowModal()`調用返回的值。同樣,一個使用`wx.ID_CANCEL` `ID`的按鈕做相同的事情,但是`ShowModal()`的返回值是`wx.ID_CANCEL`。
例9.1顯示了處理模式對話框的一個典型的方法。在對話框被調用后,返回值被用作`if`語句中的測試。在這種情況下,我們簡單地打印結果,在更復雜的例子中,`wx.ID_OK`將執行用戶在對話框中所要求的動作,如打開文件或選擇顏色。
典型的,你在完成對對話框的使用后,你應該顯式地銷毀它。這通知C++對象它應該自我銷毀,然后這將使得它的`Python`部分被作為垃圾回收。如果你希望在你的應用程序中,以后再次使用該對話框時不重建它,以加速對話框的響應時間,那么你可以保持對該對話框的一個引用,并當你需要再次激活它時,簡單地調用它的`ShowModal()`方法。當應用程序準備退出時,確保已銷毀了它,否則`MainLoop()`將仍將它作為一個存在的頂級窗口,并且程序將不能正常退出。
### 如何創建一個警告框?
經由一個對話框與用戶交互的最簡單的三個辦法分別是:`wx.MessageDialog`,它是一個警告框、`wx.TextEntryDialog`,它提示用戶去輸入一些短的文本、`wx.SingleChoiceDialog`,它使用戶能夠從一個有效選項列表中進行選擇。在接下來的三個小節中,我們將論這些簡單的對話框。
消息對話框顯示一個短的消息,并使用戶通過按下按鈕來作響應。通常,消息框被用作去顯示重要的警告、`yes`/`no`問題、或詢問用戶是否繼續某種操作。圖9.2顯示了一個典型的消息框。
**圖9.2**

使用消息框是十分的簡單。例9.2顯示了創建一個消息框的兩種辦法。
**例9.2** **創建一個消息框**
```
import wx
if __name__ == "__main__":
app = wx.PySimpleApp()
# 方法一,使用類
dlg = wx.MessageDialog(None, "Is this explanation OK?",
'A Message Box',
wx.YES_NO | wx.ICON_QUESTION)
retCode = dlg.ShowModal()
if (retCode == wx.ID_YES):
print "yes"
else:
print "no"
dlg.Destroy()
# 方法二,使用函數
retCode = wx.MessageBox("Is this way easier?", "Via Function",
wx.YES_NO | wx.ICON_QUESTION)
```
例9.2創建了兩個消息框,一個在另一個的后面。這第一個方法是創建類`wx.MessageDialog`的一個實例,并使用`ShowModal()`來顯示它。
**使用`wx.MessageDialog`類**
使用`wx.MessageDialog`的構造函數,你可以設置對話框的消息和按鈕,構造函數如下:
`wx.MessageDialog(parent`, `message`, `caption`="`Message` `box`",
* `style`=`wx.OK` | `wx.CANCEL`, `pos`=`wx.DefaultPosition)`
`message`參數是實際顯示在對話框中的文本。如果消息字符串包含\n字符,那么文本將在此換行。`caption`參數顯示在對話框的標題欄中。`pos`參數使你可以指定對話框顯示在屏幕上的位置——在微軟`Windows`下,這個參數將被忽略。
`wx.MessageDialog`的樣式標記分為兩類。第一類控制顯示在對話框中的按鈕。表9.1說明了這些樣式。
**表9.1 `wx.MessageDialog`的按鈕樣式**
| | |
| --- | --- |
| `wx.CANCEL` | 包括一個`cancel`(取消)按鈕。這個按鈕有一個`ID`值`wx.ID_CANCEL`。 |
| `wx.NO_DEFAULT` | 在一個`wx.YES_NO`對話框中,`No`(否)按鈕是默認的。 |
| `wx.OK` | 包括一個`OK`按鈕,這個按鈕有一個`ID`值`wx.ID_OK`。 |
| `wx.YES_DEFAULT` | 在一個`wx.YES_NO`對話框中,`Yes`按鈕是默認的。這是默認行為。 |
| `wx.YES_NO` | 包括`Yes`和`No`按鈕,各自的`ID`值分別是`wx.ID_YES`和`wx.ID_NO`。 |
第二套樣式標記控制緊挨著消息文本的圖標。它們顯示在表9.2中。
**表9.2 `wx.MessageDialog`的圖標樣式**
| | |
| --- | --- |
| `wx.ICON_ERROR` | 表示一個錯誤的圖標。 |
| `wx.ICON_EXCLAMATION` | 表示警告的圖標。 |
| `wx.ICON_HAND` | 同`wx.ICON_ERROR`。 |
| `wx.ICON_INFORMATION` | 信息圖標,字母i。 |
| `wx.ICON_QUESTION` | 問號圖標。 |
最后,你可以使用樣式`wx.STAY_ON_TOP`將對話框顯示在系統中任何其它窗口的上面,包括系統窗口和`wxPython`應用程序窗口。
你在例9.2所見到的,對話框通過使用`ShowModal()`被調用。根據所顯示的按鈕,返回的結果是以下值之一:`wx.ID_OK`, `wx.ID_CANCEL`,`wx.ID_YES`, 或 `wx.ID_NO`。如同其它對話框的情況,你通常使用這些值來控制程序的執行。
**使用`wx.MessageBox()`函數**
例9.2中的#1顯示了一個調用消息框的更簡短的方法。這個便利的函數`wx.MessageBox()`創建對話框,調用`ShowModal()`,并且返回下列值之一:`wx.YES`, `wx.NO`, `wx.CANCEL`, 或 `wx.OK`。函數的形式比`MessageDialog`的構造函數更簡單,如下所示:
```
wx.MessageBox(message, caption="Message", style=wx.OK)
```
在這個例子中,參數`message`, `caption`, `style`的意思和構造函數中的相同,你可以使用所有相同的樣式標記。正如我們貫穿本章將看到的,在`wxPython`預定義的幾個對話框都有便利的函數。在你為單一的使用創建對話框的時候,你的選擇有一個優先的問題。如果你計劃束縛住對話框以便多次調用它,那么你可能會優先選擇去實例化對象以便你能夠束縛該引用,而不使用函數的方法,盡管這對于這些簡單的對話框來說,所節約的時間可以忽略不計。
要在你的消息框中顯示大量的文本(例如,終端用戶許可證的顯示),你可以使用`wxPython`特定的類`wx.lib.dialogs.ScrolledMessageDialog`,它包含如下的構造函數:
```
wx.lib.dialogs.ScrolledMessageDialog(parent, msg, caption,
pos=wx.wxDefaultPosition, size=(500,300))
```
這個對話框不使用本地消息框控件,它根據別的`wxPython`窗口部件來創建一個對話框。它只顯示一個`OK`按鈕,并且沒有更多的樣式信息。
### 如何從用戶得到短的文本?
這第二個簡單類型的對話框是`wx.TextEntryDialog`,它被用于從用戶那里得到短的文本輸入。它通常用在在程序的開始時要求用戶名或密碼的時候,或作為一個數據輸入表單的基本替代物。圖9.3顯示了一個典型的文本對話框。
**圖9.3** **文本輸入標準對話框**

例9.3顯示了產生圖9.3的代碼
**例9.3**
```
import wx
if __name__ == "__main__":
app = wx.PySimpleApp()
dialog = wx.TextEntryDialog(None,
"What kind of text would you like to enter?",
"Text Entry", "Default Value", style=wx.OK|wx.CANCEL)
if dialog.ShowModal() == wx.ID_OK:
print "You entered: %s" % dialog.GetValue()
dialog.Destroy()
```
在前一小節,我們創建了一個對話框類的實例,在這里,我們要用到的對話框類是`wx.TextEntryDialog`。該類的構造函數比簡單消息對話框要復雜一些:
```
wx.TextEntryDialog(parent, message, caption="Please enter text",
defaultValue="", style=wx.OK | wx.CANCEL | wx.CENTRE,
pos=wx.DefaultPosition)
```
`message`參數是顯示在對話框中的文本提示,而`caption`顯示在標題欄中。`defaultValue`顯示在文本框中的默認值。`style`可以包括`wx.OK`和`wx.CANCEL`,它顯示適當的按鈕。
幾個`wx.TextCtrl`的樣式也可以用在這里。最有用的應該是`wx.TE_PASSWORD`,它掩飾所輸入的真實密碼。你也可以使用`wx.TE_MULTILINE`來使用戶能夠在對話框中輸入多行文本,也可以使用`wx.TE_LEFT`, `wx.TE_CENTRE`, 和 `wx.TE_RIGHT`來調整所輸入的文本的對齊位置。
例9.3的最后顯示了在文本框和對話框之間的另一區別。用戶所輸入的信息被存儲在該對話框實例中,并且以后必須應用程序獲取。在這種情況下,你可以使用對話框的`GetValue()`方法來得到該值。記住,如果用戶按下`Cancel`(取消)去退出該對話框,這意味他們不想去使用他所鍵入的值。你也可以在程序中使用`SetValue()`方法來設置該值。
下面這些是使用文本對話框的便利函數:
1、`wx.GetTextFromUser()` 2、`wx.GetPasswordFromUser()` 3、`wx.GetNumberFromUser()`
其中和例9.3的用處最近似的是`wx.GetTextFromUser()`:
```
wx.GetTextFromUser(message, caption="Input text",
default_value="", parent=None)
```
這里的`message`, `caption`, `default_value`, 和 `parent`與`wx.TextEntryDialog`的構造函數中的一樣。如果用戶按下`OK`,該函數的返回值是用戶所輸入的字符串。如果用戶按下`Cancel`,該函數返回空字符串。
如果你希望用戶輸入密碼,你可以使用`wx.GetPasswordFromUser()`函數:
```
wx.GetPasswordFromUser(message, caption="Input text",
default_value="", parent=None)
```
這里的參數意義和前面的一樣。用戶的輸入被顯示為星號,如果用戶按下`OK`,該函數的返回值是用戶所輸入的字符串。如果用戶按下`Cancel`,該函數返回空字符串。
最后,你可以使用`wx.GetNumberFromUser()`要求用戶輸入一個數字:
```
wx.GetNumberFromUser(message, prompt, caption, value, min=0,
max=100, parent=None)
```
這里的參數的意義有一點不同,`message`是顯在`prompt`上部的任意長度的消息,`value`參數是默認顯示在文本框中的長整型值。`min`和`max`參數為用戶的輸入限定一個范圍。如果用戶按下`OK`按鈕退出的話,該方法返回所輸入的值,并轉換為長整型。如果這個值不能轉換為一個數字,或不在指定的范圍內,那么該函數返回-1,這意味如果你將該函數用于負數的范圍的話,你可能要考慮一個轉換的方法。
### 如何用對話框顯示選項列表?
如果給你的用戶一個空的文本輸入域顯得太自由了,那么你可以使用`wx.SingleChoiceDialog`來讓他們在一組選項中作單一的選擇。圖9.4顯示了一個例子。
**圖9.4** **一個單選對話框**

例9.4顯示了產生圖9.4的代碼
**例9.4** **顯示一個選擇列表對話框**
```
import wx
if __name__ == "__main__":
app = wx.PySimpleApp()
choices = ["Alpha", "Baker", "Charlie", "Delta"]
dialog = wx.SingleChoiceDialog(None, "Pick A Word", "Choices",
choices)
if dialog.ShowModal() == wx.ID_OK:
print "You selected: %s\n" % dialog.GetStringSelection()
dialog.Destroy()
```
`wx.SingleChoiceDialog`的構造函數如下所示:
```
wx.SingleChoiceDialog(parent, message, caption, choices,
clientData=None, style=wx.OK | wx.CANCEL | wx.CENTRE,
pos=wx.DefaultPosition)
```
`message`和`caption`參數的意義與前面的一樣,分別顯示在對話框和標題欄中。`choices`參數要求一個字符串的列表,它們是你呈現在對話框中的選項。`style`參數有三個項,這是默認的,分別是`OK`按鈕、`Cancle`按鈕和使對話框在屏幕中居中。`centre`選項和`pos`參數在`Windows`操作系統上不工作。
如果你想在用戶看見對話框之前,設置它的默認選項,使用`SetSelection(selection)`方法。參數`selection`是選項的索引值,而非實際選擇的字符串。在用戶選擇了一個選項后,你即可以使用`GetSelection()`——它返回所選項的索引值,也可以使用`GetStringSelection()`——它返回實際所選的字符串,來得到它。
有兩個用于單選對話框的便利函數。第一個是`wx.GetSingleChoice`,它返回用戶所選的字符串:
`wx.GetSingleChoice(message`, `caption`, `aChoices`, `parent`=`None)`
參數`message`, `caption`, 和`parent`的意義和`wx.SingleChoiceDialog`構造函數的一樣。`aChoices`參數是選項的列表。如果用戶按下`OK`,則返回值是所選的字符串,如果用戶按下`Cancel`,則返回值是空字符串。這意味如果空字符是一個有效的選擇的話,那么你就不該使用這個函數。
第二個是`wx.GetSingleChoiceIndex:`
```
wx.GetSingleChoiceIndex(message, caption, aChoices, parent=None)
```
這個函數與第一個有相同的參數,但是返回值不同。如果用戶按下`OK`,則返回值是所選項的索引,如果用戶按下`Cancel`,則返回值是-1。
### 如何顯示進度條?
在許多程序中,程序需要自己做些事情而不受用戶輸入的干擾。這時就需要給用戶一些可見的顯示,以表明程序正在做一些事情及完成的進度。在`wxPython`中,這通常使用一個進度條來管理,如圖9.5所示。
**圖9.5**

例9.5顯示了產生圖9.5的代碼
**例9.5** **生成一個進度條**
```
import wx
if __name__ == "__main__":
app = wx.PySimpleApp()
progressMax = 100
dialog = wx.ProgressDialog("A progress box", "Time remaining", progressMax,
style=wx.PD_CAN_ABORT | wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME)
keepGoing = True
count = 0
while keepGoing and count progressMax:
count = count + 1
wx.Sleep(1)
keepGoing = dialog.Update(count)
dialog.Destroy()
```
進度條的所有選項在構造函數中被設置,構造函數如下:
```
wx.ProgressDialog(title, message, maximum=100, parent=None,
style=wx.PD_AUTO_HIDE | wx.PD_APP_MODAL)
```
這些參數不同于其它對話框的。參數`title`被放置在窗口的標題欄,`message`被顯示在對話框中。`maximum`是你用來顯示進度計數的最大值。
表9.3 列出了特定于`wx.ProgressDialog`六個樣式,它們影響進度條的行為。
**表9.3 `wx.ProgressDialog`的樣式**
| | |
| --- | --- |
| `wx.PD_APP_MODAL` | 如果設置了這個樣式,進度條對整個應用程序是模式的,這將阻塞所有的用戶事件。如果沒有設置這個樣式,那么進度條僅對它的父窗口是模式的。 |
| `wx.PD_AUTO_HIDE` | 進度條將自動隱藏自身直到它達到它的最大值。 |
| `wx.PD_CAN_ABORT` | 在進度條上放上一個`Cancel`按鈕,以便用戶停止。如何響應來自該對話框的取消將在以后說明。 |
| `wx.PD_ELAPSED_TIME` | 顯示該對話框已經出現了多長時間。 |
| `wx.PD_ESTIMATED_TIME` | 顯示根據已花的時間、當前的計數值和計數器的最大值所估計出的完成進度所需的總時間。 |
| `wx.PD_REMAINING_TIME` | 顯示要完成進度所估計的剩余時間,或(所需總時間-已花時間)。 |
要使用進度條,就要調用它的唯一的方法`Update(value`,`newmsg`="")。`value`參數是進度條的新的內部的值,調用`update`將導致進度條根據新的計數值與最大計算值的比例重繪。如果使用可選的參數`newmsg`,那么進度條上的文本消息將變為該字符串。這讓你可以給用戶一個關于當前進度的文本描述。
這個`Update()`方法通常返回`True`。但是,如果用戶通過`Cancel`按鈕已經取消了該對話框,那么下次的`Update()`將返回`False`。這是你響應用戶的取消請求的機會。要檢測用戶的取消請求,我們建議你盡可能頻繁地`Update()`。
## 使用標準對話框
大多數操作系統都為像文件選擇、字體選擇和顏色選擇這些任務提供了標準對話框。這為平臺提供了一致感觀。你也可以使用來自于`wxPython`的這些對話框,它們也為你的應用程序提供了一致的感觀。如果你使用`wxPython`,那么它為你提供了類似的對話框,即使所在的平臺沒有提供系統對話框。
### 如何使用文件選擇對話框?
在`wxPython`中,`wx.FileDialog`為主流的平臺使用本地操作系統對話框,對其它操作系統使用非本地相似的外觀。微軟`Windows`的版本如圖9.6所示。
**圖9.6**

你可以設置文件對話框開始在任一目錄,你也可以使用通配符過濾來限制去顯示某種文件類型。例9.6顯示了一個基本的例子。
**例9.6** **使用`wx.FileDialog`**
```
import wx
import os
if __name__ == "__main__":
app = wx.PySimpleApp()
wildcard = "Python source (*.py)|*.py|" \
"Compiled Python (*.pyc)|*.pyc|" \
"All files (*.*)|*.*"
dialog = wx.FileDialog(None, "Choose a file", os.getcwd(),
"", wildcard, wx.OPEN)
if dialog.ShowModal() == wx.ID_OK:
print dialog.GetPath()
dialog.Destroy()
```
文件對話框是我們這章已見過的最復雜的對話框,它有幾個屬性可以通過編程的方式讀寫。它的構造函數使得你能夠設置它的一些屬性:
```
wx.FileDialog(parent, message="Choose a file", defaultDir="",
defaultFile="", wildcard="*.*", style=0,
pos=wx.DefaultPosition)
```
`message`參數出現在窗口的標題欄中。`defaultDir`參數告訴對話框初始的時候顯示哪個目錄。如果這個參數為空或表示的目錄不存在,那么對話框開始在當前目錄。`defaultFile`是默認保存為的文件。`wildcard`參數使你可以基于給定的模式來過濾列表,使用通常的*和?作為通配符。通配符可以是單個模式,如*.`py`或格式如 描述 | 模式 | 描述 | 模式 的一系列模式——類似于例9.6中所用。
"`Python` `source` (*.`py)`|*.`py`|`Compiled` `Python` (*.`pyc)`|*.`pyc`|
* `All` `files` (*.*)|*.*"
如果有一個多個項目的模式,那么它們顯示在圖9.6所示的下拉菜單中。`pos`參數不保證被基本的系統所支持。
**選擇一個文件**
`wx.FileDialog`的兩個最重要的樣式標記是`wx.OPEN`和`wx.SAVE`,它們表明對話框的類型并影響對話框的行為。
用于打開文件的對話框有兩個標記,它們進一步影響對話框的行為。`wx.HIDE_READONLY`標記灰化復選框,使用戶以只讀模式打開文件。`wx.MULTIPLE`標記使用戶可以在一個目錄中選擇打開多個文件。
保存文件對話框有一個有用的標記`wx.OVERWRITE_PROMPT`,它使得保存文件時,如果有相同的文件存在,則提示用戶是否覆蓋。
兩種文件對話框都可以使用`wx.CHANGE_DIR`標記。當使用這個標記時,文件的選擇也可改變應用程序的工作目錄為所選文件所在的目錄。這使得下次文件對話框打開在相同的目錄,而不需要應用程序再在別處存儲該值。
和本章迄今為止我人們所見過的其它對話框不一樣,文件對話框的屬性`directory`,`filename`, `style`, `message`, 和`wildcard`是可以通過方法來得到和設置的。這些方法使用`Get`/`Set`命名習慣。
在用戶退出對話框后,如果返回值是`wx.OK`,那么你可以使用方法`GetPath()`來得到用戶的選擇,該函數的返回值是字符串形式的文件全路徑名。如果對話框是一個使用了`wx.MULTIPLE`標記的打開對話框,則用`GetPaths()`代替`GetPath()`。該方法返回路徑字符串的一個`Python`列表。如果你需要知道在用戶選擇時使用了下拉菜單中的哪個項,你可以使用`GetFilterIndex()`,它返回項目的索引。要通過編程改變索引,使用方法`SetFilterIndex()`。
這后面的是一個使用文件對話框的便利函數:
```
wx.FileSelector(message, default_path="", default_filename="",
default_extension="", wildcard="*.*'', flags=0, parent=None,
x=-1, y=-1)
```
`message`, `default_path`, `default_filename`, 和 `wildcard`參數意義與構造函數的基本相同,盡管參數的名字不同。`flags`參數通常被稱作`style`,`default_extension`參數是保存為文件時默認的后綴(如果用戶沒有指定后綴的情況下)。如果用戶按下`OK`,返回值是字符串形式的路徑名,如果用戶按下`Cancel`則返回一個空字符串。
**選擇一個目錄**
如果用戶想去選擇一個目錄而非一個文件,使用`wx.DirDialog`,它呈現一個目錄樹的視圖,如圖9.7所示。
這個目錄選擇器比文件對話框簡單些。例9.7顯示了相關的代碼。
**例9.7** **顯示一個目錄選擇對話框**
```
import wx
if __name__ == "__main__":
app = wx.PySimpleApp()
dialog = wx.DirDialog(None, "Choose a directory:",
style=wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
if dialog.ShowModal() == wx.ID_OK:
print dialog.GetPath()
dialog.Destroy()
```
**圖9.7**

這個對話框的所有的功能幾乎都在構造函數中:
```
wx.DirDialog(parent, message="Choose a directory", defaultPath="",
style=0, pos = wx.DefaultPosition, size = wx.DefaultSize,
name="wxDirCtrl")
```
由于`message`參數顯示在對話框中,所以你不需要一個鉤子去改變標題欄。`defaultPath`告訴對話框選擇的默認路徑,如果它為空,那么對話框顯示文件系統的根目錄。`pos`和`size`參數在微軟`Windows`下被忽略,`name`參數在所有的操作系統下都被忽略。該對話框的樣式標記`wx.DD_NEW_DIR_BUTTON`給對話框一個用于創建目錄的一個按鈕。這個標記在老版的微軟`Windows`中不工作。
`wx.DirDialog`類的`path`, `message`, 和`style`屬性都有相應的`get`*和`set`*方法。你可以使用`GetPath()`方法來在對話框被調用后獲取用戶的選擇。這個對話框也有一個便利的函數:
```
wx.DirSelector(message=wx.DirSelectorPromptStr, default_path="",
style=0, pos=wxDefaultPosition, parent=None)
```
所有的參數和前面的構造函數相同。如果`OK`被按下,則該函數返回所選擇的字符串形式的目錄名,如果按下`Cancel`,則返回空字符串。
### 如何使用字體選擇對話框?
在`wxPython`中,字體選擇對話框與文件對話框是不同的,因為它使用了一個單獨的幫助類來管理它所呈現的信息。圖9.8顯示了微軟`Windows`版的字體對話框。
例9.8 顯示了產生圖9.8的代碼,并且與前面的對話框例子看起來也有些不同。
**例9.8** **字體對話框**
```
import wx
if __name__ == "__main__":
app = wx.PySimpleApp()
dialog = wx.FontDialog(None, wx.FontData())
if dialog.ShowModal() == wx.ID_OK:
data = dialog.GetFontData()
font = data.GetChosenFont()
colour = data.GetColour()
print 'You selected: "%s", %d points\n' % (
font.GetFaceName(), font.GetPointSize())
dialog.Destroy()
```
**圖9.8**

`wx.FontDialog` 的構造函數比前面的那些簡單的多:
`wx.FontDialog(parent`, `data)`
你不能為該對話框設置一個消息或標題,并且被通常作為樣式標記傳遞的信息被包含在`data`參數中,該參數是類`wx.FontData`。`wx.FontData`類自己只有一個有用的方法:`GetFontData()`,該方法返回字體數據的實例。
`wx.FontData`的實例使你能夠設置管理字體對話框顯示的值,并且也能夠容納用戶輸入的信息。例如,在例9.8中的代碼調用了`wx.FontData`實例的兩個`get`*方法來確定所選字體的細節。`wx.FontData`的構造函數沒有參數——所有的屬性必須通過使用表9.4中的方法來設置。
**表9.4** **`wx.FontData`的方法**
| | |
| --- | --- |
| `GetAllowSymbols()`,`SetAllowSymbols(allowSymbols)` | 決定是否在對話框中僅顯示符號字體(如`dingbats`)。參數是布爾值。只在`Windows`中有意義。該屬性的初始值是`True`。 |
| `GetChosenFont()`,`SetChosenFont(font)` | 以`wx.Font`對象的方式返回用戶所選的字體。如果用戶選擇了取消,那么返回`None`。`wx.Font`類將在第12章作更詳細的討論。 |
| `GetColour()`,`SetColour(colour)` | 返回在對話框的顏色選擇部分所選的顏色。`set`*方法使你可以預先設定默認值。`get`*方法返回一個`wx.Colour`實例。`set`*方法中的`colour`只能是一個`wx.Colour`或一個顏色的字符串名。該屬性的初始值是`black`。 |
| `GetEnableEffects()`,`EnableEffects(enable)` | 在該對話框的`Windows`版本中,該屬性控制是否顯示字體的所選顏色、中間是否有直線通過、是否帶下劃線等特性。 |
| `GetInitialFont()`,`SetInitialFont(font)` | 返回對話框初值的字體值(即當前所用的字體)。這個屬性可以在對話框顯示之前通過應用程序顯式的來設置。它的初始值是`None`。 |
| `SetRange(min`, `max)` | 設置字體尺寸(磅)的有效范圍。僅用于微軟的`Windows`系統。最初值是0~0,意味沒有范圍的限制。 |
||`GetShowHelp()`,`SetShowHelp()`||如果為`True`,那么該對話框的微軟`Windows`版本將顯示一個幫助按鈕。初始值為`False`||。
有一個使用字體對話框的便利的函數,它回避了`wx.FontData`類:
```
wx.GetFontFromUser(parent, fontInit)
```
`fontInit`參數是`wx.Font`的一個實例,它用作對話框的初始值。該函數的返回值是一個`wx.Font`實例。如用戶通過`OK`關閉了對話框,則方法`wx.Font.Ok()`返回`True`,否則返回`False`。
### 如何使用顏色對話框?
顏色對話框類似于字體對話框,因為它使用了一個外部的數據類來管理它的信息。圖9.9顯示了這類對話框的微軟版本。
例9.9顯示了生成該對話框的代碼,它幾乎與前面的字體對話框相同。
**例9.9**
```
import wx
if __name__ == "__main__":
app = wx.PySimpleApp()
dialog = wx.ColourDialog(None)
dialog.GetColourData().SetChooseFull(True)
if dialog.ShowModal() == wx.ID_OK:
data = dialog.GetColourData()
print 'You selected: %s\n' % str(data.GetColour().Get())
dialog.Destroy()
```
**圖9.9**

用于顏色選擇器的`wxPython`的類是`wx.ColourDialog`。它的構造函數很簡單,沒有太多的參數:
`wx.ColourDialog(parent`, `data`=`None)`
`data`參數是類`wx.ColourData`的實例,它比相應字體的更簡單。它只包含默認的沒有參數的構造函數和后面的三個屬性:
1、`GetChooseFull`/`SetChooseFull(flag)`:僅在微軟`Windows`下工作。當設置后,將顯示完整的對話框,包括自定義顏色選擇器。如果不設置,則自定義顏色選擇器不被顯示。
2、`GetColour`/`SetColour(colour)`:當圖表被關閉后,調用`get`*來看用戶的選擇。最初它被設置為`black`。如果在對話框顯示之前設置了它,那么對話框最初顯示為該顏色。
3、`GetCustomColour(i)`/`SetCustomColour(i`, `colour)`:根據自定義的顏色數組中的索引i來返回或設置元素。i位于[0,15]之間。初始時,所有的自定義顏色都是白色。
一個回避了`wx.ColorData`的使用顏色對話框的便利函數是:
`wx.GetColourFromUser(parent`, `colInit)`
`colInit`是`wx.Colour`的一個實例,并且當對話框顯示時它是對話框的初始的值。函數的返回值也是一個`wx.Colour`的實例。如果用戶通過`OK`關閉了對話框,那么方法`wx.Colour.OK()`返回`True`。如果用戶通過`Cancel`關閉了對話框,那么方法`wx.Colour.OK()`返回`False`。
### 如何使用戶能夠瀏覽圖像?
如果你在你的程序中做圖形處理,那么在他們瀏覽文件樹時使用縮略圖是有幫助的。用于該目的的`wxPython`對話框被稱為`wx.lib.imagebrowser.ImageDialog`。圖9.10顯示了一個例子。
例9.10顯示了用于該圖像瀏覽對話框的簡單的代碼。
**例9.10** **創建一個圖像瀏覽對話框**
```
import wx
import wx.lib.imagebrowser as imagebrowser
if __name__ == "__main__":
app = wx.PySimpleApp()
dialog = imagebrowser.ImageDialog(None)
if dialog.ShowModal() == wx.ID_OK:
print "You Selected File: " + dialog.GetFile()
dialog.Destroy()
```
**圖9.10**

`wx.lib.imagebrowser.ImageDialog`類是十分簡單的,并有相對較少的選項供程序員去設置。要改變該對話框的行為的話,請查閱改變顯示的文件類型的`Python`源碼。類的構造函數要求兩個參數。
`ImageDialog(parent`, `set_dir`=`None)`
`set_dir`參數是對話框顯示時所在的目錄。如果不設置,那么使用應用程序當前的工作目錄。在對話框被關閉后,`GetFile()`返回所選文件的完整路徑字符串,`GetDirectory()`只返回目錄部分。
## 創建向導
向導是一系列被鏈接在一起的簡單對話框,它使得用戶一步步地跟隨它們。通常它們被用于指導用戶的安裝或一個復雜的配置過程。圖9.11顯示了一個向導示例。
**圖9.11**

在`wxPython`中,一個向導是一系列的頁面,它由類`wx.wizard.Wizard`的一個實例控制。向導實例管理用戶的頁面切換事件。這些頁面自身也是類`wx.wizard.WizardPageSimple`或`wx.wizard.WizardPage`的實例。這兩種類的實例,它們只不過是附加了必要的管理頁面鏈接邏輯的`wx.Panel`的實例。已證明這兩個實例之間的區別僅當用戶按下`Next`按鈕時。`wx.wizard.WizardPage`的實例使你能夠動態地決定瀏覽哪頁,而`wx.wizard.WizardPageSimple`的實例要求向導被顯示前,順序被預先設置。例9.11顯示了產生圖9.11的代碼。
**例9.11** **創建一個簡單的靜態向導**
```
import wx
import wx.wizard
class TitledPage(wx.wizard.WizardPageSimple):#1 創建頁面樣板
def __init__(self, parent, title):
wx.wizard.WizardPageSimple.__init__(self, parent)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.sizer)
titleText = wx.StaticText(self, -1, title)
titleText.SetFont(
wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
self.sizer.Add(titleText, 0,
wx.ALIGN_CENTRE | wx.ALL, 5)
self.sizer.Add(wx.StaticLine(self, -1), 0,
wx.EXPAND | wx.ALL, 5)
if __name__ == "__main__":
app = wx.PySimpleApp()
wizard = wx.wizard.Wizard(None, -1, "Simple Wizard")# 創建向導實例
# 創建向導頁面
page1 = TitledPage(wizard, "Page 1")
page2 = TitledPage(wizard, "Page 2")
page3 = TitledPage(wizard, "Page 3")
page4 = TitledPage(wizard, "Page 4")
page1.sizer.Add(wx.StaticText(page1, -1,
"Testing the wizard"))
page4.sizer.Add(wx.StaticText(page4, -1,
"This is the last page."))
#2 創建頁面鏈接
wx.wizard.WizardPageSimple_Chain(page1, page2)
wx.wizard.WizardPageSimple_Chain(page2, page3)
wx.wizard.WizardPageSimple_Chain(page3, page4)
wizard.FitToPage(page1)#3 調整向導的尺寸
if wizard.RunWizard(page1):#4 運行向導
print "Success"
wizard.Destroy()
```
**#1** 為了便于移植的目的,我們創建了一個簡單的小的頁面,它包含了一個靜態文本標題。通常情況下,這兒還包含一些表單元素,可能還有一些要用戶輸入的數據。
**#2** `wx.wizard.WizardPageSimple_Chain()`函數是一個便利的方法,它以兩個頁面為參數相互地調用它們的`SetNext()`和`SetPrev()`方法。
**#3** `FitToSize()`根據頁面參數及該頁鏈條上的所有頁面調整向導的大小。該方法只能在頁面鏈接被創建后調用。
**#4** 該方法的參數是向導開始時的頁面。向導在它到達一個沒有下一頁的頁面時知道去關閉。如果用戶瀏覽了整個向導并通過按下`Finish`按鈕退出的話,`RunWizard()`方法返回`True`。
創建`wx.wizard.Wizard`實例是使用向導的第一步。其構造函數如下:
```
wx.wizard.Wizard(parent, id=-1, title=wx.EmptyString,
bitmap=wx.NullBitmap, pos=wx.DefaultPosition)
```
在這里的`parent`, `id`, `title`, `pos`和`wx.Panel`的意義相同。如果設置了`bitmap`參數,那么該參數將顯示在每一頁上。這兒只有一個樣式標記:`wx.wizard.WIZARD_EX_HELPBUTTON`,它顯示一個幫助按鈕。這是一個擴展的標記,需要使用第8章所說的兩步創建過程。
通常,你將調用例9.11的#3中所示的`FitToSize()`來管理窗口的尺寸,但是你也可以通過調用帶有一個元組或`wx.Size`實例的`SetPageSize()`來設置一個最小的尺寸。`GetPageSize()`方法返回當前的尺寸,在這兩種情況下,該尺寸僅用于對話框中的單個的頁面部分,而作為一個整體的對話框將更大一些。
你可以管理該類中的頁面。方法`GetCurrentPage()`返回當前被顯示的頁面,如果該向導當前沒有被顯示,該方法返回`None`。你可以通過調用`HasNextPage()`和`HasPrevPage()`來確定當前頁是否有下一頁或上一頁。 使用`RunWizard()`來運行該向導,如例9.11中#4的說明。
向導產生的命令事件如表9.5所示,你可以捕獲這些事件,以便作專門的處理。這些事件對象屬于類`wx.wizard.WizardEvent`,它們提供了兩個可用的方法。`GetPage()`返回`wx.WizardPage`的實例,該實例在向導事件產生時是有效,而非作為事件結果被顯示的實例。如果事件是前進一頁,那么`GetDirection()`返回`True`,如果事件是后退一頁,那么`GetDirection()`返回`False`。
**表9.5 `wx.wizard.WizardDialog`的事件**
| | |
| --- | --- |
| `EVT_WIZARD_CANCEL` | 當用戶按下`Cancel`按鈕時產生。該事件可以使用`Veto()`來否決,這種情況下,對話框將不會消失。 |
| `EVT_WIZARD_FINISHED` | 當用戶按下`Finish`按鈕時產生。 |
| `EVT_WIZARD_HELP` | 當用戶按下`Help`按鈕時產生。 |
| `EVT_WIZARD_PAGE_CHANGED` | 在頁面被切換后產生。 |
| `EVT_WIZARD_PAGE_CHANGING` | 當用戶已請求了一個頁面切換時產生,這時頁面還沒有發生切換。這個事件可以被否決(例如,如果頁面上有一個必須被填寫的字段)。 |
`wx.wizard.WizardPageSimple`類被當作一個面板一樣。它的構造函數使你可以設置上一頁和下一頁,如下所示:
`wx.wizard.WizardPageSimple(parent`=`None`, `prev`=`None`, `next`=`None)`
如果你想在構造器中設置它們,你可以使用`SetPrev()`和`SetNext()`方法。如果那樣太麻煩,你可以使用`wx.wizard.WizardPageSimple_Chain()`,它設置兩頁間的鏈接關系。
向導頁的復雜版:`wx.wizard.WizardPage`,稍微不同。它沒有顯式地設置前一頁和下一頁,而是使你能夠使用更復雜的邏輯去定義下一步到哪兒。它的構造函數如下:
```
wx.WizardPage(parent, bitmap=wx.NullBitmap, resource=None)
```
如果`bitmap`參數被設置了,那么該參數覆蓋父向導中所設置的位圖。`resource`參數從一個`wxPython`資源裝載頁面。要處理頁面邏輯,就要覆蓋`GetPrev()`和`GetNext()`方法來返回你想要向導下一步的位置。該類的一個典型的用法是根據用戶對當前頁的響應動態地決定接下來的頁面。
## 顯示啟動提示
許多應用程序都使用啟動提示來作為一種向用戶介紹該程序的特性等信息的方法。在`wxPython`中有一個非常簡單的機制用來顯示啟動提示。圖9.12顯示了一個提示窗口的示例。
**圖9.12**

例9.12顯示了相關代碼
**例9.12**
```
import wx
if __name__ == "__main__":
app = wx.PySimpleApp()
provider = wx.CreateFileTipProvider("tips.txt", 0)
wx.ShowTip(None, provider, True)
```
有兩個便利的函數用來管理啟動提示。第一個如下創建一個`wx.TipProvider`:
```
wx.CreateFileTipProvider(filename, currentTip)
```
`filename`是包含提示字符串的文件的名字。`currentTip`是該文件中用于一開始顯示的提示字符的索引,該文件中的第一個提示字符串的索引是0。
提示文件是一個簡單的文本文件,其中的每一行是一個不同的提示。空白行被忽略,以#開始的行被當作注釋并也被忽略。下面是上例所使用的提示文件中的內容:
`You` `can` `do` `startup` `tips` `very` `easily.` `Feel` `the` `force`, `Luke.`
提示的提供者(`provider`)是類`wx.PyTipProvider`的一個實例。如果你需要更細化的功能,你可以創建你自己的`wx.TipProvider`的子類并覆蓋`GetTip()`函數。
顯示提示的函數是`wx.ShowTip():`
```
wx.ShowTip(parent, tipProvider, showAtStartup)
```
`parent`是父窗口,`tipProvider`通常創建自`wx.CreateFileTipProvider`。`showAtStartup`控制啟動提示顯示時,復選框是否被選擇。該函數的返回值是復選框的狀態值,以便你使用該值來決定你的應用程序下次啟動時是否顯示啟動提示。
## 使用驗證器(validator)來管理對話框中的數據
驗證器是一個特殊的`wxPython`對象,它簡化了對話框中的數據管理。當我們在第三章中討論事件時,我們簡要的提及到如果一個窗口部件有一個驗證器,那么該驗證器能夠被事件系統自動調用。我們已經見過了幾個`wxPython`窗口部件類的構造函數中將驗證器作為參數,但是我們還沒有討論它們。
驗證器有三個不相關的功能:
1、在對話框關閉前驗證控件中的數據 2、自動與對話框傳遞數據 3、驗證用戶鍵入的數據
=== 如何使用驗證器來確保正確的?藎?===
驗證器對象是`wx.Validator`的子類。父類是抽象的,不能直接使用。盡管在C++ `wxWidget`集中有一對預定義的驗證器類,但是在`wxPython`中,你需要定義你自己的驗證器類。正如我們在別處所見的,你的`Python`類需要繼承自`Python`特定的子類:`wx.PyValidator`,并且能夠覆蓋該父類的所有方法。一個自定義的驗證器子類必須覆蓋方法`Clone()`,該方法應該返回驗證器的相同的副本。
一個驗證器被關聯到你的系統中的一個特定的窗口部件。這可以用兩種方法之一來實現。第一種方法,如果窗口部件許可的話,驗證器可以被作為一個參數傳遞給該窗口部件的構造函數。如果該窗口部件的構造函數沒有一個驗證器參數,你仍然可以通過創建一個驗證器實例并調用該窗口部件的`SetValidator(validator)`方法來關聯一個驗證器。
要驗證控件中的數據,你可以先在你的驗證器子類中覆蓋`Validate(parent)`方法。`parent`參數是驗證器的窗口部件的父窗口(對話框或面板)。如果必要,可以使用這個來從對話框中其它窗口部件得到數據,或者你可以完全忽略該參數。你可以使用`self.GetWindow()`來得到正在被驗證的窗口部件的一個引用。你的`Validate(parent)`方法的返回值是一個布爾值。`True`值表示驗證器的窗口部件中的數據已驗證了。`False`表示有問題。你可以根據`Validate()`方法來使用`x.MessageBox()`去顯示一個警告,但是你不應該做其它的任何可以在`wxPython`應用程序中引發事件的事情。
`Validate()`的返回值是很重要的。它在你使用`OK`按鈕(該按鈕使用`wx.ID_OK` `ID`)企圖關閉一個對話框時發揮作用。作為對`OK`按鈕敲擊處理的一部分,`wxPython`調用對話框中有驗證器的窗口部件的`Validate()`函數。如果任一驗證器返回`False`,那么對話框不將關閉。例9.13顯示了一個帶有驗證器的示例對話框,它檢查所有文本控件中有無數據。
**例9.13** **檢查所有文本控件有無數據的驗證器**
```
import wx
about_txt = """\
The validator used in this example will ensure that the text
controls are not empty when you press the Ok button, and
will not let you leave if any of the Validations fail."""
class NotEmptyValidator(wx.PyValidator):# 創建驗證器子類
def __init__(self):
wx.PyValidator.__init__(self)
def Clone(self):
"""
Note that every validator must implement the Clone() method.
"""
return NotEmptyValidator()
def Validate(self, win):#1 使用驗證器方法
textCtrl = self.GetWindow()
text = textCtrl.GetValue()
if len(text) == 0:
wx.MessageBox("This field must contain some text!", "Error")
textCtrl.SetBackgroundColour("pink")
textCtrl.SetFocus()
textCtrl.Refresh()
return False
else:
textCtrl.SetBackgroundColour(
wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
textCtrl.Refresh()
return True
def TransferToWindow(self):
return True
def TransferFromWindow(self):
return True
class MyDialog(wx.Dialog):
def __init__(self):
wx.Dialog.__init__(self, None, -1, "Validators: validating")
# Create the text controls
about = wx.StaticText(self, -1, about_txt)
name_l = wx.StaticText(self, -1, "Name:")
email_l = wx.StaticText(self, -1, "Email:")
phone_l = wx.StaticText(self, -1, "Phone:")
#2 使用驗證器
name_t = wx.TextCtrl(self, validator=NotEmptyValidator())
email_t = wx.TextCtrl(self, validator=NotEmptyValidator())
phone_t = wx.TextCtrl(self, validator=NotEmptyValidator())
# Use standard button IDs
okay = wx.Button(self, wx.ID_OK)
okay.SetDefault()
cancel = wx.Button(self, wx.ID_CANCEL)
# Layout with sizers
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(about, 0, wx.ALL, 5)
sizer.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5)
fgs = wx.FlexGridSizer(3, 2, 5, 5)
fgs.Add(name_l, 0, wx.ALIGN_RIGHT)
fgs.Add(name_t, 0, wx.EXPAND)
fgs.Add(email_l, 0, wx.ALIGN_RIGHT)
fgs.Add(email_t, 0, wx.EXPAND)
fgs.Add(phone_l, 0, wx.ALIGN_RIGHT)
fgs.Add(phone_t, 0, wx.EXPAND)
fgs.AddGrowableCol(1)
sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5)
btns = wx.StdDialogButtonSizer()
btns.AddButton(okay)
btns.AddButton(cancel)
btns.Realize()
sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5)
self.SetSizer(sizer)
sizer.Fit(self)
app = wx.PySimpleApp()
dlg = MyDialog()
dlg.ShowModal()
dlg.Destroy()
app.MainLoop()
```
**#1** 該方法測試基本的控件有無數據。如果沒有,相應的控件的背景色變為粉紅色。
**#2** 這幾行,對話框中的每個文本控件都關聯一個驗證器。
圖9.13顯示了一個文本域為空就企圖關閉的對話框。
**圖9.13**

明確地告訴對話框去核對驗證器的代碼沒有出現在示例中,因為它是`wxPython`事件系統的一部分。對話框與框架之間的另一區別是對話框有內建的驗證器行為,而框架沒有。如果你喜歡將驗證器用于不在對話框內的控件,那么調用父窗口的`Validate()`方法。如果父窗口已設置了`wx.WS_EX_VALIDATE_RECURSIVELY`額外樣式,那么所有的子窗口的`Validate()`方法也被調用。如果任一驗證失敗,那么`Validate`返回`False`。接下來,我們將討論如何將驗證器用于數據傳輸。
### 如何使用驗證器傳遞數據?
驗證器的第二個重要的功能是,當對話框打開時,它自動將數據傳送給對話框顯示,當該對話框關閉時,自動從對話框把數據傳輸到一個外部資源。圖9.14顯示了一個示例對話框。
**圖9.14**

要實現這個,你必須在你的驗證器子類中覆蓋兩個方法。方法`TransferToWindow()`在對話框打開時自動被調用。你必須使用這個方法把數據放入有驗證器的窗口部件。`TransferFromWindow()`方法在使用`OK`按鈕關閉對話框窗口時且數據已被驗證后被自動調用。你必須使用這個方法來將數據從窗口部件移動給其它的資源。
數據傳輸必須發生的事實意味著驗證器必須對一個外部的數據對象有一些了解,如例9.14所示。在這個例子中,每個驗證器都使用一個全局數據字典的引用和一個字典內的對于相關控件重要的關鍵字來被初始化。
當對話框打開時,`TransferToWindow()`方法從字典中根據關鍵字讀取數據并把數據放入文本域。當對話框關閉時,`TransferFromWindow()`方法反向處理并把數據寫入字典。這個例子的對話框顯示你傳輸的數據。
**例9.14** **一個數據傳輸驗證器**
```
import wx
import pprint
about_txt = """\
The validator used in this example shows how the validator
can be used to transfer data to and from each text control
automatically when the dialog is shown and dismissed."""
class DataXferValidator(wx.PyValidator):# 聲明驗證器
def __init__(self, data, key):
wx.PyValidator.__init__(self)
self.data = data
self.key = key
def Clone(self):
"""
Note that every validator must implement the Clone() method.
"""
return DataXferValidator(self.data, self.key)
def Validate(self, win):# 沒有驗證數據
return True
def TransferToWindow(self):# 對話框打開時被調用
textCtrl = self.GetWindow()
textCtrl.SetValue(self.data.get(self.key, ""))
return True
def TransferFromWindow(self):# 對話框關閉時被調用
textCtrl = self.GetWindow()
self.data[self.key] = textCtrl.GetValue()
return True
class MyDialog(wx.Dialog):
def __init__(self, data):
wx.Dialog.__init__(self, None, -1, "Validators: data transfer")
# Create the text controls
about = wx.StaticText(self, -1, about_txt)
name_l = wx.StaticText(self, -1, "Name:")
email_l = wx.StaticText(self, -1, "Email:")
phone_l = wx.StaticText(self, -1, "Phone:")
# 將驗證器與窗口部件相關聯
name_t = wx.TextCtrl(self, validator=DataXferValidator(data, "name"))
email_t = wx.TextCtrl(self, validator=DataXferValidator(data, "email"))
phone_t = wx.TextCtrl(self, validator=DataXferValidator(data, "phone"))
# Use standard button IDs
okay = wx.Button(self, wx.ID_OK)
okay.SetDefault()
cancel = wx.Button(self, wx.ID_CANCEL)
# Layout with sizers
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(about, 0, wx.ALL, 5)
sizer.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5)
fgs = wx.FlexGridSizer(3, 2, 5, 5)
fgs.Add(name_l, 0, wx.ALIGN_RIGHT)
fgs.Add(name_t, 0, wx.EXPAND)
fgs.Add(email_l, 0, wx.ALIGN_RIGHT)
fgs.Add(email_t, 0, wx.EXPAND)
fgs.Add(phone_l, 0, wx.ALIGN_RIGHT)
fgs.Add(phone_t, 0, wx.EXPAND)
fgs.AddGrowableCol(1)
sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5)
btns = wx.StdDialogButtonSizer()
btns.AddButton(okay)
btns.AddButton(cancel)
btns.Realize()
sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5)
self.SetSizer(sizer)
sizer.Fit(self)
app = wx.PySimpleApp()
data = { "name" : "Jordyn Dunn" }
dlg = MyDialog(data)
dlg.ShowModal()
dlg.Destroy()
wx.MessageBox("You entered these values:\n\n" +
pprint.pformat(data))
app.MainLoop()
```
對話框中驗證器的傳輸數據方法的調用自動發生。要在非對話框窗口中使用驗證器來傳輸數據,必須調用父窗口部件的`TransDataFromWindow()`和`TransferDataToWindow()`方法。如果該窗口設置了`wx.WS_EX_VALIDATE_RECURSIVELY`額外樣式,那么在所有的子窗口部件上也將調用該傳輸函數。
### 如何在數據被鍵入時驗證數據?
在數據被傳給窗口部件之前,你也可使用驗證器來在用戶輸入數據時驗證所輸入的數據。這是非常有用的,因為它可以防止將得到的壞的數據傳入你的應用程序。圖9.12顯示了一個例子,其中對話框的文本闡明了該思想。
**圖9.15**

驗證數據的方法的自動化成份少于其它的機制。你必須顯式綁定驗證器的窗口部件的字符事件給一個函數,如下所示:
`self.Bind(wx.EVT_CHAR`, `self.OnChar)`
該窗口部件假設事件源屬于驗證器。例9.15顯示了這個綁定。
**例9.15 實時驗證**
```
import wx
import string
about_txt = """\
The validator used in this example will validate the input on the fly
instead of waiting until the okay button is pressed. The first field
will not allow digits to be typed, the second will allow anything
and the third will not allow alphabetic characters to be entered.
"""
class CharValidator(wx.PyValidator):
def __init__(self, flag):
wx.PyValidator.__init__(self)
self.flag = flag
self.Bind(wx.EVT_CHAR, self.OnChar)# 綁定字符事件
def Clone(self):
"""
Note that every validator must implement the Clone() method.
"""
return CharValidator(self.flag)
def Validate(self, win):
return True
def TransferToWindow(self):
return True
def TransferFromWindow(self):
return True
def OnChar(self, evt):# 數據處理
key = chr(evt.GetKeyCode())
if self.flag == "no-alpha" and key in string.letters:
return
if self.flag == "no-digit" and key in string.digits:
return
evt.Skip()
class MyDialog(wx.Dialog):
def __init__(self):
wx.Dialog.__init__(self, None, -1, "Validators: behavior modification")
# Create the text controls
about = wx.StaticText(self, -1, about_txt)
name_l = wx.StaticText(self, -1, "Name:")
email_l = wx.StaticText(self, -1, "Email:")
phone_l = wx.StaticText(self, -1, "Phone:")
# 綁定驗證器
name_t = wx.TextCtrl(self, validator=CharValidator("no-digit"))
email_t = wx.TextCtrl(self, validator=CharValidator("any"))
phone_t = wx.TextCtrl(self, validator=CharValidator("no-alpha"))
# Use standard button IDs
okay = wx.Button(self, wx.ID_OK)
okay.SetDefault()
cancel = wx.Button(self, wx.ID_CANCEL)
# Layout with sizers
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(about, 0, wx.ALL, 5)
sizer.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5)
fgs = wx.FlexGridSizer(3, 2, 5, 5)
fgs.Add(name_l, 0, wx.ALIGN_RIGHT)
fgs.Add(name_t, 0, wx.EXPAND)
fgs.Add(email_l, 0, wx.ALIGN_RIGHT)
fgs.Add(email_t, 0, wx.EXPAND)
fgs.Add(phone_l, 0, wx.ALIGN_RIGHT)
fgs.Add(phone_t, 0, wx.EXPAND)
fgs.AddGrowableCol(1)
sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5)
btns = wx.StdDialogButtonSizer()
btns.AddButton(okay)
btns.AddButton(cancel)
btns.Realize()
sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5)
self.SetSizer(sizer)
sizer.Fit(self)
app = wx.PySimpleApp()
dlg = MyDialog()
dlg.ShowModal()
dlg.Destroy()
app.MainLoop()
```
由于`OnChar()`方法是在一個驗證器中,所以它在窗口部件響應字符事件之間被調用。該方法讓你可以通過使用`Skip()`來將事件傳送給窗口部件。你必須調用`Skip()`,否則驗證器將妨礙正常的事件處理。驗證器執行一個測試來查看用于該控件的字符是否有效。如果該字符無效,那么`Skip()`不被調用,并且事件處理停止。如果有必須的話,除了`wx.EVT_CHAR`之外的其它事件也可以被綁定,并在窗口部件響應之前驗證器處理那些事件。
對于處理你`wxPython`應用程序中的數據,驗證器是一個強大且靈活的機制。適當地使用它們,可以讓你的應用程序的開發和維護更加的順暢。
## 本章小結
1、對話框被用于在有一套特殊的信息需要被獲取的情況下,處理與用戶的交互,這種交互通常很快被完成。在`wxPython`中,你可以使用通用的`wx.Dialog`類來創建你自己的對話框,或者你也可以使用預定義的對話框。大多數情況下,通常被使用的對話框都有相應的便利函數,使得這種對話框的使用更容易。
2、對話框可以顯示為模式對話框,這意味在對話框可見時,該應用程序中的用戶所有的其它輸入將被阻塞。模式對話框通過使用`ShowModal()`方法來被調用,它的返回值依據用戶所按的按鈕而定(`OK`或`Cancel`)。關閉模式對話框并不會銷毀它,該對話框的實例可以被再用。
3、在`wxPython`中有三個通用的簡單對話框。`wx.MessageDialog`顯示一個消息對話框。`wx.TextEntryDialog`使用戶能夠鍵入文本,`wx.SingleChoiceDialog`給用戶一個基于列表項的選擇。
4、當正在執行一個長時間的后臺的任務時,你可以使用`wx.ProgressDialog`來給用戶顯示進度信息。用戶可以通過`wx.FileDialog`使用標準文件對話框來選擇一個文件。可以使用`wx.DirDialog`來創建一個標準目錄樹,它使得用戶可以選擇一個目錄。
5、你可以使用`wx.FontDialog`和`wx.ColorDialog`來訪問標準的字體選擇器和顏色選擇器。在這兩種情況中,對話框的行為和用戶的響應是由一個單獨的數據類來控制的。
6、要瀏覽縮略圖,可以使用`wxPython`特定的類`wx.lib.imagebrowser.ImageDialog`。這個類使用戶能夠通過文件系統并選擇一個圖像。
7、你可以通過使用`wx.wizard.Wizard`創建一個向導來將一組相關的對話框表單鏈接起來。對話框表單是`wx.wizard.WizardSimplePage`或`wx.wizard.WizardPage`的實例。兩者的區別是,`wx.wizard.WizardSimplePage`的頁到頁的路線需要在向導被顯示之前就安排好,而`wx.wizard.WizardPage`使你能夠在運行時管理頁到頁的路線的邏輯。
8、使用`wx.CreateFileTipProvider`和`wx.ShowTip`函數可以很容易地顯示啟動提示。
9、驗證器是很有用的對象,如果輸入的數據不正確的話,它可以自動阻止對話框的關閉。他們也可以在一個顯示的對話框和一個外部的對象之間傳輸數據,并且能夠實時地驗證數據的輸入。
- 活學活用wxPython
- 前言
- 致謝
- 關于本書
- 第一部分
- 第一章 歡迎使用wxPython
- 第二章 給wxPython程序一個堅實的基礎
- 第三章 在事件驅動環境中開發
- 第四章 用PyCrust使得wxPython更易處理
- 第五章 繪制藍圖
- 第六章 使用wxPython基本構件
- 第二部分 基礎wxPython
- 第七章 使用基礎控件
- 第八章 將構件放入窗體中
- 第九章 通過對話框讓用戶選擇
- 第十章 創建和使用wxPython菜單
- 第十一章 使用sizer放置構件
- 第十二章 操作基本圖像
- 第三部分 高級wxPython
- 第十三章 建造列表控件并管理列表項
- 第十四章 網格控件
- 第十五章 樹形控件
- 第十六章 在應用程序中加入HTML
- 第十七章 wxPython的打印構架
- 第十八章 使用wxPython的其他功能