第四章 用PyCrust使得wxPython更易处理

时间:2021-01-10 07:12:30

第四章 用PyCrust使得wxPython更易处理 - 易度

第四章 用PyCrust使得wxPython更易处理

目录

    用PyCrust使得wxPython更易处理
        如何与wxPython程序交互?
        PyCrust的有用特性是什么?
            自动完成
            调用提示和参数默认
            语法高亮
            Python
            命令重调用
            剪切和粘贴
            标准shell环境
            动态更新
        PyCrust
            Namespace标签
            Display标签
            Session标签
            Dispatcher
        如何将PyCrust应用于wxPython应用程序
        在Py包中还有其它什么?
            使用GUI程序工作
            使用支持模块工作
        如何在wxPython中使用Py包中的模块?
        本章小结

1. 用PyCrust使得wxPython更易处理

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包中剩下的程序。

1.1. 如何与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所示:

[attachment:w4.1.gif]

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被创建来填补这种需要。

1.2. PyCrust的有用特性是什么?

现在,我们将关注PyCrust提供的shell的一些特性。PyCrust的shell看起来有些熟悉,这是因为它也显示了如同命令行Python shell相同的信息行和提示符。下图4.2显示了一个打开着的PyCrust的屏幕:

[attachment:w4.2.gif]

你应该注意一下这个PyCrust框架,它包含了一个wx.SplitterWindow控件,框架被分成两个区域:上部的区域看起来像通常的Python shell;底部的区域包含了一个Notebook控件,这个控件包含了不同的标签,默认标签显示的信息是有关当前的名字空间的。上部区域是PyCrust shell,它有几个有用的特性,我们将在下面几节讨论。

1.2.1. 自动完成

当你在一个对象名后键入一点号时将引发自动完成功能。PyCrust将按字母顺序显示关于该对象的所有已知的属性的一个列表。当你在点号后输入字母时,在列表中的高亮选项将改变去匹配你所输入的字母。如果高亮选项正是你所要的,这时按下Tab键,PyCrust将为你补全该属性名的其余部分。

在下图4.3中,PyCrust显示一个字符串对象的属性的列表。这个自动完成的列表包含了该对象的所有属性和方法。

图4.3

[attachment:w4.3.gif]

1.2.2. 调用提示和参数默认

当你在一个可调用的对象名后键入左括号时,PyCrust显示一个调用提示窗口(如图4.4),该窗口包含了所能提供的参数信息和文档字符串(如果可调用对象中定义了文档字符串的话)。

可调用对象可以是函数、方法、内建的或类。可调用对象的定义都可以有参数,并且可以有用来说明功能的文档字符串,以及返回值的类型。如果你知道如何使用该可调用对象,那么你可以忽略调用提示并继续键入。

图4.4

[attachment:w4.4.gif]

1.2.3. 语法高亮

当你在shell中键入代码时,PyCrust根据它的重要性改变文本的颜色。例如,Python的关键词用一种颜色显示,原义字符串用另一种颜色,注释用另一种颜色。这就使得你可以通过颜色来确认你的输入是否有误。

1.2.4. Python

帮助

PyCrust完整地提供了关于Python的帮助功能。Python的帮助功能显示了几乎所有Python方面的信息,如下图4.5所示

图4.5

[attachment:w4.5.gif]

Python的帮助功能提供了另外一个提示符(help)。在使用了help之后,你可以通过在help提示符之后键入quit来退出帮助模式,返回到通常的Python提示符( )。

1.2.5. 命令重调用

在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类,创建该类的一个实例,并看它的行为如何。然后,你可以返回到这个类的定义,增加更多的方法或编辑已有的方法,并创建一个新的实例。通过这样的反复,你可以将你的类的定义做得足够好,然后将它粘帖到你的源代码中。

1.2.6. 剪切和粘贴

你可能想重用在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对这两种情况都提供了支持。

1.2.7. 标准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工作的完全一样。

1.2.8. 动态更新

当你在运行PyCrust时,PyCrust的shell的所有特性都是动态地被更新的,这意味着,诸如“自动完成”和“调用提示”等特性是有效的,即使是在shell提示符中定义的对象。例如图4.6和4.7所显示的会话,那么我们定义并使用了一个类。

在图4.6中,PyCrust为新类显示了自动完成选项。 图4.6

[attachment:w4.6.gif]

在图4.7中,PyCrust显示了关于类所定义的新的方法的调用提示。 图4.7

[attachment:w4.7.gif]

1.3. PyCrust

notebook的标签是干什么的?

PyCrust界面的下半部是一个notebook控件,notebook控件包括了几个带有有用信息的标签。PyCrust开始时,你所看到的标签是“Namespace”标签。

1.3.1. Namespace标签

如图4.8所示,Namespace标签又被用wx.SplitterWindow控件分成两部分。左边包含一个树控件,它显示当前的名字空间,而右边显示在名字空间树中当前被选择的对象的细节。

图4.8

[attachment:w4.8.gif]

名字空间树呈现一个关于在当前名字空间中所有对象的层次关系的视图。如果你运行Python的内建函数locals(),这些对象将作为返回结果。在图4.8中,我们已经导入了wx包并在名字空间树中选择了它。右边显示了所选择的项目的名字,它的类型和它的当前值。如果对象有与之相关联的源代码,PyCrust也将显示出来。这里,wx是一个wxPython包,所以PyCrust显示__init__.py文件的源代码,该文件位于wx目录中。

右边显示的第一行是左边所选择的对象的全名,你可以把它复制并粘贴到PyCrust shell或你的应用程序源码中。例如,我们在PyCrust中引入locale模块并选择名字空间树中locale/encoding_alias/'en'项,右边就显示了所选对象的完整名,你可以把它复制并粘贴到PyCrust shell中,如下所示:

切换行号显示切换行号显示

   1 import locale
   2 locale.encoding_alias['en']
   3 'ISO8859-1'

这里,PyCrust给我们提供了一个全名( locale.encoding_alias['en']),它使用Python的索引(['en'])来引用encoding_alias目录中的指定项目。这个机制同样适用于列表(list)。如果你在名字空间树中发现了你想用在你的代码中的东西,那么PyCrust给了你这精确语法去完成这个任务。

1.3.2. 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构造器的细节,如下所示:
切换行号显示切换行号显示

   1 __init__(self, Window parent, int id=-1, String label=EmptyString,
   2     Point pos=DefaultPosition, Size size=DefaultSize,
   3     long style=0, Validator validator=DefaultValidator,
   4     String name=ButtonNameStr) -  Button
   5
   6 Create and show a button. The preferred way to create standard buttons
   7 is to use a standard ID and an empty label. In this case wxWigets will
   8 automatically use a stock label that corresponds to the ID given. In
   9 addition, the button will be decorated with stock icons under GTK+2.

由于wxPython的类实际上是封装的C++的类,所以调用提示信息完全基于类的文档字符串。它们显示了底层C++类所需要的参数和类型信息。对于完全用Python语言定义的对象,PyCrust检查它们以确定它的参数特性。

1.3.3. Session标签

    Session标签是一个简单的文本控件,它列出了在当前shell会话中所键入的所有命令。这使得剪切和粘贴命令以用在别处更为简单。

1.3.4. Dispatcher

标签

PyCrust包括了一个名为dispatcher的模块,它提供了在一个应用程序中联系对象的机制。PyCrust使用dispatcher来维持它的界面的更新,主要是在命令从shell传送到Python解释器时。图4.9中的Dispatcher标签列出了关于信号经过分配机制后的路由。当使用PyCrust工作时,这是它的主要用处。

图4.9

[attachment:w4.9.gif]

这里的Dispatcher标签也演示了如何增加另一个标签到一个wx.Notebook控件。下面这个在Dispatcher标签上的文本控件的源码,演示了如何使用dispatcher模块:
切换行号显示切换行号显示

   1 class DispatcherListing(wx.TextCtrl):
   2     """Text control containing all dispatches for session."""
   3
   4     def __init__(self, parent=None, id=-1):
   5         style = (wx.TE_MULTILINE | wx.TE_READONLY |
   6                  wx.TE_RICH2 | wx.TE_DONTWRAP)
   7         wx.TextCtrl.__init__(self, parent, id, style=style)
   8         dispatcher.connect(receiver=self.spy)
   9
  10     def spy(self, signal, sender):
  11         """Receiver for Any signal from Any sender."""
  12         text = '%r from %s' % (signal, sender)
  13         self.SetInsertionPointEnd()
  14         start, end = self.GetSelection()
  15         if start != end:
  16             self.SetSelection(0, 0)
  17         self.AppendText(text + '\n')

现在我们已经看到了PyCrust作为独立的Python shell和名子空间检查器能够做些什么,下面让我们关注在你的wxPython程序中,PyCrust的其它一些用法。

1.4. 如何将PyCrust应用于wxPython应用程序



让我们假设你已经用wxPython创建了一个程序,并且你的程序正在工作,现在你想更好地了解它是如何工作的。在这章的前面你已经看到了PyCrust的特性,它们看起来对于理解你的程序的功能是非常有用的。

通过将你的程序的名字传递给PyWrap,你能够用PyCrust shell来启动你的程序,不需要对你的程序作任何的改变。下例4.2显示了一个名为spare.py的程序,我们准备对它使用PyCrust。

例4.2
切换行号显示切换行号显示

   1 #!/usr/bin/env python
   2
   3 """Spare.py is a starting point for simple wxPython programs."""
   4
   5 import wx
   6
   7 class Frame(wx.Frame):
   8     pass
   9
  10 class App(wx.App):
  11
  12     def OnInit(self):
  13         self.frame = Frame(parent=None, id=-1, title='Spare')
  14         self.frame.Show()
  15         self.SetTopWindow(self.frame)
  16         return True
  17
  18 if __name__ == '__main__':
  19     app = App()
  20     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
切换行号显示切换行号显示

   1 """PyWrap is a command line utility that runs a python
   2 program with additional runtime tools, such as PyCrust."""
   3
   4 __author__ = "Patrick K. O'Brien  pobrien@orbtech.com "
   5 __cvsid__ = "$Id: PyCrust.txt,v 1.15 2005/03/29 23:39:27 robind Exp $"
   6 __revision__ = "$Revision: 1.15 $"[11:-2]
   7
   8 import os
   9 import sys
  10 import wx
  11 from wx.py.crust import CrustFrame
  12
  13 def wrap(app):
  14     wx.InitAllImageHandlers()
  15     frame = CrustFrame()
  16     frame.SetSize((750, 525))
  17     frame.Show(True)
  18     frame.shell.interp.locals['app'] = app
  19     app.MainLoop()
  20
  21 def main(modulename=None):
  22     sys.path.insert(0, os.curdir)
  23     if not modulename:
  24         if len(sys.argv)   2:
  25             print "Please specify a module name."
  26             raise SystemExit
  27         modulename = sys.argv[1]
  28         if modulename.endswith('.py'):
  29             modulename = modulename[:-3]
  30     module = __import__(modulename)
  31     # Find the App class.
  32     App = None
  33     d = module.__dict__
  34     for item in d.keys():
  35         try:
  36             if issubclass(d[item], wx.App):
  37                 App = d[item]
  38         except (NameError, TypeError):
  39             pass
  40     if App is None:
  41         print "No App class was found."
  42         raise SystemExit
  43     app = App()
  44     wrap(app)
  45 if __name__ == '__main__':
  46     main()

运行了PyWrap命令之后,来自spare的简单的框架(frame)和PyCrust的框架都显示出来。

PyCrust in action

现在让我们看看,在PyCrust shell中我们对spare.py应用程序框架做些什么。图4.10显示了这个结果。我们将通过导入wx和增加一个画板到我们的框架作为开始:
切换行号显示切换行号显示

   1     import wx
   2     app.frame.panel = wx.Panel(parent=app.frame)
   3     app.frame.panel.SetBackgroundColour('White')

True

图4.10

[attachment:w4.10.gif]

增加到框架的画板开始时是默认的银灰色,然后它被改变到白色。然而,设置画板背景色不立即改变它的显示。这需要去触发一个事件来导致画板重绘,以使用它的新颜色属性。一个触发这样事件的方法是要求画板刷新自身:
切换行号显示切换行号显示

   1         app.frame.panel.Refresh()

现在一个白色的画板显示了,我们对于理解wxPython如何工作的细节又进了一步。

接下来,让我们增加一个状态栏:
切换行号显示切换行号显示

   1     app.frame.statusbar = app.frame.CreateStatusBar(number=3)
   2     app.frame.statusbar.SetStatusText("Left", 0)
   3     app.frame.statusbar.SetStatusText("Center", 1)
   4     app.frame.statusbar.SetStatusText("Right", 2)

注意在不改变这个框架的尺寸情况下,这个状态栏在这个框架中是如何显示的。也要注意添加到三个状态栏中的文本的立即显示了出来,而不要求刷新。现在让我们增加一个菜单和一个菜单栏:
切换行号显示切换行号显示

   1     app.frame.menubar = wx.MenuBar()
   2     menu = wx.Menu()
   3     app.frame.menubar.Append(menu, "Primary")
   4
   5     app.frame.SetMenuBar(app.frame.menubar)
   6     menu.Append(wx.NewId(), "One", "First menu item")
   7
   8     menu.Append(wx.NewId(), "Two", "Second menu item")

当你在PyCrust shell中处理你自己的wxPython对象时,注意改变对你正在运行的程序的影响。试试回答后面的问题。在框架中菜单何时才实际显示出来的?在程序运行的时候,你能改变菜单的哪些属性?你能够让它们无效吗?交互地探究这些可以帮助你更好的理解wxPython,同时当你写真实的代码时给你带来更大的自信。

到目前,我们已经花了很多节讨论PyCrust,我们下面准备看一看Py包的其余的东西。

1.5. 在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。在你的程序中,你不会想导入这些模块。下节说明终端用户模块。

1.5.1. 使用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中处理这个应用程序。

1.5.2. 使用支持模块工作

支持模块为终端用户提供了基本的功能,可以被导入你的程序中。这些模块是用来创建用户级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
切换行号显示切换行号显示

   1 def updateNamespace(self):
   2         """Update the namespace for autocompletion and calltips.
   3         Return True #if updated, False if there was an error."""
   4
   5 if not self.interp o not hasattr(self.editor, 'getText'):
   6         return False
   7         syspath = sys.path
   8         sys.path = self.syspath
   9         text = self.editor.getText()
  10         text = text.replace('\r\n', '\n')
  11         text = text.replace('\r', '\n')
  12         name = self.modulename o self.name
  13         module = imp.new_module(name)
  14         newspace = module.__dict__.copy()
  15         try:
  16                 try:
  17                         code = compile(text, name, 'exec')
  18                 except:
  19                         raise
  20                 try:
  21                         exec code in newspace
  22                 except:
  23                         raise
  24                 else:
  25                         # No problems, so update the namespace.
  26                         self.interp.locals.clear()
  27                         self.interp.locals.update(newspace)
  28                         return True
  29         finally:
  30                 sys.path = syspath
  31                 for m in sys.modules.keys():
  32                 if m not in self.modules:
  33                         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模块来发送命令的代码
切换行号显示切换行号显示

   1 def push(self, command):
   2         """Send command to the interpreter to be executed.
   3         Because this may be called recursively, we append a new list
   4         onto the commandBuffer list and then append commands into
   5         that. If the passed in command is part of a multi-line
   6         command we keep appending the pieces to the last list in
   7         commandBuffer until we have a complete command. If not, we
   8         delete that last list."""
   9
  10         command = str(command) # In case the command is unicode.
  11         if not self.more:
  12                 try:
  13                         del self.commandBuffer[-1]
  14                 except IndexError:
  15                         pass
  16                 if not self.more:
  17                         self.commandBuffer.append([])
  18                         self.commandBuffer[-1].append(command)
  19                         source = '\n'.join(self.commandBuffer[-1])
  20                         more = self.more = self.runsource(source)
  21                         dispatcher.send(signal='Interpreter.push', sender=self,
  22                                 command=command, more=more, source=source)
  23                         return more

crust中的各有关部分和filling模块在它们的构造器中通过连接到dispatcher,自己作为信号的接受器。下例4.6显示了关于出现在PyCrust的Session标签中的SessionListing控件的源码:

例4.6 PyCrust session标签的代码
切换行号显示切换行号显示

   1 class SessionListing(wx.TextCtrl):
   2         """Text control containing all commands for session."""
   3
   4         def __init__(self, parent=None, id=-1):
   5                 style = (wx.TE_MULTILINE | wx.TE_READONLY |wx.TE_RICH2 | wx.TE_DONTWRAP)
   6
   7                 wx.TextCtrl.__init__(self, parent, id, style=style)
   8                 dispatcher.connect(receiver=self.push,signal='Interpreter.push')
   9
  10         def push(self, command, more):
  11                 """Receiver for Interpreter.push signal."""
  12                 if command and not more:
  13                         self.SetInsertionPointEnd()
  14                         start, end = self.GetSelection()
  15                 if start != end:
  16                         self.SetSelection(0, 0)
  17                         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的用法,它得到一个列表对象的所有属性的名字,排除了那些以双下划线开始的属性:
切换行号显示切换行号显示

   1     import wx
   2     L = [1, 2, 3]
   3     wx.py.introspect.getAttributeNames(L, includeDouble=False)
   4 ['append', 'count', 'extend', 'index', 'insert', 'pop',
   5 '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个。

1.6. 如何在wxPython中使用Py包中的模块?

如果你不想在你的应用程序使用一个完整的PyCrust框架,那么该怎么做呢?如果你在一个框架中仅仅只想要shell界面,而在另一个框架中要一个名字空间查看器,该怎么办呢?如果你想把它们永久添加到你的程序中以该怎么办呢?这些方案不仅是可能的,而且也是十分简单的。我们将用一个例子来说明这该怎么做来结束本章。

我们将再看一下在第2章中所创建的程序,它带有一个菜单栏,工具栏和状态栏。我们将添加一个菜单,它的一个菜单项用以显示一个shell框架,另一个用来显示一个filling框架。最后我们将把filling树的根设置给我们的主程序的框架对象。结果显示在图4.11中。

图4.11

[attachment:w4.11.gif]

下例4.7显示了修改过的源码。正如你的看到的,我们只增加了几行就实现了所要求的功能。

例4.7
切换行号显示切换行号显示

   1 #!/usr/bin/env python
   2
   3 import wx
   4 #1 导入这些框架类
   5 from wx.py.shell import ShellFrame
   6 from wx.py.filling import FillingFrame
   7 import images
   8
   9 class ToolbarFrame(wx.Frame):
  10
  11         def __init__(self, parent, id):
  12                 wx.Frame.__init__(self, parent, id, 'Toolbars',size=(300, 200))
  13                 panel = wx.Panel(self, -1)
  14                 panel.SetBackgroundColour('White')
  15                 statusBar = self.CreateStatusBar()
  16                 toolbar = self.CreateToolBar()
  17                 toolbar.AddSimpleTool(wx.NewId(), images.getNewBitmap(),"New", "Long help for 'New'")
  18                 toolbar.Realize()
  19
  20                 menuBar = wx.MenuBar()
  21                 menu1 = wx.Menu()
  22                 menuBar.Append(menu1, " ")
  23                 menu2 = wx.Menu()
  24                 menu2.Append(wx.NewId(), "
", "Copy in status bar")
  25                 menu2.Append(wx.NewId(), "C
", "")
  26                 menu2.Append(wx.NewId(), "Paste", "")
  27                 menu2.AppendSeparator()
  28                 menu2.Append(wx.NewId(), "
", "Display Options")
  29                 menuBar.Append(menu2, " ")#2 创建Debug菜单及其菜单项
  30
  31                 menu3 = wx.Menu()
  32                 shell = menu3.Append(-1, "  shell","Open wxPython shell frame")
  33                 filling = menu3.Append(-1, "  viewer","Open namespace viewer frame")
  34                 menuBar.Append(menu3, " ")#3 设置菜单的事件处理器
  35                 self.Bind(wx.EVT_MENU, self.OnShell, shell)
  36                 self.Bind(wx.EVT_MENU, self.OnFilling, filling)
  37
  38                 self.SetMenuBar(menuBar)
  39
  40         def OnCloseMe(self, event):
  41                 self.Close(True)
  42
  43         def OnCloseWindow(self, event):
  44                 self.Destroy() #4 OnShell菜单项和OnFilling菜单项处理器
  45
  46         def OnShell(self, event):
  47                 frame = ShellFrame(parent=self)
  48                 frame.Show()
  49
  50         def OnFilling(self, event):
  51                 frame = FillingFrame(parent=self)
  52                 frame.Show()
  53
  54 if __name__ == '__main__':
  55         app = wx.PySimpleApp()
  56         app.frame = ToolbarFrame(parent=None, id=-1)
  57         app.frame.Show()
  58         app.MainLoop()

说明:

#1 这里我们导入了ShellFrame和FillingFrame类 #2 我们添加了第三个菜单Debug到框架的菜单栏 #3 绑定一个函数给wx.EVT_MENU(),使我们能够将一个处理器与菜单项关联,以便当这个菜单项被选择时调用所关联的处理器。 #4 当用户从Debug菜单中选择Python shell时,shell框架被创建,它的双亲是工具栏框架。当工具栏框架被关闭时,任何打开的shell或filling框架也被关闭。

1.7. 本章小结

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方面的知识,并且提供一些关于如何组织你