# 第四章 用PyCrust使得wxPython更易處理
1. [用PyCrust使得wxPython更易處理](#A.2BdSg-PyCrust.2BT39flw-wxPython.2BZvRmE1kEdAY-)
1. [如何與wxPython程序交互?](#A.2BWYJPVU4O-wxPython.2Begtej06kTpL.2FHw-)
2. [PyCrust的有用特性是什么?](#PyCrust.2BdoRnCXUocnlgJ2YvTsBOSP8f-)
1. [自動完成](#A.2BgepSqFuMYhA-)
2. [調用提示和參數默認](#A.2BjAN1KGPQeTpUjFPCZXCe2Iuk-)
3. [語法高亮](#A.2Bi.2B1s1ZrYTq4-)
4. [Python](#Python)
5. [命令重調用](#A.2BVH1O5JHNjAN1KA-)
6. [剪切和粘貼](#A.2BUmpSB1SMfJiNNA-)
7. [標準shell環境](#A.2BaAdRxg-shell.2Bc69Ygw-)
8. [動態更新](#A.2BUqhgAWb0ZbA-)
3. [PyCrust](#PyCrust)
1. [Namespace標簽](#Namespace.2BaAd7fg-)
2. [Display標簽](#Display.2BaAd7fg-)
3. [Session標簽](#Session.2BaAd7fg-)
4. [Dispatcher](#Dispatcher)
4. [如何將PyCrust應用于wxPython應用程序](#A.2BWYJPVVwG-PyCrust.2BXpR1KE6O-wxPython.2BXpR1KHoLXo8-)
5. [在Py包中還有其它什么?](#A.2BVyg-Py.2BUwVOLY.2FYZwlRdluDTsBOSP8f-)
1. [使用GUI程序工作](#A.2BT391KA-GUI.2Begtej13lT1w-)
2. [使用支持模塊工作](#A.2BT391KGUvYwFqIVdXXeVPXA-)
6. [如何在wxPython中使用Py包中的模塊?](#A.2BWYJPVVco-wxPython.2BTi1Pf3Uo-Py.2BUwVOLXaEaiFXVw.3F)
7. [本章小結](#A.2BZyx64FwPftM-)
`PyCrust`是一個圖形化的`shell`程序,使用`wxPython`寫成,它可以用來幫助你分析你的`wxPython`程序。
為何稱它為`PyCrust`?這是因為當`Patrick?O’Brien`使用`wxPython`創建一個交互式的`Python` `shell`時,`PyShell`已被使用了,所以選用了`PyCrust`這個名字。
`PyCrust`是`Py`包中的一部分,`Py`包目前被包含在`wxPython`中。這個`Py`包還包含了其它相關功能的程序,這包括`PyFilling`, `PyAlaMode`, `PyAlaCarte`, 和`PyShell`。這些程序每個都是想成為融圖形化、點擊環境 、`wxPython`的交互、內省運行特點為一體。但是`PyCrust`表現最完善。
在這章中,我們將說明`PyCrust`和那些相關程序都干些什么,還有,你如何使用它們才能使得你用`wxPython`工作得更流暢。我們以談論普通的`Python` `shell`作為開始,然后專門針對`PyCrust`,最后我們將涉及`Py`包中剩下的程序。
## 如何與wxPython程序交互?
與其它編程語言相比,`Python`的一個顯著的特點是你可以以兩種方式來使用它:你可以用它來運行存在的使用`Python`語言寫的程序,或從命令提示符來交互地運行`Python`。交互地運行`Python`如同與`Python`解釋器會話。
在下例4.1中,我們從命令行啟動`Python`,并鍵入一些數學運算。`Python`啟動后顯示幾行信息,然后是它的主提示符' '。當你鍵入的東西要求額外的代碼行時,`Python`顯示它的次提示符'...'。
例4.1 簡單的`Python`交互式會話
```
$ Python
Python 2.3.3 (#1, Jan 25 2004, 11:06:18)
[GCC 3.2.2 (Mandrake Linux 9.1 3.2.2-3mdk)] on linux2
Type "help", "copyright", "credits" o "license" for more information.
2 + 2
4
7 * 6
42
5 ** 3
125
for n in range(5):
... print n * 9
...
0
9
18
27
36
```
交互式的`Python`不僅僅是一個好的桌面計算器,它也是一個好的學習工具,因為它提供了及時的反饋。當你有疑問時,你可以運行`Python`,鍵入幾行試驗性的代碼,看`Python`如何反應,據此調整你的主要代碼。學習`Python`或學習現有的`Python`代碼是如何工作的,最好的方法之一就是交互式地調試。
**`PyCrust`配置了標準的`Python`** **`shell`**
當你交互式的使用`Python`工作時,你工作在一個稱為`Python` `shell`的環境中,它類似于其它的`shell`環境,如微軟平臺的`DOS`窗口,或類`Unix`系統的`bash`命令行。
所有`Python` `shell`中最基本的是例4.1中所出現的,它是你從命令行啟動`Python`時所見到的。雖然它是一個有用的`shell`,但是它基于文本的,而非圖形化的,并且它不提供快捷方式或有幫助的提示。有幾個圖形化的`Python` `shell`已經被開發出來了,它們提供了這些額外的功能。最著名的是`IDLE`,它是`Python`發布版的標準部分。`IDLE`如下圖4.1所示:

`IDLE`看起來很像命令行`Python` `shell`,但是它有額外的特性如調用提示。
其它的`Python`開發工具,如`PythonWin`和`Boa` `Constructor`,包括了類似于`IDLE`中的圖形化`Python` `shell`。雖然每種工具的`shell`各有一些有用的特性,如命令再調用(`recall)`、自動完成、調用提示,但是沒有一個工具完整包含了所有的特性 。在這種情況下,`PyCrust`產生了,`PyCrust`的目的之一就是提供所有現存的`Python` `shell`的特性。
創建`PyCrust`的另一個動機是:使用一個`GUI`工具包所寫的代碼不能工作在另一個不同的`GUI`工具包上。例如,`IDLE`是用`Tkinter`寫的,而不是`wxPython`。由于這樣,如果你試圖在`IDLE`的`Python` `shell`中引入和使用`wxPython`模塊,那么你將陷入`wxPython`的事件循環與`Tkinter`事件循環間的沖突,結果將導致程序的凍結或崩潰。
事實上,這兩個工具包將在控制事件循環上產生沖突。因此,如果你使用`wxPython`模塊工作時想要內省運行特性,你的`Python` `shell`必須是用`wxPython`寫的。由于沒有現存的`Python` `shell`支持完整的特性,`PyCrust`被創建來填補這種需要。
## PyCrust的有用特性是什么?
現在,我們將關注`PyCrust`提供的`shell`的一些特性。`PyCrust`的`shell`看起來有些熟悉,這是因為它也顯示了如同命令行`Python` `shell`相同的信息行和提示符。下圖4.2顯示了一個打開著的`PyCrust`的屏幕:

你應該注意一下這個`PyCrust`框架,它包含了一個`wx.SplitterWindow`控件,框架被分成兩個區域:上部的區域看起來像通常的`Python` `shell`;底部的區域包含了一個`Notebook`控件,這個控件包含了不同的標簽,默認標簽顯示的信息是有關當前的名字空間的。上部區域是`PyCrust` `shell`,它有幾個有用的特性,我們將在下面幾節討論。
### 自動完成
當你在一個對象名后鍵入一點號時將引發自動完成功能。`PyCrust`將按字母順序顯示關于該對象的所有已知的屬性的一個列表。當你在點號后輸入字母時,在列表中的高亮選項將改變去匹配你所輸入的字母。如果高亮選項正是你所要的,這時按下`Tab`鍵,`PyCrust`將為你補全該屬性名的其余部分。
在下圖4.3中,`PyCrust`顯示一個字符串對象的屬性的列表。這個自動完成的列表包含了該對象的所有屬性和方法。
圖4.3

### 調用提示和參數默認
當你在一個可調用的對象名后鍵入左括號時,`PyCrust`顯示一個調用提示窗口(如圖4.4),該窗口包含了所能提供的參數信息和文檔字符串(如果可調用對象中定義了文檔字符串的話)。
可調用對象可以是函數、方法、內建的或類。可調用對象的定義都可以有參數,并且可以有用來說明功能的文檔字符串,以及返回值的類型。如果你知道如何使用該可調用對象,那么你可以忽略調用提示并繼續鍵入。
圖4.4

### 語法高亮
當你在`shell`中鍵入代碼時,`PyCrust`根據它的重要性改變文本的顏色。例如,`Python`的關鍵詞用一種顏色顯示,原義字符串用另一種顏色,注釋用另一種顏色。這就使得你可以通過顏色來確認你的輸入是否有誤。
### Python
**幫助**
`PyCrust`完整地提供了關于`Python`的幫助功能。`Python`的幫助功能顯示了幾乎所有`Python`方面的信息,如下圖4.5所示
圖4.5

`Python`的幫助功能提供了另外一個提示符(`help)`。在使用了`help`之后,你可以通過在`help`提示符之后鍵入`quit`來退出幫助模式,返回到通常的`Python`提示符( )。
### 命令重調用
在`PyCrust` `shell`中有多種方法可以用來減少重復輸入。它們大都通過捕獲你先前的鍵入來實現,如果有必要,你可以修改所捕獲的內容,之后它們將之發送給`Python`解釋器。
例如,`PyCrust`維護著當前會話中你所鍵入的所有命令的一個歷史記錄。你可以從命令歷史記錄中重調用你先前鍵入的任何`Python`命令(一行或多行)。下表4.1顯示了一個關于該功能的快捷鍵列表。
`Ctrl`+上箭頭:獲取前一個歷史項 `Alt`+P:獲取前一個歷史項 `Ctrl`+下箭頭:獲取下一個歷史項 `Alt`+N:獲取下一個歷史項 `Shift`+上箭頭:插入前一個歷史項 `Shift`+下箭頭:插入下一個歷史項 `F8`:歷史項命令補全(鍵入先前命令的少量字符并按`F8`) `Ctrl`+`Enter`:在多行命令中插入新行
正如你所看到的,這兒有不同的命令用于獲取和插入舊命令,它們通過`PyCrust`如何處理當前`wxPythob`提示符中所鍵入的文本被區分。要替換你的鍵入或插入一個舊的命令,可以使用快捷鍵來獲取或插入一個歷史項。
插入一行到一個多行命令中的工作與插入到一單行命令不同。要插入一行到一個多行命令,你不能只按`Enter`鍵,因為這樣將把當前的命令發送給`Python`解釋器。替代的方法是,按下`Ctrl`+`Enter`來插入一個中斷到當前行。如果你處于行尾,那么一個空行被插入當前行之后。這個過程類似于你在一個通常的文本編輯中剪切和粘帖文本的方法。
最后一種重調用命令的方法是簡單地將光標移到想要使用的命令,然后按`Enter`鍵。`PyCrust`復制該命令到當前的`Python`提示符。然后你可以修改該命令或按`Enter`鍵以將該命令提交給解釋器。
快捷鍵讓你可以快速地開發代碼,并做每步的測試。例如,你可以定義一個新的`Python`類,創建該類的一個實例,并看它的行為如何。然后,你可以返回到這個類的定義,增加更多的方法或編輯已有的方法,并創建一個新的實例。通過這樣的反復,你可以將你的類的定義做得足夠好,然后將它粘帖到你的源代碼中。
### 剪切和粘貼
你可能想重用在`shell`中已開發的代碼,而避免重新鍵入。有時,你可能找到一些樣例代碼(可能來自在線的教程),你想把它用到一個`Python` `shell`中。`PyCrust`提供了一些簡單的剪切和粘貼選項,列表于下表4.2
`Ctrl`+C:復制所選的文本,去掉提示符 `Ctrl`+`Shift`+C:復制所選的文本,保留提示符 `Ctrl`+X:剪切所選的文本 `Ctrl`+V:粘貼自剪貼板 `Ctrl`+`Shift`+V:粘貼自剪貼板的多個命令并運行
粘貼的另一個特性是:`PyCrust`從所粘貼到`PyCrust` `shell`中的代碼中識別并自動去掉標準的`Python`提示符。這使得復制教程或`email`信息中的例子代碼,把它粘貼到`PyCrust`中,并測試它變得簡單了,省去了手工的清理。
某些時候,當你復制代碼時,你可能想去除`PyCrust`提示符,如當你復制代碼到你的源文件中時。另一些時候,你可能想保留這個提示符,如錄你復制例子到一個文檔中,或把它發送到一個新聞組。當從`shell`復制時,`PyCrust`對這兩種情況都提供了支持。
### 標準shell環境
在`wxPython`環境中,`PyCrust`的行為盡可能地與命令行的`Python` `shell`相同。不同的是,一旦`Python`代碼被輸入到了`PyCrust` `shell`中,就沒有辦法來中斷該代碼的運行。例如,假定你在`PyCrust`中寫了一個無限循環,如下所示:
* `while` `True:`
... `print` "`Hello`" ...
在你按下`Enter`之后,上面的代碼被傳送到`Python`解釋器,`PyCrust`停止響應。要中斷這個無限的循環,必須關閉`PyCrust`程序。這個缺點是與命令行的`Python` `shell`對比而言的。命令行的`Python` `shell`保留了處理鍵盤中斷(`Ctrl`+C)的能力。在命令行的`Python` `shell`中你會看到如下的行為:
```
while True:
... print "Hello"
...
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Traceback (most recent call last):
File " stdin ", line 2, in ?
KeyboardInterrupt
```
在`GUI`環境中的事件處理的本質,使得設計出能夠讓`PyCrust`中斷一個無限循環或在`shell`提示符中鍵入的長時間運行的代碼序列的方法有很大的不同。將來的`PyCrust`版本可能會提供對這個缺點的一個解決辦法。幸運的是,在`PyCrust`和標準命令`shell`之間只有這一個不同點。在其它方面,`PyCrust` `shell`和命令行的`Python` `shell`工作的完全一樣。
### 動態更新
當你在運行`PyCrust`時,`PyCrust`的`shell`的所有特性都是動態地被更新的,這意味著,諸如“自動完成”和“調用提示”等特性是有效的,即使是在`shell`提示符中定義的對象。例如圖4.6和4.7所顯示的會話,那么我們定義并使用了一個類。
在圖4.6中,`PyCrust`為新類顯示了自動完成選項。 圖4.6

在圖4.7中,`PyCrust`顯示了關于類所定義的新的方法的調用提示。 圖4.7

## PyCrust
**`notebook`的標簽是干什么的?**
`PyCrust`界面的下半部是一個`notebook`控件,`notebook`控件包括了幾個帶有有用信息的標簽。`PyCrust`開始時,你所看到的標簽是“`Namespace`”標簽。
### Namespace標簽
如圖4.8所示,`Namespace`標簽又被用`wx.SplitterWindow`控件分成兩部分。左邊包含一個樹控件,它顯示當前的名字空間,而右邊顯示在名字空間樹中當前被選擇的對象的細節。
圖4.8

名字空間樹呈現一個關于在當前名字空間中所有對象的層次關系的視圖。如果你運行`Python`的內建函數`locals()`,這些對象將作為返回結果。在圖4.8中,我們已經導入了`wx`包并在名字空間樹中選擇了它。右邊顯示了所選擇的項目的名字,它的類型和它的當前值。如果對象有與之相關聯的源代碼,`PyCrust`也將顯示出來。這里,`wx`是一個`wxPython`包,所以`PyCrust`顯示`__init__.py`文件的源代碼,該文件位于`wx`目錄中。
右邊顯示的第一行是左邊所選擇的對象的全名,你可以把它復制并粘貼到`PyCrust` `shell`或你的應用程序源碼中。例如,我們在`PyCrust`中引入`locale`模塊并選擇名字空間樹中`locale`/`encoding_alias`/'`en`'項,右邊就顯示了所選對象的完整名,你可以把它復制并粘貼到`PyCrust` `shell`中,如下所示:
```
import locale
locale.encoding_alias['en']
'ISO8859-1'
```
這里,`PyCrust`給我們提供了一個全名( `locale.encoding_alias`['`en`']),它使用`Python`的索引(['`en`'])來引用`encoding_alias`目錄中的指定項目。這個機制同樣適用于列表(`list)`。如果你在名字空間樹中發現了你想用在你的代碼中的東西,那么`PyCrust`給了你這精確語法去完成這個任務。
### Display標簽
* `Display`標簽中用于顯示一個對象。`PyCrust`有一個內建函數`pp()`,這個函數使用`Python`的`pprint`模塊為顯示一個對象。使用中不需要顯式地引入和重復使用`pprint`,在`Display`中,這些信息隨對象的更新而每次更新。
例如,如果我們在`PyCrust` `shell`中有一個列表,我們要在 `Display`標簽中顯示它的內容,我們可以在`PyCrust` `shell`中使用`pp()`,然后列表的內容就顯示在 `Display`標簽中了。以后每當我們改變了列表的內容, `Display`標簽中的內容隨即改變。
`Calltip`標簽顯示了在`Python` `shell`中最近調用提示的內容。如果你的調用要求大量的參數,那么你可以選擇`Calltip`標簽。當使用`wxPython`包時,存在著大量的類,這些類有許多方法,這些方法又要求許多參數。例如,為了創建一人`wx.Button`,你可能要提供八個參數,有一個是必須提供的,其它七個有默認的值。`Calltip`標簽顯示了關于`wx.Button`構造器的細節,如下所示:
```
__init__(self, Window parent, int id=-1, String label=EmptyString,
Point pos=DefaultPosition, Size size=DefaultSize,
long style=0, Validator validator=DefaultValidator,
String name=ButtonNameStr) - Button
Create and show a button. The preferred way to create standard buttons
is to use a standard ID and an empty label. In this case wxWigets will
automatically use a stock label that corresponds to the ID given. In
addition, the button will be decorated with stock icons under GTK+2.
```
由于`wxPython`的類實際上是封裝的C++的類,所以調用提示信息完全基于類的文檔字符串。它們顯示了底層C++類所需要的參數和類型信息。對于完全用`Python`語言定義的對象,`PyCrust`檢查它們以確定它的參數特性。
### Session標簽
* `Session`標簽是一個簡單的文本控件,它列出了在當前`shell`會話中所鍵入的所有命令。這使得剪切和粘貼命令以用在別處更為簡單。
### Dispatcher
**標簽**
`PyCrust`包括了一個名為`dispatcher`的模塊,它提供了在一個應用程序中聯系對象的機制。`PyCrust`使用`dispatcher`來維持它的界面的更新,主要是在命令從`shell`傳送到`Python`解釋器時。圖4.9中的`Dispatcher`標簽列出了關于信號經過分配機制后的路由。當使用`PyCrust`工作時,這是它的主要用處。
圖4.9

這里的`Dispatcher`標簽也演示了如何增加另一個標簽到一個`wx.Notebook`控件。下面這個在`Dispatcher`標簽上的文本控件的源碼,演示了如何使用`dispatcher`模塊:
```
class DispatcherListing(wx.TextCtrl):
"""Text control containing all dispatches for session."""
def __init__(self, parent=None, id=-1):
style = (wx.TE_MULTILINE | wx.TE_READONLY |
wx.TE_RICH2 | wx.TE_DONTWRAP)
wx.TextCtrl.__init__(self, parent, id, style=style)
dispatcher.connect(receiver=self.spy)
def spy(self, signal, sender):
"""Receiver for Any signal from Any sender."""
text = '%r from %s' % (signal, sender)
self.SetInsertionPointEnd()
start, end = self.GetSelection()
if start != end:
self.SetSelection(0, 0)
self.AppendText(text + '\n')
```
現在我們已經看到了`PyCrust`作為獨立的`Python` `shell`和名子空間檢查器能夠做些什么,下面讓我們關注在你的`wxPython`程序中,`PyCrust`的其它一些用法。
## 如何將PyCrust應用于wxPython應用程序
。
讓我們假設你已經用`wxPython`創建了一個程序,并且你的程序正在工作,現在你想更好地了解它是如何工作的。在這章的前面你已經看到了`PyCrust`的特性,它們看起來對于理解你的程序的功能是非常有用的。
通過將你的程序的名字傳遞給`PyWrap`,你能夠用`PyCrust` `shell`來啟動你的程序,不需要對你的程序作任何的改變。下例4.2顯示了一個名為`spare.py`的程序,我們準備對它使用`PyCrust`。
例4.2
```
#!/usr/bin/env python
"""Spare.py is a starting point for simple wxPython programs."""
import wx
class Frame(wx.Frame):
pass
class App(wx.App):
def OnInit(self):
self.frame = Frame(parent=None, id=-1, title='Spare')
self.frame.Show()
self.SetTopWindow(self.frame)
return True
if __name__ == '__main__':
app = App()
app.MainLoop()
```
為了運行這個程序時使用`PyCrust`,要將該程序的全路徑傳遞給`PyWrap`。在`Linux`上,命令行類似如下:
```
$ pywrap spare.py
```
在`windows`下,命令行類似如下:
```
F:\ python pywrap.py spare.py
```
在開始的時候,`PyWrap`試圖導入命令行所包括的模塊。然后`PyWrap`在模塊中尋找`wx.App`的子類,并創建子類的一個實例。之后,`PyWrap`創建一個帶有`shell`的`wx.py.crust.CrustFrame`窗口,把這個應用程序對象顯示在`PyCrust`的名字空間樹中,并且啟動 `wxPython`事件循環。
`PyWrap`的源碼顯示在例子4.3中。它顯示了如何用少量的代碼將大量的功能增加到你的程序中。
例4.3
```
"""PyWrap is a command line utility that runs a python
program with additional runtime tools, such as PyCrust."""
__author__ = "Patrick K. O'Brien pobrien@orbtech.com "
__cvsid__ = "$Id: PyCrust.txt,v 1.15 2005/03/29 23:39:27 robind Exp $"
__revision__ = "$Revision: 1.15 $"[11:-2]
import os
import sys
import wx
from wx.py.crust import CrustFrame
def wrap(app):
wx.InitAllImageHandlers()
frame = CrustFrame()
frame.SetSize((750, 525))
frame.Show(True)
frame.shell.interp.locals['app'] = app
app.MainLoop()
def main(modulename=None):
sys.path.insert(0, os.curdir)
if not modulename:
if len(sys.argv) 2:
print "Please specify a module name."
raise SystemExit
modulename = sys.argv[1]
if modulename.endswith('.py'):
modulename = modulename[:-3]
module = __import__(modulename)
# Find the App class.
App = None
d = module.__dict__
for item in d.keys():
try:
if issubclass(d[item], wx.App):
App = d[item]
except (NameError, TypeError):
pass
if App is None:
print "No App class was found."
raise SystemExit
app = App()
wrap(app)
if __name__ == '__main__':
main()
```
運行了`PyWrap`命令之后,來自`spare`的簡單的框架(`frame)`和`PyCrust`的框架都顯示出來。
**`PyCrust`** **`in`** **`action`**
現在讓我們看看,在`PyCrust` `shell`中我們對`spare.py`應用程序框架做些什么。圖4.10顯示了這個結果。我們將通過導入`wx`和增加一個畫板到我們的框架作為開始:
```
import wx
app.frame.panel = wx.Panel(parent=app.frame)
app.frame.panel.SetBackgroundColour('White')
```
`True`
圖4.10

增加到框架的畫板開始時是默認的銀灰色,然后它被改變到白色。然而,設置畫板背景色不立即改變它的顯示。這需要去觸發一個事件來導致畫板重繪,以使用它的新顏色屬性。一個觸發這樣事件的方法是要求畫板刷新自身:
```
app.frame.panel.Refresh()
```
現在一個白色的畫板顯示了,我們對于理解`wxPython`如何工作的細節又進了一步。
接下來,讓我們增加一個狀態欄:
```
app.frame.statusbar = app.frame.CreateStatusBar(number=3)
app.frame.statusbar.SetStatusText("Left", 0)
app.frame.statusbar.SetStatusText("Center", 1)
app.frame.statusbar.SetStatusText("Right", 2)
```
注意在不改變這個框架的尺寸情況下,這個狀態欄在這個框架中是如何顯示的。也要注意添加到三個狀態欄中的文本的立即顯示了出來,而不要求刷新。現在讓我們增加一個菜單和一個菜單欄:
```
app.frame.menubar = wx.MenuBar()
menu = wx.Menu()
app.frame.menubar.Append(menu, "Primary")
app.frame.SetMenuBar(app.frame.menubar)
menu.Append(wx.NewId(), "One", "First menu item")
menu.Append(wx.NewId(), "Two", "Second menu item")
```
當你在`PyCrust` `shell`中處理你自己的`wxPython`對象時,注意改變對你正在運行的程序的影響。試試回答后面的問題。在框架中菜單何時才實際顯示出來的?在程序運行的時候,你能改變菜單的哪些屬性?你能夠讓它們無效嗎?交互地探究這些可以幫助你更好的理解`wxPython`,同時當你寫真實的代碼時給你帶來更大的自信。
到目前,我們已經花了很多節討論`PyCrust`,我們下面準備看一看`Py`包的其余的東西。
## 在Py包中還有其它什么?
所有`PyCrust`中的程序都利用了`Py`包中的`Python`模塊,諸如`shell.py`,`crust.py`,`introspect.py`和 `interpreter.py`。這些程序是用來做`PyCrust`的建造塊,你可以分別或一起使用它們。
`PyCrust`代表了組裝包含在`Py`包中功能模塊的一各方法。`PyShell`是另一方法,`PyAlaMode`是第三種。在這 些方法中,它們的底層代碼大多數是相同的,只是外包裝有所變化而已。因此,你可以把`Py`當做一個模塊 庫,你可以隨意地在你的程序中的任何地方組裝其中的模塊,用來顯示一個`wxPython` `shell`、一個代碼編 輯器或運行時內省信息。
在`Py`包中,提供給用戶界面功能的模塊和沒有這功能的模塊有明顯的區別。這個區別使得在你的程序中很 容易使用這些模塊。以`Py`開頭的模塊是終端用戶`GUI`程序,如`PyCrust`,`PyShell`,`PyAlaMode`和`PyAlaCarte`。在你的程序中,你不會想導入這些模塊。下節說明終端用戶模塊。
### 使用GUI程序工作
下表4.3說明了用戶級程序。
`PyAlaCarte`:簡單的源代碼編輯器。一次編輯一個文件。 `PyAlaMode`:多文件源代碼編輯器。每個文件分別顯示在一個`notebook`標簽中。第一個標簽包含一個`PyCrust`分隔窗口。 `PyCrust`:合并了`wxPython` `shell`和`notebook`標簽,`notebook`包含一個名字空間樹查看器。 `PyFilling`:簡單的名字空間樹查看器。這個程序自己不是很有用。它的存在只是作為如何使用底層庫的一個例子。 `PyShell`:簡單的`wxPython` `shell`界面,沒有`PyCrust`中的`notebook`。功能上,`PyShell`中的`wxPython` `shell`和`PyCrust`中的是一樣的。 `PyWrap`:命令行工具,用以運行一個存在的程序和`PyCrust`框架,讓你能夠在`PyCrust` `shell`中處理這個應用程序。
### 使用支持模塊工作
支持模塊為終端用戶提供了基本的功能,可以被導入你的程序中。這些模塊是用來創建用戶級`Py`程序的建
造塊。下表4.4列出了這些支持模塊,它們是`Py`包的一部分,說明如下:
`buffer`:支持文件編輯。 `crust`:包含`PyCrust`應用程序獨有的`GUI`元素。 `dispatcher`:提供全局信號分派服務。 `document`:`document`模塊包含一個非常簡單的`Document`類,這個類是一個小的文件類。`document`跟蹤不同的文件屬性,如名字和路徑,并提供`read()`和`write()`方法。`Buffer`類委托這些低級的讀寫操作給一個`Document`實例。 `editor`:包含所有顯示在`PyAlaCarte`和`PyAlaMode`程序中的`GUI`編輯部分。 `editwindow`:這個`editwindow`模塊包含了一個簡單的`EditWindow`類。這個類繼承自`wx.stc.StyledTextCtrl` (`STC)`,并且提供了`Py`包中的`STC`的三種主要用法的所有共同的特性,這三種主要用法是:作為一個`Python` `shell`,作為一個源代碼編輯器和作為一個只讀源碼顯示器。 `filling`:包含所有的`GUI`控件,這些`GUI`控件讓用戶能夠瀏覽對象名字空間并顯示那些對象運行時的信息。 `frame`:`frame`模塊定義了一個`Frame`類,這個`Frame`類是`Py`包中所有其它`frames`的基類。菜單項根據當前狀態和上下文不斷地自我更新。 `images`:`images`模塊包含被不同`Py`程序使用的圖標。 `interpreter`:`Interpreter`類負責提供自動完成列表,調用提示信息等。 `introspect`:為一些方面提供多種內省類型,像調用提示和命令自動完成。 `pseudo`:這個模塊定義文件類類,文件類允許`Interpreter`類去重定向`stdin`,`stdout`,`stderr`。 `shell`:這個模塊包含`GUI`元素,這些`GUI`元素定義顯示在`PyCrust`,`PyShell`和`PyAlaMode`中的`Python` `shell`的界面。 `version`:這個模塊包含一個名為`VERSION`的字符串變量,`VERSION`代表`Py`當前的版本。
下面我們討論更復雜的模塊。
**`buffer`模塊**
`buffer`模塊包含一個`Buffer`類,這個類支持文件的通常編輯。`buffer`有一些方法,例如`new()`, `open()`, `hasChanged()`, `save()`,和`saveAs()`。文件操作基于`buffer`所委托的`Document`類的實例,`Document`類定義在`document`模塊中。文件內容的實際編輯是通過`Editor`類的一個或多個實例發生的,`Editor`類定義在 `editor`模塊中。`buffer`扮演一個在一個或多個編輯器和實際物理文件之間的中間人。
`Buffer`類的一個獨特的手法是每個`buffer`實例都分配了它自己的`Python`解釋器實例。這個特點使得`buffer` 能夠被用在那些當編輯`Python`源代碼文件時需要提供自動完成,調用提示和其它運行時幫助的應用程序中 。每個`buffer`解釋器都是完全獨立的,并且在`buffer`的`updateNamespace()`方法被調用時更新。下例4.4顯示了`updateNamespace()`方法的源代碼。
例4.4
```
def updateNamespace(self):
"""Update the namespace for autocompletion and calltips.
Return True #if updated, False if there was an error."""
if not self.interp o not hasattr(self.editor, 'getText'):
return False
syspath = sys.path
sys.path = self.syspath
text = self.editor.getText()
text = text.replace('\r\n', '\n')
text = text.replace('\r', '\n')
name = self.modulename o self.name
module = imp.new_module(name)
newspace = module.__dict__.copy()
try:
try:
code = compile(text, name, 'exec')
except:
raise
try:
exec code in newspace
except:
raise
else:
# No problems, so update the namespace.
self.interp.locals.clear()
self.interp.locals.update(newspace)
return True
finally:
sys.path = syspath
for m in sys.modules.keys():
if m not in self.modules:
del sys.modules[m]
```
這個方法使用`Python`內建的`compile`方法編譯編輯器中的文本,然后使用關鍵詞`exec`來執行。如果編譯成 功,將放置若干變量到`newspace`名字空間中。通過用執行的結果重置解釋器的局部名字空間,解釋器支持 訪問定義在編輯器的`buffer`中的任何類,方法或變量。
**`crust` 模塊**
`crust`模塊包含6個`GUI`元素,它們專門用于`PyCrust`應用程序的。這最常用的類是`CrustFrame`,它是`wx.Frame`的子類。如果你再看一下例4.3,你能看到`PyWrap`程序是如何導入`CrustFrame`并創建其一個實例的。這是嵌入一個`PyCrust`框架到你自己的程序中的最簡單的方法。如果你想要比一個完整的框架更小的東西,你可以使用下表4.5所列的一個或多個類。
表4.5
`Crust`:基于`wx.SplitterWindow`并包含一個`shell`和帶有運行時信息的`notebook`。 `Display`:樣式文本控件,使用`Pretty` `Print`顯示一個對象。 `Calltip`:文本控件,包含最近`shell`調用幫助。 `SessionListing`:文本控件,包含關于一個會話的所有命令。 `DispatcherListing`:文本控件,包含關于一個會話的所有分派。 `CrustFrame`:一個框架,包含一個`Crust`分隔窗口。
這些`GUI`元素可以被用在任何`wxPython`程序中,以提供有用的可視化內省。
**`dispatcher`模塊**
`dispatcher`提供了全局信號分派服務。那意味它扮演著一個中間人,使得對象能夠發送和接受消息,而無須知道彼此。所有它們需要知道的是這個正在發送的信號(通常是一個簡單的字符串)。一個或多個對象可以要求這個`dispatcher`,當信號已發出時去通知它們,并且一個或多個對象可以告訴這個`dispatcher`去發送特殊的信號。
下例4.5是關于為什么`dispatcher`是如此有用的一個例子。因為所有的`Py`程序都是建造在相同的底層模塊之上的,所以`PyCrust`和`PyShell`使用幾乎相同的代碼。這唯一的不同是,`PyCrust`包括了一個帶有額外功能的`notebook`,如名字空間樹查看器,當命令被發送到解釋器時,名字空間樹查看器更新。在一個命令通過解釋器時,解釋器使用`dispatcher`發送一個信號:
例4.5 經由`dispatcher`模塊來發送命令的代碼
```
def push(self, command):
"""Send command to the interpreter to be executed.
Because this may be called recursively, we append a new list
onto the commandBuffer list and then append commands into
that. If the passed in command is part of a multi-line
command we keep appending the pieces to the last list in
commandBuffer until we have a complete command. If not, we
delete that last list."""
command = str(command) # In case the command is unicode.
if not self.more:
try:
del self.commandBuffer[-1]
except IndexError:
pass
if not self.more:
self.commandBuffer.append([])
self.commandBuffer[-1].append(command)
source = '\n'.join(self.commandBuffer[-1])
more = self.more = self.runsource(source)
dispatcher.send(signal='Interpreter.push', sender=self,
command=command, more=more, source=source)
return more
```
`crust`中的各有關部分和`filling`模塊在它們的構造器中通過連接到`dispatcher`,自己作為信號的接受器。下例4.6顯示了關于出現在`PyCrust`的`Session`標簽中的`SessionListing`控件的源碼:
例4.6 `PyCrust` `session`標簽的代碼
```
class SessionListing(wx.TextCtrl):
"""Text control containing all commands for session."""
def __init__(self, parent=None, id=-1):
style = (wx.TE_MULTILINE | wx.TE_READONLY |wx.TE_RICH2 | wx.TE_DONTWRAP)
wx.TextCtrl.__init__(self, parent, id, style=style)
dispatcher.connect(receiver=self.push,signal='Interpreter.push')
def push(self, command, more):
"""Receiver for Interpreter.push signal."""
if command and not more:
self.SetInsertionPointEnd()
start, end = self.GetSelection()
if start != end:
self.SetSelection(0, 0)
self.AppendText(command + '\n')
```
注意`SessionListing`的接受器(`push()`方法)是如何忽略由解釋器發送來的`sender`和`source`參數的。`dispatcher`非常靈活,并且只發送接受器能接受的參數。
**`editor`模塊**
`editor`模塊包含了出現在`PyAlaCarte`和`PyAlaMode`程序中的所有`GUI`編輯組件。如果你愿意在你的程序中包括一個`Python`源代碼編輯器,那么使用在下表4.6中所說明的類。
這些類可以被使用在任何程序中以提供有用的代碼風格編輯功能。
表4.6 定義在`editor`模塊中的類
`EditerFrame`:被`PyAlaCarte`用來支持一次一個文件的編輯。`EditerFrame`是來自`frame`模塊的較一般的`Frame`類的子類。 `EditorNotebookFrame`:`EditerFrame`的子類,它通過增加一個`notebook`界面和同時編輯多個文件的能力擴展了`EditerFrame`。它是一個被`PyAlaMode`使用的`frame`類。 `EditorNotebook`:這個控件被`EditorNotebookFrame`用來在各自的標簽中顯示各自的文件。 `Editor`:管理一個`buffer`和與之相關的`EditWindow`之間的關聯。 `EditWindow`:基于`StyledTextCtrl`的文本編輯控件。
**`filling`模塊**
`filling`模塊包含所有使用戶能夠瀏覽對象的名字空間和顯示關于那些對象的運行時信息的`GUI`控件。 定義在`filling`模塊中的四個類的說明見下表4.7
表4.7
`FillingTree`:基于`wx.TreeCtrl`,`FillingTree`提供對象名字空間的分級樹。 `FillingText`:`editwindow.EditWindow`的子類,用以顯示當前在`FillingTree`所選擇的對象的細節。 `Filling`:一個`wx.SplitterWindow`,它的左邊包括一個`FillingTree`,右邊包括一個`FillingText`。 `FillingFrame`:一個包含`Filling`分隔窗口的框架。雙擊`filling`樹中的一項將打開一個新的`FillingFrame`,其中被選擇的項作為樹的根。
使用這些類,使你能夠容量地創建`Python`名字空間的分級樹。如果你設置你的數據為`Python`對象,這能夠用作一個快速的數據瀏覽器。
**`interpreter`模塊**
`interpreter`模塊定義了一個`Interpreter`類,基于`Python`標準庫中的`code`模塊的`Interactive`- `Interpreter`類。除了負責發送源代碼給`Python`外,`Interpreter`類還負責提供自動完成列表,調用提示信息和甚至觸發自動完成特性的關鍵碼(通常是".")。
由于這清楚的責任劃分,你可以創建你自己的`Interpreter`的子類并傳遞其一個實例到`PyCrust` `shell`,從而代替默認的`interpreter`。這已經應用到了一些程序中以支持自定義評議種類,而仍然利用`PyCrust`環境。
**`introspect`模塊**
`introspect`模塊被`Interpreter`和`FillingTree`類使用。它為調用提示和命令自動完成提供了多種內省類型支持功能。下面顯示了`wx.py.introspect`的用法,它得到一個列表對象的所有屬性的名字,排除了那些以雙下劃線開始的屬性:
```
import wx
L = [1, 2, 3]
wx.py.introspect.getAttributeNames(L, includeDouble=False)
['append', 'count', 'extend', 'index', 'insert', 'pop',
'remove', 'reverse', 'sort']
```
`getAttributeNames()`函數被`FillingTree`類使用以生成它的名字空間分級。理解內省模塊的最好方法是關注單元測試。查看你的`Python`安裝目錄的`Lib`/`site`-`packages`/`wx`/`py`/`tests`中的`test_introspect.py`文件。
**`shell`模塊**
`shell`模塊包含出現在`PyCrust`, `PyShell`, 和`PyAlaMode`定義`Python` `shell`界面的`GUI`元素。下表4.8提供了每個元素的說明。這最常用的類是`ShellFrame`,它是`frame.Frame`的子類。它包含一個`Shell`類的實例,`Shell`類處理提供交互`Python`環境的大部分工作。
表4.8 定義在`shell`模塊中的類
`Shell`:`Python` `shell`基于`wx.stc.StyleTextCtrl`。`Shell`子類化`editwindow.EditWindow`,然后使底層的文本控件的行為像一具`Python` `shell`,而非一個源碼文件編輯器。 `ShellFacade`:簡化的與所有`shell`相關的功能的接口。它是半透明的,它仍然是可訪問的,盡管只有一些是對`shell`用戶可見的。 `ShellFrame`:一個包含一個`Shell`窗口的框架。
`ShellFacade`類在`PyCrust`的開發期間被創建,它作為在`shell`中訪問`shell`對象自身時去簡化事情的一個方法。當你啟動`PyCrust`或`PyShell`時,`Shell`類的實例在`Python` `shell`中的有效的。例如,你可以在`shell`提示符下調用`shell`的`about()`方法,如下所示:
```
shell.about()
Author: "Patrick K. O'Brien pobrien@orbtech.com "
Py Version: 0.9.4
Py Shell Revision: 1.7
Py Interpreter Revision: 1.5
Python Version: 2.3.3
wxPython Version: 2.4.1.0p7
Platform: linux2
```
由于`Shell`繼承自`StyledTextCtrl`,所以它包含超過600個屬性。大部分屬性對`shell`提示符是沒用的,因此,`ShellFacade`被創建來限制當你進入`shell`時出現在自動完成列表中的屬性的數量。目前,`shell`對象只顯示最有用的`shell`屬性的25個。
## 如何在wxPython中使用Py包中的模塊?
如果你不想在你的應用程序使用一個完整的`PyCrust`框架,那么該怎么做呢?如果你在一個框架中僅僅只想要`shell`界面,而在另一個框架中要一個名字空間查看器,該怎么辦呢?如果你想把它們永久添加到你的程序中以該怎么辦呢?這些方案不僅是可能的,而且也是十分簡單的。我們將用一個例子來說明這該怎么做來結束本章。
我們將再看一下在第2章中所創建的程序,它帶有一個菜單欄,工具欄和狀態欄。我們將添加一個菜單,它的一個菜單項用以顯示一個`shell`框架,另一個用來顯示一個`filling`框架。最后我們將把`filling`樹的根設置給我們的主程序的框架對象。結果顯示在圖4.11中。
**圖4.11**

下例4.7顯示了修改過的源碼。正如你的看到的,我們只增加了幾行就實現了所要求的功能。
例4.7
```
#!/usr/bin/env python
import wx
#1 導入這些框架類
from wx.py.shell import ShellFrame
from wx.py.filling import FillingFrame
import images
class ToolbarFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, 'Toolbars',size=(300, 200))
panel = wx.Panel(self, -1)
panel.SetBackgroundColour('White')
statusBar = self.CreateStatusBar()
toolbar = self.CreateToolBar()
toolbar.AddSimpleTool(wx.NewId(), images.getNewBitmap(),"New", "Long help for 'New'")
toolbar.Realize()
menuBar = wx.MenuBar()
menu1 = wx.Menu()
menuBar.Append(menu1, " ")
menu2 = wx.Menu()
menu2.Append(wx.NewId(), " ", "Copy in status bar")
menu2.Append(wx.NewId(), "C ", "")
menu2.Append(wx.NewId(), "Paste", "")
menu2.AppendSeparator()
menu2.Append(wx.NewId(), " ", "Display Options")
menuBar.Append(menu2, " ")#2 創建Debug菜單及其菜單項
menu3 = wx.Menu()
shell = menu3.Append(-1, " shell","Open wxPython shell frame")
filling = menu3.Append(-1, " viewer","Open namespace viewer frame")
menuBar.Append(menu3, " ")#3 設置菜單的事件處理器
self.Bind(wx.EVT_MENU, self.OnShell, shell)
self.Bind(wx.EVT_MENU, self.OnFilling, filling)
self.SetMenuBar(menuBar)
def OnCloseMe(self, event):
self.Close(True)
def OnCloseWindow(self, event):
self.Destroy() #4 OnShell菜單項和OnFilling菜單項處理器
def OnShell(self, event):
frame = ShellFrame(parent=self)
frame.Show()
def OnFilling(self, event):
frame = FillingFrame(parent=self)
frame.Show()
if __name__ == '__main__':
app = wx.PySimpleApp()
app.frame = ToolbarFrame(parent=None, id=-1)
app.frame.Show()
app.MainLoop()
```
**說明:**
**#1** 這里我們導入了`ShellFrame`和`FillingFrame`類 #2 我們添加了第三個菜單`Debug`到框架的菜單欄 #3 綁定一個函數給`wx.EVT_MENU()`,使我們能夠將一個處理器與菜單項關聯,以便當這個菜單項被選擇時調用所關聯的處理器。 #4 當用戶從`Debug`菜單中選擇`Python` `shell`時,`shell`框架被創建,它的雙親是工具欄框架。當工具欄框架被關閉時,任何打開的`shell`或`filling`框架也被關閉。
## 本章小結
1、像`wxPython`這樣的工具包本身是大而復雜的。`GUI`控件之間的交互并不總是直觀的,整個的處理決定于事件并響應于事件,而非一個線性的執行序列。使用如`PyCrust` `shell`能夠很大程度上提高你對事件驅動環境的理解。
2、`PyCrust`僅僅是另一個`Python` `shell`,它類似于`IDLE`, `Boa` `Constructor`, `PythonWin`和其它開發工具所包括的`shell`。然而,`PyCrust`是用`wxPython`創建的,當你使用`wxPython`開發程序時,它是很有用的。尤其是,你不會有任何事件循環沖突方面的問題,并且你可以在`PyCrust`的`shell`和名字空間查看器中處理你的程序運行時的所有方面。
3、由于`PyCrust`是`wxPython`發行版的一部分,所以它隨同`wxPython`一同被安裝,包括了所有的源碼。這使得`PyCrust`容易使用,并且減少了摸清如何在你自己的程序中提供內省功能的學習曲線。
4、另外,`Py`包的模塊化的設計,使你很容易去挑選最有益于你程序的模塊,如源代碼編輯、名字空間查看、或`shell`
5、`PyCrust`減少了`wxPython`學習的曲線,并幫助你掌握你的程序運行時的細微之處。
下一章,我們將應用我們所學到的關于`wxPython`方面的知識,并且提供一些關于如何組織你的`GUI`程序的實用的建議。
- 活學活用wxPython
- 前言
- 致謝
- 關于本書
- 第一部分
- 第一章 歡迎使用wxPython
- 第二章 給wxPython程序一個堅實的基礎
- 第三章 在事件驅動環境中開發
- 第四章 用PyCrust使得wxPython更易處理
- 第五章 繪制藍圖
- 第六章 使用wxPython基本構件
- 第二部分 基礎wxPython
- 第七章 使用基礎控件
- 第八章 將構件放入窗體中
- 第九章 通過對話框讓用戶選擇
- 第十章 創建和使用wxPython菜單
- 第十一章 使用sizer放置構件
- 第十二章 操作基本圖像
- 第三部分 高級wxPython
- 第十三章 建造列表控件并管理列表項
- 第十四章 網格控件
- 第十五章 樹形控件
- 第十六章 在應用程序中加入HTML
- 第十七章 wxPython的打印構架
- 第十八章 使用wxPython的其他功能