# 第七章 使用基礎控件
1. [使用基本的控件工作](#A.2BT391KFf6Zyx2hGOnTvZd5U9c-)
1. [顯示文本](#A.2BZj55OmWHZyw-)
2. [使用按鈕工作](#A.2BT391KGMJlK5d5U9c-)
3. [輸入并顯示數字](#A.2Bj5NRZV52Zj55OmVwW1c-)
4. [給用戶以選擇](#A.2Bftl1KGI3TuWQCWLp-)
5. [本章小結](#A.2BZyx64FwPftM-)
`wxPython`工具包提供了多種不同的窗口部件,包括了本章所提到的基本控件。我們涉及靜態文本、可編輯的文本、按鈕、微調、滑塊、復選框、單選按鈕、選擇器、列表框、組合框和標尺。對于每種窗口部件,我們將提供一個關于如何使用它的簡短例子,并附上相關的`wxPython` `API`的說明。
## 顯示文本
這一節以在屏幕上顯示文本的例子作為開始,包括用作標簽的靜態文本域,有樣式和無樣式的都使用了。你可以創建用于用戶輸入的單行和多行文本域。另外,我們將討論如何選擇文本的字體。
### 如何顯示靜態文本?
大概對于所有的`UI`工具來說,最基本的任務就是在屏幕上繪制純文本。在`wxPython`中,使用類`wx.StaticText`來完成。圖7.1顯示了這個靜態文本控件。

在`wx.StaticText`中,你能夠改變文本的對齊方式、字體和顏色。簡單的靜態文本控件可以包含多行文本,但是你不能處理多種字體或樣式。處理多種字體或樣式,要使用更精細的文本控件,如`wx.html.HTMLWindow`,它在第十六章中說明。為了在靜態文本控件中顯示多行文本,我們要包括其中有換行符的字符串,并使控件的大小足夠顯示所有的文本。有一個特點是你在圖7.1中所不能看到的,那就是`wx.StaticText`窗口不會接受或響應鼠標事件。
**如何顯示靜態文本**
例子7.1顯示了產生圖7.1的代碼。
**例7.1** **如何使用靜態文本的一個基本例子**
```
import wx
class StaticTextFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Static Text Example',
size=(400, 300))
panel = wx.Panel(self, -1)
# 這是一個基本的靜態文本
wx.StaticText(panel, -1, "This is an example of static text",
(100, 10))
# 指定了前景色和背景色的靜態文本
rev = wx.StaticText(panel, -1, "Static Text With Reversed Colors",
(100, 30))
rev.SetForegroundColour('white')
rev.SetBackgroundColour('black')
# 指定居中對齊的的靜態文本
center = wx.StaticText(panel, -1, "align center", (100, 50),
(160, -1), wx.ALIGN_CENTER)
center.SetForegroundColour('white')
center.SetBackgroundColour('black')
# 指定右對齊的靜態文本
right = wx.StaticText(panel, -1, "align right", (100, 70),
(160, -1), wx.ALIGN_RIGHT)
right.SetForegroundColour('white')
right.SetBackgroundColour('black')
# 指定新字體的靜態文本
str = "You can also change the font."
text = wx.StaticText(panel, -1, str, (20, 100))
font = wx.Font(18, wx.DECORATIVE, wx.ITALIC, wx.NORMAL)
text.SetFont(font)
# 顯示多行文本
wx.StaticText(panel, -1, "Your text\ncan be split\n"
"over multiple lines\n\neven blank ones", (20,150))
#顯示對齊的多行文本
wx.StaticText(panel, -1, "Multi-line text\ncan also\n"
"be right aligned\n\neven with a blank", (220,150),
style=wx.ALIGN_RIGHT)
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = StaticTextFrame()
frame.Show()
app.MainLoop()
```
`wx.StaticText`的構造函數和基本的`wxWidget`構造函數相同,如下所示:
```
wx.StaticText(parent, id, label, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=0, name="staticText")
```
表7.1說明了這些參數——大多數的`wxPython`窗口部件都有相類似的參數。對于構造函數的參數的更詳細的說明,請參見第2章的相關論述。
**表7.1** **`wx.StaticText`構造函數的參數**
`parent`:父窗口部件。
`id`:標識符。使用-1可以自動創建一個唯一的標識。
`label`:你想顯示在靜態控件中的文本。
`pos`:一個`wx.Point`或一個`Python`元組,它是窗口部件的位置。
`size`:一個`wx.Size`或一個`Python`元組,它是窗口部件的尺寸。
`style`:樣式標記。
`name`:對象的名字,用于查找的需要。
接下來我們更詳細地討論樣式標記。
**使用樣式工作**
所有在例7.1中靜態文本實例所調用的方法都是屬于基父類`wx.Window`的;`wx.StaticText`沒有定義任何它自己的新方法。表7.2列出了一些專用于`wx.StaticText`的樣式。
**表7.2**
`wx.ALIGN_CENTER`:靜態文本位于靜態文本控件的中心。
`wx.ALIGN_LEFT`:文本在窗口部件中左對齊。這是默認的樣式。
`wx.ALIGN_RIGHT`:文本在窗口部件中右對齊。
`wx.ST_NO_AUTORESIZE`:如果使用了這個樣式,那么在使用了`SetLabel()`改變文本之后,靜態文本控件不將自我調整尺寸。你應結合使用一個居中或右對齊的控件來保持對齊。
`wx.StaticText`控件覆蓋了`SetLabel()`,以便根據新的文本來調整自身,除非`wx.ST_NO_AUTORESIZE`樣式被設置了。
當創建了一個居中或右對齊的單行靜態文本時,你應該顯式地在構造器中設置控件的尺寸。指定尺寸以防止`wxPython`自動調整該控件的尺寸。`wxPython`的默認尺寸是剛好包容了文本的矩形尺寸,因此對齊就沒有什么必要。要在程序中動態地改變窗口部件中的文本,而不改變該窗口部件的尺寸,就要設置`wx.ST_NO_AUTORESIZE`樣式。這樣就防止了在文本被重置后,窗口部件自動調整尺寸到剛好包容了文本。如果靜態文本是位于一個動態的布局中,那么改變它的尺寸可能導致屏幕上其它的窗口部件移動,這就對用戶產生了干擾。
**其它顯示文本的技術**
還有其它的方法來顯示文本。其中之一就是`wx.lib.stattext.GenStaticText`類,它是`wx.StaticText`的純`Python`實現。它比標準C++版的跨平臺性更好,并且它接受鼠標事件。當你想子類化或創建你自己的靜態文本控件時,它是更可取的。
你可以使用`DrawText(text`, x,`y)`和`DrawRotatedText(text`, x, y, `angle)`方法直接繪制文本到你的設備上下文。后者是顯示有一定角度的文本的最容易的方法,盡管`GenStaticText`的子類也能處理旋轉問題。設備上下文在第6章中做了簡短的說明,我們將在第12章中對它做更詳細的說明。
### 如何讓用戶輸入文本?
超越純粹顯示靜態文本,我們將開始討論當輸入文本時的用戶交互。`wxPython`的文本域窗口部件的類是`wx.TextCtrl`,它允許單行和多行文本輸入。它也可以作為密碼輸入控件,掩飾所按下的按鍵。如果平臺支持的話,`wx.TextCtrl`也提供豐富格式文本的顯示,通過使用所定義和顯示的多文本樣式。圖7.2顯示了一個作為單行控件的`wx.TextCtrl`的樣板。其中的密碼輸入框對密碼進行了掩飾。

接下來,我們將演示如何創建文本,然后討論文本控件的樣式選項。
**如何創建文本輸入控件**
例子7.2顯示了用于生成圖7.2的代碼
**例7.2** **`wx.TextCtrl`的單行例子**
```
import wx
class TextFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Text Entry Example',
size=(300, 100))
panel = wx.Panel(self, -1)
basicLabel = wx.StaticText(panel, -1, "Basic Control:")
basicText = wx.TextCtrl(panel, -1, "I've entered some text!",
size=(175, -1))
basicText.SetInsertionPoint(0)
pwdLabel = wx.StaticText(panel, -1, "Password:")
pwdText = wx.TextCtrl(panel, -1, "password", size=(175, -1),
style=wx.TE_PASSWORD)
sizer = wx.FlexGridSizer(cols=2, hgap=6, vgap=6)
sizer.AddMany([basicLabel, basicText, pwdLabel, pwdText])
panel.SetSizer(sizer)
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = TextFrame()
frame.Show()
app.MainLoop()
```
`wx.TextCtrl`類的構造函數較小且比其父類`wx.Window`更精細,它增加了兩個參數:
`wx.TextCtrl(parent`, `id`, `value` = "", `pos`=`wx.DefaultPosition`, `size`=`wx.DefaultSize`, `style`=0, `validator`=`wx.DefaultValidator` `name`=`wx.TextCtrlNameStr)`
參數`parent`, `id`, `pos`, `size`, `style`, 和 `name`與`wx.Window`構造函數的相同。`value`是顯示在該控件中的初始文本。
`validator`參數用于一個`wx.Validator`。`validator`通常用于過慮數據以確保只能鍵入要接受的數據。將在第9章對`validator`做更詳細的討論。
**使用單行文本控件樣式**
這里,我們將討論一些唯一無二的文本控件樣式。 表7.3說明了用于單行文本控件的樣式標記
**表7.3** **單行`wx.TextCtrl`的樣式**
`wx.TE_CENTER`:控件中的文本居中。
`wx.TE_LEFT`:控件中的文本左對齊。默認行為。
`wx.TE_NOHIDESEL`:文本始終高亮顯示,只適用于`Windows`。
`wx.TE_PASSWORD`:不顯示所鍵入的文本,代替以星號顯示。
`wx.TE_PROCESS_ENTER`:如果使用了這個樣式,那么當用戶在控件內按下回車鍵時,一個文本輸入事件被觸發。否則,按鍵事件內在的由該文本控件或該對話框管理。
`wx.TE_PROCESS_TAB`:如果指定了這個樣式,那么通常的字符事件在`Tab`鍵按下時創建(一般意味一個制表符將被插入文本)。否則,`tab`由對話框來管理,通常是控件間的切換。
`wx.TE_READONLY`:文本控件為只讀,用戶不能修改其中的文本。
`wx.TE_RIGHT`:控件中的文本右對齊。
像其它樣式標記一樣,它們可以使用|符號來組合使用,盡管其中的三個對齊標記是相互排斥的。
對于添加文本和移動插入點,該文本控件自動管理用戶的按鍵和鼠標事件。對于該文本控件可用的命令控制組合說明如下:
* `ctrl`-x :剪切 `ctrl`-c :復制 `ctrl`-v :粘貼 `ctrl`-z :撤消
### 不輸入的情況下如何改變文本?
除了根據用戶的輸入改變顯示的文本外,`wx.TextCtrl`提供了在程序中改變顯示的文本的一些方法。你可以完全改變文本或僅移動插入點到文本中不同的位置。表7.4列出了`wx.TextCtrl`的文本處理方法。
**表7.4**
`AppendText(text)`:在尾部添加文本。
`Clear()`:重置控件中的文本為“”。并且生成一個文本更新事件。
`EmulateKeyPress(event)`:產生一個按鍵事件,插入與事件相關聯的控制符,就如同實際的按鍵發生了。
`GetInsertionPoint()` `SetInsertionPoint(pos)` `SetInsertionPointEnd()`:得到或設置插入點的位置,位置是整型的索引值。控件的開始位置是0。
`GetRange(from`, `to)`:返回控件中位置索引范圍內的字符串。
`GetSelection()` `GetStringSelection()` `SetSelection(from`, `to)`:`GetSelection()`以元組的形式返回當前所選擇的文本的起始位置的索引值(開始,結束)。`GetStringSelection()`得到所選擇的字符串。`SetSelection(from`, `to)`設置選擇的文本。
`GetValue()` `SetValue(value)`:`SetValue()`改變控件中的全部文本。`GetValue()`返回控件中所有的字符串。
`Remove(from`, `to)`:刪除指定范圍的文本。
`Replace(from`, `to`, `value)`:用給定的值替換掉指定范圍內的文本。這可以改變文本的長度。
`WriteText(text)`:類似于`AppendText()`,只是寫入的文本被放置在當前的插入點。
當你的控件是只讀的或如果你根據事件而非用戶鍵盤輸入來改變控件中的文本是,這些方法是十分有用的。
### 如何創建一個多行或樣式文本控件?
你可以使用`wx.TE_MULTILINE`樣式標記創建一個多行文本控件。如果本地窗口控件支持樣式,那么你可以改變被控件管理的文本的字體和顏色樣式,這有時被稱為豐富格式文本。對于另外的一些平臺,設置樣式的調用被忽視掉了。圖7.3顯示了多行文本控件的一個例子。

例7.3包含了用于創建圖7.3的代碼。通常,創建一個多行文本控件是通過設置`wx.TE_MULTILINE`樣式標記來處理的。較后的部分,我們將討論使用豐富文本樣式。
**例7.3** **創建一個多行文本控件**
```
import wx
class TextFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Text Entry Example',
size=(300, 250))
panel = wx.Panel(self, -1)
multiLabel = wx.StaticText(panel, -1, "Multi-line")
multiText = wx.TextCtrl(panel, -1,
"Here is a looooooooooooooong line of text set in the control.\n\n"
"See that it wrapped, and that this line is after a blank",
size=(200, 100), style=wx.TE_MULTILINE) #創建一個文本控件
multiText.SetInsertionPoint(0) #設置插入點
richLabel = wx.StaticText(panel, -1, "Rich Text")
richText = wx.TextCtrl(panel, -1,
"If supported by the native control, this is reversed, and this is a different font.",
size=(200, 100), style=wx.TE_MULTILINE|wx.TE_RICH2) #創建豐富文本控件
richText.SetInsertionPoint(0)
richText.SetStyle(44, 52, wx.TextAttr("white", "black")) #設置文本樣式
points = richText.GetFont().GetPointSize()
f = wx.Font(points + 3, wx.ROMAN, wx.ITALIC, wx.BOLD, True) #創建一個字體
richText.SetStyle(68, 82, wx.TextAttr("blue", wx.NullColour, f)) #用新字體設置樣式
sizer = wx.FlexGridSizer(cols=2, hgap=6, vgap=6)
sizer.AddMany([multiLabel, multiText, richLabel, richText])
panel.SetSizer(sizer)
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = TextFrame()
frame.Show()
app.MainLoop()
```
**使用多行或豐富文本樣式**
除了`wx.TE_MULTILINE`,還有另外的樣式標記,它們只在一個多行或豐富文本控件的上下文中有意義。表7.5列出了這些窗口樣式。
**表7.5**
`wx.HSCROLL`:如果文本控件是多行的,并且如果該樣式被聲明了,那么長的行將不會自動換行,并顯示水平滾動條。該選項在`GTK`+中被忽略。
`wx.TE_AUTO_URL`:如果豐富文本選項被設置并且平臺支持的話,那么當用戶的鼠標位于文本中的一個`URL`上或在該`URL`上敲擊時,這個樣式將導致一個事件被生成。
`wx.TE_DONTWRAP`:`wx.HSCROLL`的別名。
`wx.TE_LINEWRAP`:對于太長的行,以字符為界換行。某些操作系統可能會忽略該樣式。
`wx.TE_MULTILINE`:文本控件將顯示多行。
`wx.TE_RICH`:用于`Windows`下,豐富文本控件用作基本的窗口部件。這允許樣式文本的使用。
`wx.TE_RICH2`:用于`Windows`下,把最新版本的豐富文本控件用作基本的窗口部件。
`wx.TE_WORDWRAP`:對于太長的行,以單詞為界換行。許多操作系統會忽略該樣式。
記住,上面這些樣式可以組合使用,所以上面例子中的多行豐富文本控件使用`wx.TE_MULTILINE` | `wx.TE_RICH2`來聲明。
用在`wx.TextCtrl`窗口部件中的文本樣式是類`wx.TextAttr`的實例。`wx.TextAttr`實例的屬性有文本顏色、背景色、和字體,它們都能夠在構造函數中被指定,如下所示:
`wx.TextAttr(colText`, `colBack`=`wx.NullColor`, `font`=`wx.NullFont)`
文本色和背景色是`wxPython`對象,它們可以使用顏色名或顏色的`RGB`值(紅, 綠, 藍)來指定。`wx.NullColor`指明使用控件目前的背景色。`font`是一個`wx.Font`對象,我們將在下一小節討論。`wx.NullFont`對象指明使用當前默認字體。
類`wx.TextAttr`有相關屬性的`get`*()方法:`GetBackgroundColour()`, `GetFont()`, 和 `GetTextColour()`,也有返回布爾值的驗證存在性的方法:`HasBackgroundColour()`, `HasFont()`, 和 `HasTextColour()`。如果屬性包含一個默認值,則`Has`*()方法返回`False`。如果所有這三個屬性都包含默認值,則`IsDefault()`方法返回`true`。這個類沒有`set`*()方法,因為`wx.TextAttr`的實例是不可變的。要改變文本的樣式,你必須創建一個實例。
使用文本樣式,要調用`SetDefaultStyle(style)`或`SetStyle(start`, `end`,`style)`。第一個方法設置為控件當前的樣式。任何插入到該控件中的文本,不管是鍵入的,或使用了`AppendText()` 或 `WriteText()`方法的,都以該樣式顯示。如果樣式的某個屬性是默認的,那么該樣式的當前值被保留。但是,如果樣式的所有屬性都是默認的,那么恢復默認樣式。`SetStyle()`與`SetDefaultStyle(style)`類似,只是立即對位于`start` 和 `end`位置之間的文本起作用。樣式參數中的默認屬性通過檢查該控件的當前默認樣式來解決。例7.3使用下面一行代碼來反轉文本中幾個字符的顏色:
`richText.SetStyle(44`, 52, `wx.TextAttr(`"`white`", "`black`"))
背景色變為了黑色,相應的字符變為了白色。
表7.6列出了`wx.TextCtrl`的方法,它們在處理多行控件和豐富文本中是有用的。
**表7.6**
`GetDefaultStyle()` `SetDefaultStyle(style)`:上面已作了說明。
`GetLineLength(lineNo)`:返回給定行的長度的整數值。
`GetLineText(lineNo)`:返回給定行的文本。
`GetNumberOfLines()`:返回控件中的行的數量。對于單行,返回1。
`IsMultiLine()` `IsSingleLine()`:布爾類型的方法,確定控件的狀態。
`PositionToXY(pos)`:指定文本內的一個整數值位置,返回以元組(列,行)形式的索引位置。列和行的索引值均以0作為開始。
`SetStyle(start`, `end`,`style)`:立即改變指定范圍內文本的樣式。
`ShowPosition(pos)`:引起一個多行控件的滾動,以便觀察到指定位置的內容。
`XYToPosition(x`, `y)`:與`PositionToXY(pos)`相反——指定行和列,返回整數值位置。
如果你能在系統中使用任意字體的話,那么就可以更加靈活的創建樣式。 接下來,我們將給你展示如何創建和使用字體實例。
### 如何創建一個字體?
字體是類`wx.Font`的實例。你所訪問的任何字體,它已經被安裝并對于基本的系統是可訪問的。創建一個字體實例,要使用如下的構造函數:
`wx.Font(pointSize`, `family`, `style`, `weight`, `underline`=`False`, `faceName`="", `encoding`=`wx.FONTENCODING_DEFAULT)`
`pointSize`是字體的以磅為單位的整數尺寸。`family`用于快速指定一個字體而無需知道該字體的實際的名字。字體的準確選擇依賴于系統和具體可用的字體。可用的字體類別的示例顯示在表7.7中。你所得到的精確的字體將依賴于你的系統。
**表7.7**
`wx.DECORATIVE`:一個正式的,老的英文樣式字體。
`wx.DEFAULT`:系統默認字體。
`wx.MODERN`:一個單間隔(固定字符間距)字體。
`wx.ROMAN`:`serif`字體,通常類似于`Times` `New` `Roman`。
`wx.SCRIPT`:手寫體或草寫體
`wx.SWISS`:`sans`-`serif`字體,通常類似于`Helvetica`或`Arial`。
`style`參數指明字體的是否傾斜,它的值有:`wx.NORMAL`, `wx.SLANT`, 和 `wx.ITALIC`。同樣,`weight`參數指明字體的醒目程度,可選值有:`wx.NORMAL`, `wx.LIGHT`,或`wx.BOLD`。這些常量值的行為根據它的名字就可以知道了。`underline`參數僅工作在`Windows`系統下,如果取值為`True`,則加下劃線,`False`為無下劃線。 `faceName`參數指定字體名。
`encoding`參數允許你在幾個編碼中選擇一個,它映射內部的字符和字本顯示字符。編碼不是`Unicode`編碼,只是用于`wxPython`的不同的8位編碼。大多數情況你可以使用默認編碼。
為了獲取系統的有效字體的一個列表,并使用戶可用它們,要使用專門的類`wx.FontEnumerator`,如下所示:
e = `wx.FontEnumerator()` `e.EnumerateFacenames()` `fontList` = `e.GetFacenames()`
要限制該列表為固定寬度,就要將上面的第一行改為e = `wx.FontEnumerator(fixedWidth`=`True)`。
### 如果我們系統不支持豐富文本,那么我還能使用樣式文本嗎?
可以。在`wxPython`中有一個跨平臺的樣式文本窗口部件,名為`wx.stc.StyledTextCtrl`,它是`Python`對`Scintilla`豐富文本組件的封裝。因為`Scintilla`不是`wxWidgets`的一部分,而是作為一個獨立的第三方組被合并到了`wxPython`中,所以它不與我們已經討論過的類共享相同的`API`。`wx.stc.StyledCtrl`的完整說明超過了我們要講的范圍,但是你可以在`http:`//`wiki.wxpython.org`/`index.cgi`/`wxStyledTextCtrl`找到相關的文檔。
### 如果我的文本控件不匹配我的字符串該怎么辦?
當使用多行`wx.TextCtrl`的時候,要知道的一點是,該文本控件是以何種方式存儲字符串的。在內部,存儲在該`wx.TextCtrl`中的多行字符是以\n作為行的分隔符的。這與基本的操作系統無關,即使某些系統使用了不同的字符組合作為一行的分隔符。當你使用`GetValue()`來獲取該字符串時,原來的行分隔符被還原,因此你不必考慮手工轉換。這個的好處就是控件中的文本不依賴于任何特定的操作系統。
缺點是,文本控件中的行的長度和行的索引與它們在文本控件外的可能是不同的。例如,如果你在一個`Windows`系統上,系統所用的行分隔符是\r\n,通過`GetValue()`所得知的字符串的長度將比通過`GetLastPosition()`所得知的字符串的結尾長。通過在例7.3中增加下面兩行:
`print` "`getValue`", `len(multiText.GetValue())` `print` "`lastPos`", `multiText.GetLastPosition()`
我們在`Unix`系統上所得的結果應該是:
`getValue` 119 `lastPos` 119
我們在`Windows`系統上所得的結果應該是:
`getValue` 121 `lastPos` 119
這意味你不應該使用多行文本控件的位置索引來取得原字符串,位置索引應該用作`wx.TextCtrl`的另外方法的參數。對于該控件中的文本的子串,應該使用`GetRange()`或`GetSelectedText()`。也不要反向索引;不要使用原字符串的索引來取得并放入文本控件中。下面是一個例子,它使用了不正確的方法在插入點之后直接得到10個字符:
```
aLongString = """Any old
multi line string
will do here.
Just as long as
it is multiline"""
text = wx.TextCtrl(panel, -1, aLongString, style=wx.TE_MULTILINE)
x = text.GetInsertionPoint()
selection = aLongString[x : x + 10] ### 這將是不正確的
```
在`Windows`或`Mac`系統中要得到正確的結果,最后一行應換為:
`selection` = `text.GetRange(x`, x + 10)
### 如何響應文本事件?
有一個由`wx.TextCtrl`窗口部件產生的便利的命令事件,你可能想用它。你需要把相關事件傳遞給`Bind`方法以捕獲該事件,如下所示:
`frame.Bind(wx.EVT_TEXT`, `frame.OnText`, `text)`
表7.8說明了這些命令事件。
**表7.8** **`wx.TextCtrl`的事件**
`EVT_TEXT`:當控件中的文本改變時產生該事件。文本因用戶的輸入或在程序中使用`SetValue()`而被改變,都要產生該事件。
`EVT_TEXT_ENTER`:當用戶在一個`wx.TE_PROCESS_ENTER`樣式的文本控件中按下了回車鍵時,產生該事件。
`EVT_TEXT_URL`:如果在`Windows`系統上,`wx.TE_RICH`或`wx.TE_RICH2`樣式被設置了,并且`wx.TE_AUTO_URL`樣式也被設置了,那么當在文本控件內的`URL`上發生了一個鼠標事件時,該事件被觸發。
`EVT_TEXT_MAXLEN`:如果使用`SetMaxLength()`指定了該控件的最大長度,那么當用戶試圖輸入更長的字符串時,該事件被觸發。你可能會用這個,例如,這時給用戶顯示一個警告消息。
接下來,讓我們來討論被主要設計來得到鼠標輸入的控件。其中最簡單的就是按鈕。
## 使用按鈕工作
在`wxPython`中有很多不同類型的按鈕。這一節,我們將討論文本按鈕、位圖按鈕、開關按鈕(`toggle` `buttons`)和通用(`generic`)按鈕。
### 如何生成一個按鈕?
在第一部分(`part` 1)中,我們已經說明了幾個按鈕的例子,所以這里我們只簡短的涉及它的一些基本的東西。圖7.4顯示了一個簡單的按鈕。
**圖7.4**

使用按鈕是非常簡單的。例7.4顯示了該簡單按鈕的代碼。
**例7.4** **創建并顯示一個簡單的按鈕**
```
import wx
class ButtonFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Button Example',
size=(300, 100))
panel = wx.Panel(self, -1)
self.button = wx.Button(panel, -1, "Hello", pos=(50, 20))
self.Bind(wx.EVT_BUTTON, self.OnClick, self.button)
self.button.SetDefault()
def OnClick(self, event):
self.button.SetLabel("Clicked")
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = ButtonFrame()
frame.Show()
app.MainLoop()
```
`wx.Button`的構造函數類似于我們已經看到過的,如下所示:
`wx.Button(parent`, `id`, `label`, `pos`, `size`=`wxDefaultSize`, `style`=0, `validator`, `name`="`button`")
參數`label`是顯示在按鈕上的文本。它可以在程序運行期間使用`SetLabel()`來改變,并且使用`GetLabel()`來獲取。另外兩個有用的方法是`GetDefaultSize()`和`SetDefault()`。`GetDefaultSize()`返回系統默認按鈕的尺寸(對于框架間的一致性是有用的);`SetDefault()`設置按鈕為對話框或框架的默認按鈕。默認按鈕的繪制不同于其它按鈕,它在對話框獲得焦點時,通常按下回車鍵被激活。
`wx.Button`類有一個跨平臺的樣式標記:`wx.BU_EXACTFIT`。如果定義了這個標記,那么按鈕就不把系統默認的尺寸作為最小的尺寸,而是把能夠恰好填充標簽的尺寸作為最小尺寸。如果本地窗口部件支持的話,你可以使用標記`wx.BU_LEFT`, `wx.BU_RIGHT`, `wx.BU_TOP`, 和 `wx.BU_BOTTOM`來改變按鈕中標簽的對齊方式。每個標記對齊標簽到邊,該邊你根據標記的名字可以知道。正如我們在第一部分中所討論過的,`wx.Button`在被敲擊時觸發一個命令事件,事件類型是`EVT_BUTTON`。
### 如何生成一個位圖按鈕?
有時候,你可能想在你的按鈕上顯示一個圖片,而非一個文本標簽,如圖7.5所示。

在`wxPython`中,使用類`wx.BitmapButton`來創建一個位圖按鈕。處理一個`wx.BitmapButton`的代碼是與通用按鈕的代碼非常類似的,例7.5顯示了產生7.5的代碼。
**例7.5** **創建一個位圖按鈕**
```
import wx
class BitmapButtonFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Bitmap Button Example',
size=(200, 150))
panel = wx.Panel(self, -1)
bmp = wx.Image("bitmap.bmp", wx.BITMAP_TYPE_BMP).ConvertToBitmap()
self.button = wx.BitmapButton(panel, -1, bmp, pos=(10, 20))
self.Bind(wx.EVT_BUTTON, self.OnClick, self.button)
self.button.SetDefault()
self.button2 = wx.BitmapButton(panel, -1, bmp, pos=(100, 20),
style=0)
self.Bind(wx.EVT_BUTTON, self.OnClick, self.button2)
def OnClick(self, event):
self.Destroy()
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = BitmapButtonFrame()
frame.Show()
app.MainLoop()
```
與普通按鈕的主要的區別是你需要提供一個位圖,而非一個標簽。否則,構造器和大部分代碼是與文本按鈕的例子相同的。位圖按鈕在被敲擊時同樣產生`EVT_BUTTON`事件。
關于位圖按鈕有幾個有趣的特性。首先,一個樣式標記`wx.BU_AUTODRAW`,它是默認的。如果該標記是打開的,那么位圖將帶有一個3D的邊框,這使它看起來像一個文本按鈕(圖7.5中的左按鈕),并且按鈕比原位圖大幾個像素。如果該標記是關閉的,則位圖被簡單地繪制為按鈕而沒有邊框。通過設置`style`=0使圖7.5中右邊的按鈕關閉默認設置,它沒有了3D的效果。
默認情況下,給`wxPython`傳遞單個位圖作為主顯示的位圖,在當按鈕被按下或獲得焦點或無效時,`wxPython`自動創建一個標準的派生自主顯示的位圖的位圖作為此時顯示在按鈕上的位圖。如果自動創建的位圖不是你想要的,你可以使用下面的方法: `SetBitmapDisabled()`, `SetBitmapFocus()`,`SetBitmapLabel()`, 和`SetBitmap`-`Selected()`顯式地告訴`wxPython`你要使用哪個位圖。這些方法都要求一個`wx.Bitmap`對象作為參數,并且它們都有相應的`get`*()方法。
你不能通過使用標準的`wxWidgets` C++庫來合并一個位圖和文本。你可以創建一個包含文本的位圖。然而,正如我們將在通用按鈕問題討論中所看到的,`wxPython`有額外的方法來實現這一合并行為。
=== 如何創建開關按鈕(`toggle` `button`)?===
你可以使用`wx.ToggleButton`創建一個開關按鈕(`toggle` `button`)。開關按鈕(`toggle` `button`)看起來十分像文本按鈕,但它的行為更像復選框,它的選擇或非選擇狀態是可視化的。換句話說,當你按下一個開關按鈕(`toggle` `button`)時,它將一直保持被按下的狀態直到你再次敲擊它。
在`wx.ToggleButton`與父類`wx.Button`之間只有丙個區別:
1、當被敲擊時,`wx.ToggleButton`發送一個`EVT_TOGGLEBUTTON`事件。 2、`wx.ToggleButton`有`GetValue()`和`SetValue()`方法,它們處理按鈕的二進制狀態。
開關按鈕(`toggle` `button`)是有用的,它相對于復選框是另一好的選擇,特別是在工具欄中。記住,你不能使用`wxWidgets`提供的對象來將開關按鈕(`toggle` `button`)與位圖按鈕合并,但是`wxPython`有一個通用按鈕類,它提供了這種行為,我們將在下一節對其作討論。
### 什么是通用按鈕,我為什么要使用它?
通用按鈕是一個完全用`Python`重新實現的一個按鈕窗口部件,回避了本地系統窗口部件的用法。它的父類是`wx.lib.buttons.` `GenButton`。通用按鈕有通用位圖和切換按鈕。
這兒有幾個使用通用按鈕的原因:
1、通用按鈕比本地按鈕具有更好的跨平臺的外觀。另一方面,通用按鈕可能在具體的系統上看起來與本地按鈕有些微的不同。
2、使用通用按鈕,你對它的外觀有更多的控制權,并且能改變屬性,如3D斜面的寬度和顏色,而這對于本地控件可能是不允許的。
3、通用按鈕類允許特性的合并,而`wxWidget`按鈕不行。比如`GenBitmapTextButton`允許文本標簽和位圖的組合,`GenBitmapToggleButton`實現一個位圖切換按鈕。
4、如果你正在創建一個按鈕類,使用通用按鈕是較容易的。由于其代碼和參數是用`Python`寫的,所以當創建一個新的子類的時候,對于檢查和覆蓋,它們的可用性更好。
圖7.6顯示了實際的通用按鈕和常規按鈕的對照。
**圖7.6**

例7.6顯示了產生圖7.6的代碼。第二個導入語句:`import` `wx.lib.buttons` `as` `buttons`,是必須的,它使得通用按鈕類可用。
**例7.6** **創建和使用`wxPython`的通用按鈕**
```
import wx
import wx.lib.buttons as buttons
class GenericButtonFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Generic Button Example',
size=(500, 350))
panel = wx.Panel(self, -1)
sizer = wx.FlexGridSizer(1, 3, 20, 20)
b = wx.Button(panel, -1, "A wx.Button")
b.SetDefault()
sizer.Add(b)
b = wx.Button(panel, -1, "non-default wx.Button")
sizer.Add(b)
sizer.Add((10,10))
b = buttons.GenButton(panel, -1, 'Genric Button')#基本的通用按鈕
sizer.Add(b)
b = buttons.GenButton(panel, -1, 'disabled Generic')#無效的通用按鈕
b.Enable(False)
sizer.Add(b)
b = buttons.GenButton(panel, -1, 'bigger')#自定義尺寸和顏色的按鈕
b.SetFont(wx.Font(20, wx.SWISS, wx.NORMAL, wx.BOLD, False))
b.SetBezelWidth(5)
b.SetBackgroundColour("Navy")
b.SetForegroundColour("white")
b.SetToolTipString("This is a BIG button...")
sizer.Add(b)
bmp = wx.Image("bitmap.bmp", wx.BITMAP_TYPE_BMP).ConvertToBitmap()
b = buttons.GenBitmapButton(panel, -1, bmp)#通用位圖按鈕
sizer.Add(b)
b = buttons.GenBitmapToggleButton(panel, -1, bmp)#通用位圖開關按鈕
sizer.Add(b)
b = buttons.GenBitmapTextButton(panel, -1, bmp, "Bitmapped Text",
size=(175, 75))#位圖文本按鈕
b.SetUseFocusIndicator(False)
sizer.Add(b)
b = buttons.GenToggleButton(panel, -1, "Toggle Button")#通用開關按鈕
sizer.Add(b)
panel.SetSizer(sizer)
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = GenericButtonFrame()
frame.Show()
app.MainLoop()
```
在例7.6中,通用按鈕的用法非常類似于常規按鈕。通用按鈕產生與常規按鈕同樣的`EVT_BUTTON` 和 `EVT_TOGGLEBUTTON`事件。通用按鈕引入了`GetBevelWidth()`和`SetBevelWidth()`方法來改變3D斜面效果。它們用在了圖7.6中大按鈕上。
通用位圖按鈕類`GenBitmapButton`工作的像標準的`wxPython`版本。在構造器中。`GenBitmapTextButton`要求先要一個位圖,然后是文本。通用類`GenToggleButton`,`GenBitmapToggleButton`,和 `GenBitmapTextToggleButton`與非開關版的一樣,并且對于處理按鈕的開關狀態響應于`GetToggle()` 和 `SetToggle()`。
在下一節,我們將討論關于使你的用戶能夠輸入或觀看一個數字值的方案。
## 輸入并顯示數字
有時你想要顯示圖形化的數字信息,或你想讓用戶不必使用鍵盤來輸入一個數字量。在這一節,我們將瀏覽`wxPython`中用于數字輸入和顯示的工具:滑塊(`slider`)、微調控制框和顯示量度的標尺。
### 如何生成一個滑塊?
滑塊是一個窗口部件,它允許用戶通過在該控件的尺度內拖動指示器來選擇一個數值。在`wxPython`中,該控件類是`wx.Slider`,它包括了滑塊的當前值的只讀文本的顯示。圖7.7顯示了水平和垂直滑塊的例子。
**圖7.7**

滑塊的基本使用是十分簡單的,但是你可以增加許多事件。
**如何使用滑塊**
例7.7是產生圖7.7的例子。
**例7.7** **水平和垂直滑塊的顯示代碼**
```
import wx
class SliderFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Slider Example',
size=(300, 350))
panel = wx.Panel(self, -1)
self.count = 0
slider = wx.Slider(panel, 100, 25, 1, 100, pos=(10, 10),
size=(250, -1),
style=wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | wx.SL_LABELS )
slider.SetTickFreq(5, 1)
slider = wx.Slider(panel, 100, 25, 1, 100, pos=(125, 70),
size=(-1, 250),
style=wx.SL_VERTICAL | wx.SL_AUTOTICKS | wx.SL_LABELS )
slider.SetTickFreq(20, 1)
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = SliderFrame()
frame.Show()
app.MainLoop()
```
通常,當你使用`wx.Slider`類時,所有你所需要的就是一個構造函數,它與別的調用不同,如下所示:
`wx.Slider(parent`, `id`, `value`, `minValue`, `maxValue`, `pos`=`wxDefaultPosition`, `size`=`wx.DefaultSize`, `style`=`wx.SL_HORIZONTAL`, `validator`=`wx.DefaultValidator`, `name`="`slider`")
`value`是滑塊的初始值,而`minValue`和`maxValue`是兩端的值。
**使用滑塊樣式工作**
滑塊的樣式管理滑塊的位置和方向,如下表7.9所示。
**表7.9** **`wx.Slider`的樣式**
`wx.SL_AUTOTICKS`:如果設置這個樣式,則滑塊將顯示刻度。刻度間的間隔通過`SetTickFreq`方法來控制。
`wx.SL_HORIZONTAL`:水平滑塊。這是默認值。
`wx.SL_LABELS`:如果設置這個樣式,那么滑塊將顯示兩頭的值和滑塊的當前只讀值。有些平臺可能不會顯示當前值。
`wx.SL_LEFT`:用于垂直滑塊,刻度位于滑塊的左邊。
`wx.SL_RIGHT`:用于垂直滑塊,刻度位于滑塊的右邊。
`wx.SL_TOP`:用于水平滑塊,刻度位于滑塊的上部。
`wx.SL_VERTICAL`:垂直滑塊。
如果你想通過改變滑塊中的值來影響你的應用程序中的其它的部分,那么這兒有幾個你可使用的事件。這些事件與窗口滾動條所發出的是相同的,詳細的說明參見第8章的滾動條部分。
表7.10列出了你可用于滑塊的`Set`*()方法。每個`Set`*()方法都有一個對應的`Get`方法——`Get`方法的描述參考其對應的`Set`*()方法。
**表7.10**
`GetRange()` `SetRange(minValue`, `maxValue)`:設置滑塊的兩端值。
`GetTickFreq()` `SetTickFreq(n`, `pos)`:使用參數n設置刻度的間隔。參數`pos`沒有被使用,但是它仍然是必要的,將它設置為1。
`GetLineSize()` `SetLineSize(lineSize)`:設置你每按一下方向鍵,滑塊所增加或減少的值。
`GetPageSize()` `SetPageSize(pageSize)`:設置你每按一下`PgUp`或`PgDn`鍵,滑塊所增加或減少的值。
`GetValue()` `SetValue(value)`:設置滑塊的值。
盡管滑塊提供了一個可能范圍內的值的快速的可視化的表示,但是它們也有兩個缺點。其一是它們占據了許多的空間,另外就是使用鼠標精確地設置滑塊是困難的。下面我們將討論的微調控制器解決了上面的這兩個問題。
### 如何得到那些靈巧的上下箭頭按鈕?
微調控制器是文本控件和一對箭頭按鈕的組合,它用于調整數字值,并且在你要求一個最小限度的屏幕空間的時候,它是替代滑塊的最好選擇。圖7.8顯示了`wxPython`的微調控制器控件。
圖7.8

在`wxPython`中,類`wx.SpinCtrl`管理微調按鈕和相應的文本顯示。在接下來的部分,我們將創建一個微調控制器。
**如何創建一個微調控制器**
要使用`wx.SpinCtrl`來改變值,可通過按箭頭按鈕或通過在文本控件中輸入。鍵入的非數字的文本將被忽略,盡管控件顯示的是鍵入的非數字的文本。一個超出范圍的值將被認作是相應的最大或最小值,盡管顯示的是你輸入的值。例7.8顯示了`wx.SpinCtrl`的用法。
**例7.8** **使用`wx.SpinCtrl`**
```
import wx
class SpinnerFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Spinner Example',
size=(100, 100))
panel = wx.Panel(self, -1)
sc = wx.SpinCtrl(panel, -1, "", (30, 20), (80, -1))
sc.SetRange(1,100)
sc.SetValue(5)
if __name__ == '__main__':
app = wx.PySimpleApp()
SpinnerFrame().Show()
app.MainLoop()
```
幾乎微調控件所有復雜的東西都是在其構造函數中,其構造函數如下:
`wx.SpinCtrl(parent`, `id`=-1, `value`=`wx.EmptyString`, `pos`=`wx.DefaultPosition`, `size`=`wx.DefaultSize`, `style`=`wx.SP_ARROW_KEYS`, `min`=0, `max`=100, `initial`=0, `name`="`wxSpinCtrl`")
參數`value`是虛設的。使用`initial`參數來設置該控件的值,并使用`min`和`max`來設置該控件的范圍。
對于`wx.SpinCtrl`有兩個樣式標記。默認樣式是`wx.SP_ARROW_KEYS`,它允許用戶通過鍵盤上的上下箭頭鍵來改變控件的值。樣式`wx.SP_WRAP`使得控件中的值可以循環改變,也就是說你通過箭頭按鈕改變控件中的值到最大或最小值時,如果再繼續,值將變為最小或最大,從一個極端到另一個極端。你也可以捕獲`EVT_SPINCTRL`事件,它在當控件的值改變時產生(即使改變是直接由文本輸入引起的)。如果文本改變了,將引發一個`EVT_TEXT`事件,就如同你使用一個單獨的文本控件時一樣。
如例7.8所示,你可以使用`SetRange(minVal`, `maxVal)` 和 `SetValue(value)`方法來設置范圍和值。`SetValue()`函數要求一個字符串或一個整數。要得到值,使用方法:`GetValue()`(它返回一個整數), `GetMin()`, 和 `GetMax()`。
當你需要對微調控制器的行為有更多的控制時,如允許浮點數或一個字符串的列表,你可以把一個`wx.SpinButton`和一個`wx.TextCtrl`放到一起,并在它們之間建立一個聯系。然后捕獲來自`wx.SpinButton`的事件,并更新`wx.TextCtrl`中的值。
### 如何生成一個進度條?
如果你只想圖形化地顯示一個數字值而不允許用戶改變它,那么使用相應的`wxPython`窗口部件`wx.Gauge`。 相關的例子就是圖7.9所顯示的進度條。
**圖7.9**

例7.9顯示了產生圖7.9的代碼。與本章中許多別的例子不同的是,這里我們增加了一個事件處理器。下面的代碼在空閉時調整標尺的值,使得值周而復始的變化。
**例7.9** **顯示并更新一個`wx.Gauge`**
```
import wx
class GaugeFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Gauge Example',
size=(350, 150))
panel = wx.Panel(self, -1)
self.count = 0
self.gauge = wx.Gauge(panel, -1, 50, (20, 50), (250, 25))
self.gauge.SetBezelFace(3)
self.gauge.SetShadowWidth(3)
self.Bind(wx.EVT_IDLE, self.OnIdle)
def OnIdle(self, event):
self.count = self.count + 1
if self.count = 50:
self.count = 0
self.gauge.SetValue(self.count)
if __name__ == '__main__':
app = wx.PySimpleApp()
GaugeFrame().Show()
app.MainLoop()
```
`wx.Gauge`的構造函數類似于其它的數字的窗口部件:
`wx.Gauge(parent`, `id`, `range`, `pos`=`wx.DefaultPosition`, `size`=`wx.DefaultSize`, `style`=`wx.GA_HORIZONTAL`, `validator`=`wx.DefaultValidator`, `name`="`gauge`")
當你使用參數`range`來指定數字值時,該值代表標尺的上限,而下限總是0。默認樣式`wx.GA_HORIZONTAL`提供了一個水平條。要將它旋轉90度,使用`wx.GA_VERTICAL`樣式。如果你是在`Windows`上,那么樣式`wx.GA_PROGRESSBAR`給你的是來自`Windows`工具包的本地化的進度條。
作為一個只讀控件,`wx.Gauge`沒有事件。然而,它的屬性你可以設置。你可以使用`GetValue()`, `Set`-`Value(pos)`, `GetRange()`, 和 `SetRange(range)`來調整它的值和范圍。如果你是在`Windows`上,并且沒有使用本地進度條樣式,那么你可以使用`SetBezelFace(width)` `and` `SetShadowWidth()`來改變3D效果的寬度。
## 給用戶以選擇
幾乎每個應用程序都要求用戶在一套預先定義的選項間進行選擇。在`wxPython`中,有多種窗口部件幫助用戶處理這種任務,包括復選框、單選按鈕、列表框和組合框。接下來的部分將介紹這些窗口部件。
### 如何創建一個復選框?
復選框是一個帶有文本標簽的開關按鈕。復選框通常成組的方式顯示,但是每個復選框的開關狀態是相互獨立的。當你有一個或多個需要明確的開關狀態的選項時,可以使用復選框。圖7.10顯示了一組復選框。
**圖7.10**

在`wxPython`中復選框很容易使用。它們是`wx.CheckBox`類的實例,并且通過把它們一起放入一個父容器中可以讓它們在一起顯示。例7.10提供了生成圖7.10的代碼。
**例7.10** **插入三個復選框到一個框架中**
```
import wx
class CheckBoxFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Checkbox Example',
size=(150, 200))
panel = wx.Panel(self, -1)
wx.CheckBox(panel, -1, "Alpha", (35, 40), (150, 20))
wx.CheckBox(panel, -1, "Beta", (35, 60), (150, 20))
wx.CheckBox(panel, -1, "Gamma", (35, 80), (150, 20))
if __name__ == '__main__':
app = wx.PySimpleApp()
CheckBoxFrame().Show()
app.MainLoop()
```
`wx.CheckBox`有一個典型的`wxPython`構造函數:
`wx.CheckBox(parent`, `id`, `label`, `pos`=`wx.DefaultPosition`, `size`=`wx.DefaultSize`, `style`=0, `name`="`checkBox`")
`label`參數是復選框的標簽文本。復選框沒有樣式標記,但是它們產生屬于自己的獨一無二的命令事件:`EVT_CHECKBOX`。`wx.CheckBox`的開關狀態可以使用`GetValue()`和`SetValue(state)`方法來訪問,并且其值是一個布爾值。`IsChecked()`方法等同于`GetValue()`方法,只是為了讓代碼看起來更易明白。
### 如何創建一組單選按鈕(radio
**`button`)?**
單選按鈕是一種允許用戶從幾個選項中選擇其一的窗口部件。與復選框不同,單選按鈕是顯式地成組配置,并且只能選擇其中一個選項。當選擇了新的選項時,上次的選擇就關閉了。單選按鈕的使用比復選框復雜些,因為它需要被組織到一組中以便使用。`radio` `button`的名字得自于老式轎車上有著同樣行為的成組的選擇按鈕。
在`wxPython`中,有兩種方法可以創建一組單選按鈕。其一,`wx.RadioButton`,它要求你一次創建一個按鈕,而`wx.RadioBox`使你可以使用單一對象來配置完整的一組按鈕,這些按鈕顯示在一個矩形中。
`wx.RadioButton`類更簡單些,在單選按鈕對其它窗口部件有直接影響或單選按鈕不是布置在一個單一的矩形中的情況下,它是首選。圖7.11顯示了一組`wx.RadioButton`對象的列子。
**圖7.11**

我們在這個例子中使用`wx.RadioButton`的原因是因為每個單選按鈕控制著一個關聯的文本控件。由于窗口部件是位于這組單選按鈕之外的,所以我們不能只用一個單選按鈕框。
**如何創建單選按鈕**
例7.11顯示了圖7.11的代碼,它管理單選按鈕和文本控件之間的聯系。
**例7.11** **使用`wx.RadioButton`來控制另一個窗口部件**
```
import wx
class RadioButtonFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Radio Example',
size=(200, 200))
panel = wx.Panel(self, -1)
#創建單選按鈕
radio1 = wx.RadioButton(panel, -1, "Elmo", pos=(20, 50), style=wx.RB_GROUP)
radio2 = wx.RadioButton(panel, -1, "Ernie", pos=(20, 80))
radio3 = wx.RadioButton(panel, -1, "Bert", pos=(20, 110))
#創建文本控件
text1 = wx.TextCtrl(panel, -1, "", pos=(80, 50))
text2 = wx.TextCtrl(panel, -1, "", pos=(80, 80))
text3 = wx.TextCtrl(panel, -1, "", pos=(80, 110))
self.texts = {"Elmo": text1, "Ernie": text2, "Bert": text3}#連接按鈕和文本
for eachText in [text2, text3]:
eachText.Enable(False)
for eachRadio in [radio1, radio2, radio3]:#綁定事件
self.Bind(wx.EVT_RADIOBUTTON, self.OnRadio, eachRadio)
self.selectedText = text1
def OnRadio(self, event):#事件處理器
if self.selectedText:
self.selectedText.Enable(False)
radioSelected = event.GetEventObject()
text = self.texts[radioSelected.GetLabel()]
text.Enable(True)
self.selectedText = text
if __name__ == '__main__':
app = wx.PySimpleApp()
RadioButtonFrame().Show()
app.MainLoop()
```
我們創建了單選按鈕和文本框,然后使用字典來建立它們間的連接。一個`for`循環使得兩個文本框無效,另一個`for`循環綁定單選按鈕命令事件。當事件發生的時候,當前活動的文本框變為無效,與被敲擊的按鈕相匹配的文本框變為有效。
`wx.RadioButton`的使用類似于是`wx.CheckBox`。它們的構造函數幾乎是相同的,如下所示:
`wx.RadioButton(parent`, `id`, `label`, `pos`=`wx.DefaultPosition`, `size`=`wx.DefaultSize`, `style`=0, `validator`=`wx.DefaultValidator`, `name`="`radioButton`")
在復選框中,`label`是相應按鈕的顯示標簽。
`wx.RB_GROUP`樣式聲明該按鈕位于一組單選按鈕開頭。一組單選按鈕的定義是很重要的,因為它控制開關行為。當組中的一個按鈕被選中時,先前被選中的按鈕被切換到未選中狀態。在一個單選按鈕使用`wx.RB_GROUP`被創建后,所有后來的被添加到相同父窗口部件中的單選按鈕都被添加到同一組,直到另一單選按鈕使用`wx.RB_GROUP`被創建,并開始下一個組。在例7.11中,第一個單選按鈕是使用`wx.RB_GROUP`聲明的,而后來的沒有。結果導致所有的按鈕都被認為在同一組中,這樣一來,敲擊它們中的一個時,先前被選中按鈕將關閉。
**使用單選框**
通常,如果你想去顯示一組按鈕,分別聲明它們不是最好的方法。取而代之,`wxPython`使用`wx.RadioBox`類讓你能夠創建一個單一的對象,該對象包含了完整的組。如圖7.12所示,它看起來非常類似一組單選按鈕。
**圖7.12**

要使用`wx.RadioBox`類,你所需要的全部就是構造函數。例7.12顯示了圖7.12的代碼。
**例7.12** **建造單選框**
```
import wx
class RadioBoxFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Radio Box Example',
size=(350, 200))
panel = wx.Panel(self, -1)
sampleList = ['zero', 'one', 'two', 'three', 'four', 'five',
'six', 'seven', 'eight']
wx.RadioBox(panel, -1, "A Radio Box", (10, 10), wx.DefaultSize,
sampleList, 2, wx.RA_SPECIFY_COLS)
wx.RadioBox(panel, -1, "", (150, 10), wx.DefaultSize,
sampleList, 3, wx.RA_SPECIFY_COLS | wx.NO_BORDER)
if __name__ == '__main__':
app = wx.PySimpleApp()
RadioBoxFrame().Show()
app.MainLoop()
```
`wx.RadioBox`的構造函數比簡單的單選按鈕更復雜,因為你需要去一下子為所有的按鈕指定數據,如下所示:
```
wx.RadioBox(parent, id, label, pos=wx.DefaultPosition,
size=wxDefaultSize, choices=None, majorDimension=0,
style=wx.RA_SPECIFY_COLS, validator=wx.DefaultValidator,
name="radioBox")
```
`label`參數是靜態文本,它顯示在單選框的邊框上。這些按鈕使用`choices`參數指定,它是一個`Python`的字符串標簽的序列。
如同網格的`sizer`一樣,你通過使用規定一個維數的尺寸來指定`wx.RadioBox`的尺度,`wxPython`在另一維度上自動填充。維度的主尺寸使用`majorDimension`參數指定。哪一維是主要的由樣式標記決定。默認值是`wx.RA_SPECIFY_COLS`。在本例中,左框的列數被設置為2,右框的列數被設置為3,行數由`choices`列表中的元素數量動態的決定。如果你想得到相反的行為,你要將樣式設置為`wx.RA_SPECIFY_ROWS`。如果你想在單選框被敲擊時響應命令事件,那么這個命令事件是`EVT_RADIOBOX`。
`wx.RadioBox`類有許多方法來管理框中的不同的單選按鈕。這些方法使你能夠處理一個特定的內部按鈕,傳遞該按鈕的索引。索引以0為開始,并按嚴格的順序展開,它的順序就是按鈕標簽傳遞給構造函數的順序。表7.11列出了這些方法。
**表7.11** **`wx.RadioBox`的方法**
`EnableItem(n`, `flag)`:`flag`參數是一個布爾值,它用于使索引為n的按鈕有效或無效。要使整個框立即有效,使用`Enable()`。
`FindString(string)`:根據給定的標簽返回相關按鈕的整數索引值,如果標簽沒有發現則返回-1。
`GetCount()`:返回框中按鈕的數量。
`GetItemLabel(n)` `SetItemLabel(n`, `string)`:返回或設置索引為n的按鈕的字符串標簽。
`GetSelection()` `GetStringSelection()` `SetSelection(n)` `SetStringSelection(` `string)`:`GetSelection()` 和 `SetSelection()`方法處理當前所選擇的單選按鈕的整數索引。`GetStringSelection()`返回當前所選擇的按鈕的字符串標簽,`SetStringSelection()`改變所選擇的按鈕的字符串標簽為給定值。沒有`set`*()產生`EVT_RADIOBOX`事件。
`ShowItem(item`, `show)`:`show`參數是一個布爾值,用于顯示或隱藏索引為`item`的按鈕。
單選按鈕不是給用戶一系列選擇的唯一方法。列表框和組合框占用的空間也少,也可以被配置來讓用戶從同一組中作多個選擇。
### 如何創建一個列表框?
列表框是提供給用戶選擇的另一機制。選項被放置在一個矩形的窗口中,用戶可以選擇一個或多個。列表框比單選按鈕占據較少的空間,當選項的數目相對少的時候,列表框是一個好的選擇。然而,如果用戶必須將滾動條拉很遠才能看到所有的選項的話,那么它的效用就有所下降了。圖7.13顯示了一個`wxPython`列表框。

在`wxPython`中,列表框是類`wx.ListBox`的元素。該類的方法使你能夠處理列表中的選擇。
**如何創建一個列表框**
例7.13顯示了產生圖7.13的代碼
**例7.13** **使用`wx.ListBox`**
```
import wx
class ListBoxFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'List Box Example',
size=(250, 200))
panel = wx.Panel(self, -1)
sampleList = ['zero', 'one', 'two', 'three', 'four', 'five',
'six', 'seven', 'eight', 'nine', 'ten', 'eleven',
'twelve', 'thirteen', 'fourteen']
listBox = wx.ListBox(panel, -1, (20, 20), (80, 120), sampleList,
wx.LB_SINGLE)
listBox.SetSelection(3)
if __name__ == '__main__':
app = wx.PySimpleApp()
ListBoxFrame().Show()
app.MainLoop()
```
`wx.ListBox`的構造函數類似于單選框的,如下所示:
`wx.ListBox(parent`, `id`, `pos`=`wx.DefaultPosition`, `size`=`wx.DefaultSize`, `choices`=`None`, `style`=0, `validator`=`wx.DefaultValidator`, `name`="`listBox`")
單選框和列表框的主要區別是`wx.ListBox`沒有`label`屬性。顯示在列表中的元素放置在參數`choices`中,它是一個字符串的序列。列表框有三種互斥的樣式,它決定用戶如何從列表框中選擇元素,說明在表7.12中。
用戶通常對于多選有一些問題,因為它們一般希望見到的是單選列表,對于多選來說可能是有挑戰性的(就像單選題和多選題一樣),尤其是對于那些易受困擾的用戶。如果你使用了一個多選的列表,我們建議你清楚地標明該列表。
**表7.12** **列表框的選擇類型樣式**
`wx.LB_EXTENDED`:用戶可以通過使用`shift`并敲擊鼠標來選擇一定范圍內的連續的選項,或使用等同功能的按鍵。
`wx.LB_MULTIPLE`:用戶可以一次選擇多個選項(選項可以是不連續的)。實際上,在這種情況下,列表框的行為就像是一組復選框。
`wx.LB_SINGLE`:用戶一次只能選一個選項。實際上,在這種情況下,列表框的行為就像是一組單選按鈕。
有三種控制`wx.ListBox`中滾動條的顯示的樣式,如表7.13所示。
**表7.13** **列表框的滾動條類型樣式**
`wx.LB_ALWAYS_SB`:列表框將始終顯示一個垂直的滾動條,不管有沒有必要。
`wx.LB_HSCROLL`:如果本地控支持,那么列表框在選擇項太多時,將創建一個水平滾動條。
`wx.LB_HSCROLL`:列表框只在需要的時候顯示一個垂直的滾動條。這是默認樣式。
還有一個樣式`wx.LB_SORT`,它使得列表中的元素按字母順序排序。
有兩個專用于`wx.ListBox`的命令事件。`EVT_LISTBOX`事件在當列表中的一個元素被選擇時觸發(即使它是當前所選擇的元素)。如果列表被雙擊,`EVT_LISTBOX_DCLICK`事件發生。
有一些專用于列表框的方法,你可以用來處理框中的項目。表7.14對許多的方法作了說明。列表框中的項目索引從0開始。
一旦你有了一個列表框,自然就想把它與其它的窗口部件結合起來使用,如下拉菜單,或復選框。在下一節,我們對此作討論。
**表7.14** **列表框的方法**
`Append(item)`:把字符串項目添加到列表框的尾部。
`Clear()`:清空列表框。
`Delete(n)`:刪除列表框中索引為n的項目。
`Deselect(n)`:在多重選擇列表框中,導致位于位置n的選項取消選中。在其它樣式中不起作用。
`FindString(string)`:返回給定字符串的整數位置,如果沒有發現則返回-1。
`GetCount()`:返回列表中字符串的數量。
`GetSelection()` `SetSelection(n`, `select)` `GetStringSelection()` `SetStringSelection(string`, `select)` `GetSelections()`:`GetSelection()`得到當前選擇項的整數索引(僅對于單選列表)。對于多選列表,使用`GetSelections()`來返回包含所選項目的整數位置的元組。對于單選列表,`GetStringSelection()`返回當前選擇的字符串。相應的`set`方法使用布爾值參數`select`設置指定字符串或索引選項的狀態。使用這種方法改變選擇不觸發`EVT_LISTBOX`事件。
`GetString(n)` `SetString(n`, `string)`:得到或設置位置n處的字符串。
`InsertItems(items`, `pos)`:插入參數`items`中的字符串列表到該列表框中`pos`參數所指定的位置前。位置0表示把項目放在列表的開頭。
`Selected(n)`:返回對應于索引為n的項目的選擇狀態的布爾值。
`Set(choices)`:重新使用`choices`的內容設置列表框。
### 如何合并復選框和列表框?
你可以使用類`wx.CheckListBox`來將復選框與列表框合并。圖7.14顯示了列表框和復選框在合并在一起的例子。
**圖7.14**

`wx.CheckListBox`的構造函數和大多數方法與`wx.ListBox`的相同。它有一個新的事件:`wx.EVT_CHECKLISTBOX`,它在當列表中的一個復選框被敲擊時觸發。它有兩個管理復選框的新的方法:`Check(n`, `check)`設置索引為n的項目的選擇狀態,`IsChecked(item)`在給定的索引的項目是選中狀態時返回`True`。
### 如果我想要下拉形式的選擇該怎么做?
下拉式選擇是一種僅當下拉箭頭被敲擊時才顯示選項的選擇機制。它是顯示所選元素的最簡潔的方法,當屏幕空間很有限的時候,它是最有用的。圖7.15顯示了一個關閉的下拉式選擇。圖7.16顯示了一個打開的下拉式選擇。
**圖7.15**

**圖7.16**

下拉式選擇的使用與標準的列表框是很相似的。例7.14顯示了如何創建一個下拉式選擇。
**例7.14**
```
import wx
class ChoiceFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Choice Example',
size=(250, 200))
panel = wx.Panel(self, -1)
sampleList = ['zero', 'one', 'two', 'three', 'four', 'five',
'six', 'seven', 'eight']
wx.StaticText(panel, -1, "Select one:", (15, 20))
wx.Choice(panel, -1, (85, 18), choices=sampleList)
if __name__ == '__main__':
app = wx.PySimpleApp()
ChoiceFrame().Show()
app.MainLoop()
```
`wx.Choice`的構造函數與列表框的基本相同:
`wx.Choice(parent`, `id`, `pos`=`wx.DefaultPosition`, `size`=`wx.DefaultSize`, `choices`=`None`, `style`=0, `validator`=`wx.DefaultValidator`, `name`="`choice`")
`wx.Choice`沒有專門的樣式,但是它有獨特的命令事件:`EVT_CHOICE`。幾乎表7.14中所有適用于單選列表框的方法都適用于`wx.Choice`對象。
### 我能夠將文本域與列表合并在一起嗎?
將文本域與列表合并在一起的窗口部件稱為組合框,其本質上是一個下拉選擇和文本框的組合。圖7.17顯示了一個組合框。
**圖7.17** 左邊是`wx.CB_DropDOWN`樣式,右邊是`wx.CB_SIMPLE`樣式

在`Windows`上,你可以使用右邊的樣式,它是一個列表框和文本框的組合。
創建組合框的代碼與我們已經見過的選擇是類似的。該類是`wx.ComboBox`,它是`wx.Choice`的一個子類。例7.15顯示了圖7.17的代碼:
**例7.15**
```
import wx
class ComboBoxFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Combo Box Example',
size=(350, 300))
panel = wx.Panel(self, -1)
sampleList = ['zero', 'one', 'two', 'three', 'four', 'five',
'six', 'seven', 'eight']
wx.StaticText(panel, -1, "Select one:", (15, 15))
wx.ComboBox(panel, -1, "default value", (15, 30), wx.DefaultSize,
sampleList, wx.CB_DropDOWN)
wx.ComboBox(panel, -1, "default value", (150, 30), wx.DefaultSize,
sampleList, wx.CB_SIMPLE)
if __name__ == '__main__':
app = wx.PySimpleApp()
ComboBoxFrame().Show()
app.MainLoop()
```
`wx.ComboBox`的構造函數如下所示:
```
wx.ComboBox(parent, id, value="", pos=wx.DefaultPosition,
size=wx.DefaultSize, choices, style=0,
validator=wx.DefaultValidator, name="comboBox")
```
對于`wx.ComboBox`來說有4種樣式。其中的兩種決定了如何繪制組合框:`wx.CB_DropDOWN`創建一個帶有下拉列表的組合框,`wx.CB_SIMPLE`創建一個帶有列表框的組合框。在`Windows`上你可以只使用`wx.CB_SIMPLE`樣式。任何組合框都可以被指定為`wx.CB_READONLY`樣式,它防止用戶在文本域中鍵入。當組合框被指定為只讀時,所做的選擇必須來自于選擇列表的元素之一,即使你用程序來設置它也不行。最后`wx.CB_SORT`樣式導致選擇列表中的元素按字母順序顯示。
由于`wx.ComboBox`是`wx.Choice`的子類,所有的`wx.Choice`的方法都能被組合框調用,如表7.14所示。另外,還有許多方法被定義來處理文本組件,它們的行為同`wx.TextCtrl`(參見表7.4),所定義的方法有`Copy()`, `Cut()`, `GetInsertionPoint()`, `GetValue()`, `Paste()`, `Replace(from`,`to`, `text)`, `Remove(from`, `to)`, `SetInsertionPoint(pos)`, `SetInsertionPointEnd()`,和 `SetValue()`。
## 本章小結
在這一章中,我們給你展示了如何使用`wxPython`中許多最基本和常用的控件。這些通用的版本在跨平臺使用時顯得一致性較好。
1、對于靜態文本標簽的顯示,你可以使用`wx.StaticText`類。還有一個完全用`wxPython`實現的版本,名為`wx.lib.stattext.GenStaticText`。
2、如果你需要一個控件以讓用戶輸入文本,那么使用類`wx.TextCtrl`。它允許單行和多行的輸入,還有密碼掩飾和其它的功用。如果本地控支持它,你可以使用`wx.TextCtrl`來得到樣式文本。樣式是`wx.Text`-`Attr`類的實例,`wx.Font`包含字體信息。對于所有的系統,你可以使用類`wx.stc.StyledTextCtrl`(它是`wxPython`對開源`Scintilla`文本組件的封裝)在一個可編輯的文本組件中實現顏色和字體樣式。
3、創建按鈕,使用`wx.Button`類,它也有一個通用版`wx.lib.buttons.GenButton`。按鈕可以使用位圖來代替一個文本標簽(`wx.BitmapButton`),或在按下和未按下之間有一個開關狀態。還有一個等價于位圖和開關按鈕的通用版,它比標準版有更全面的特性。
4、有一些方法用于選擇或顯示數字值。你可以使用`wx.Slider`類來顯示一個垂直或水平的滑塊。`wx.SpinCtrl`顯示一個可以使用上下按鈕來改變數字值的文本控件。`wx.Gauge`控件顯示一個進度條指示器。
5、你可以從一系列的控件中選出讓用戶從列表選項作出選擇的最佳控件,最佳控件所應考慮的條件是選項的數量,用戶能否多選和你想使用的屏幕空間的總量。復選框使用`wx.CheckBox`類。這兒有兩個方法去得到單選按鈕:`wx.RadioButton`給出單個單選按鈕,而`wx.RadioBox`給出顯示在一起的一組按鈕。這兒有幾個列表顯示控件,它們的用法相似。列表框的創建使用`wx.ListBox`,并且你可以使用`wx.CheckListBox`來增加復選框。對于更簡潔的下拉式,使用`wx.Choice.` `wx.ComboBox`合并了列表和文本控件的特性。
到目前為止,我們已經涉及了基本的常用窗口部件,在接下來的章節,我們將討論不同種類的框架,它可以用這些框架來包含我們已經涉及了基本的常用窗口部件。
- 活學活用wxPython
- 前言
- 致謝
- 關于本書
- 第一部分
- 第一章 歡迎使用wxPython
- 第二章 給wxPython程序一個堅實的基礎
- 第三章 在事件驅動環境中開發
- 第四章 用PyCrust使得wxPython更易處理
- 第五章 繪制藍圖
- 第六章 使用wxPython基本構件
- 第二部分 基礎wxPython
- 第七章 使用基礎控件
- 第八章 將構件放入窗體中
- 第九章 通過對話框讓用戶選擇
- 第十章 創建和使用wxPython菜單
- 第十一章 使用sizer放置構件
- 第十二章 操作基本圖像
- 第三部分 高級wxPython
- 第十三章 建造列表控件并管理列表項
- 第十四章 網格控件
- 第十五章 樹形控件
- 第十六章 在應用程序中加入HTML
- 第十七章 wxPython的打印構架
- 第十八章 使用wxPython的其他功能