本书《wxPython in Action》由Harri Pasanen和Robin Dunn撰写,介绍了wxPython的基础知识和高级应用,包括创建最小应用、事件驱动编程、使用对话框和菜单等内容。文档详尽地涵盖了从基本控件到复杂界面的构建和管理技巧,是学习wxPython开发的重要资源。读者将学会如何使用wxPython构建功能丰富的GUI程序。
90. 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检查它们以确定它的参数特性。
4.3.4 Session标签
Session标签是一个简单的文本控件,它列出了在当前shell会话中所键入的
所有命令。这使得剪切和粘贴命令以用在别处更为简单。
4.3.5 Dispatcher 标签
PyCrust包括了一个名为dispatcher的模块,它提供了在一个应用程序中联系
对象的机制。PyCrust使用dispatcher来维持它的界面的更新,主要是在命令从
shell传送到Python解释器时。图4.9中的Dispatcher标签列出了关于信号经过分配
机制后的路由。当使用PyCrust工作时,这是它的主要用处。
90 / 565
91. 图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’)
91 / 565
100. 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
100 / 565
188. 过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)
7.1.8 如何响应文本事件?
有一个由wx.TextCtrl窗口部件产生的便利的命令事件,你可能想用它。你
需要把相关事件传递给Bind方法以捕获该事件,如下所示:
188 / 565
279. 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控制启动提示显示时,复选框是否被
选择。该函数的返回值是复选框的状态值,以便你使用该值来决定你的应用程
序下次启动时是否显示启动提示。
9.5 使用验证器(validator)来管理对话框中的数据
验证器是一个特殊的wxPython对象,它简化了对话框中的数据管理。当我
们在第三章中讨论事件时,我们简要的提及到如果一个窗口部件有一个验证
器,那么该验证器能够被事件系统自动调用。我们已经见过了几个wxPython窗
口部件类的构造函数中将验证器作为参数,但是我们还没有讨论它们。
验证器有三个不相关的功能:
1、在对话框关闭前验证控件中的数据
2、自动与对话框传递数据
3、验证用户键入的数据
9.5.1 如何使用验证器来确保正确的数据?
验证器对象是wx.Validator的子类。父类是抽象的,不能直接使用。尽管在
C++ wxWidget集中有一对预定义的验证器类,但是在wxPython中,你需要定义
279 / 565
280. 你自己的验证器类。正如我们在别处所见的,你的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):
280 / 565
284. 图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):# 声明验证器
284 / 565
316. 图10.6
弹出菜单的创建是非常类似于标准菜单的,但是它们不附加到菜单栏。例
10.9显示了一个弹出菜单的示例代码。
例10.9 在任意一个窗口部件中创建一个弹出式菜单
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1,
”Popup Menu Example”)
self.panel = p = wx.Panel(self)
menu = wx.Menu()
exit = menu.Append(-1, ”Exit”)
self.Bind(wx.EVT_MENU, self.OnExit, exit)
menuBar = wx.MenuBar()
menuBar.Append(menu, ”Menu”)
self.SetMenuBar(menuBar)
wx.StaticText(p, -1,
”Right-click on the panel to show a popup menu”,
(25,25))
self.popupmenu = wx.Menu()#创建一个菜单
for text in ”one two three four five”.split():#填充菜单
316 / 565
352. 图11.15
例11.10显示了产生上图的代码。这里有三个值得注意的事件。首先你必须
单独于sizer创建静态框对象,第二是这个例子展示了如何使用嵌套的
box sizer。本例中,三个垂直的static box sizer被放置于一个水平的box sizer中。
例11.10 static box sizer的一个例子
import wx
from blockwindow import BlockWindow
labels = ”one two three four five six seven eight nine”.split()
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, ”StaticBoxSizer Test”)
self.panel = wx.Panel(self)
# make three static boxes with windows positioned inside them
box1 = self.MakeStaticBoxSizer(“Box 1”, labels[0:3])
box2 = self.MakeStaticBoxSizer(“Box 2”, labels[3:6])
box3 = self.MakeStaticBoxSizer(“Box 3”, labels[6:9])
# We can also use a sizer to manage the placement of other
# sizers (and therefore the windows and sub-sizers that they
# manage as well.)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(box1, 0, wx.ALL, 10)
sizer.Add(box2, 0, wx.ALL, 10)
sizer.Add(box3, 0, wx.ALL, 10)
self.panel.SetSizer(sizer)
sizer.Fit(self)
352 / 565
383. 是单色的,那么当前文本的前景色和背景色被用于该位图,否则,该位图自己
的颜色方案被使用。图12.3显示了相关功能。
图12.3
例12.3显示了一个使用设备上下文来显示位图的一个简单例子,用于创建
图12.3。
例12.3 创建一个设备上下文并绘制一个位图
# This one shows how to draw images on a DC.
import wx
import random
random.seed()
class RandomImagePlacementWindow(wx.Window):
def __init__(self, parent, image):
wx.Window.__init__(self, parent)
self.photo = image.ConvertToBitmap()# 创建位图
# choose some random positions to draw the image at:
# 创建随机的位置
self.positions = [(10,10)]
383 / 565
396. 13.1.1 什么是图标模式?
列表控件看起来类似于微软的Windows资源管理器的一个文件树系统的显
示面板。它以四种模式之一的一种显示一个信息的列表。默认模式是图标模
式,显示在列表中的每个元素都是一个其下带有文本的一个图标。图13.1显示
了一个图标模式的列表。
例13.1是产生图13.1的代码。注意这个例子使用了同目录下的一些.png文
件。
例13.1 创建一个图标模式的列表
#-*- encoding:UTF-8 -*-
import wx
import sys, glob
class DemoFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1,
”wx.ListCtrl in wx.LC_ICON mode”,
size=(600,400))
# load some images into an image list
il = wx.ImageList(32,32, True)#创建图像列表
for name in glob.glob(“icon??.png”):
bmp = wx.Bitmap(name, wx.BITMAP_TYPE_PNG)
il_max = il.Add(bmp)
# create the list control
#创建列表窗口部件
self.list = wx.ListCtrl(self, -1,
style=wx.LC_ICON | wx.LC_AUTOARRANGE)
# assign the image list to it
self.list.AssignImageList(il, wx.IMAGE_LIST_NORMAL)
# create some items for the list
#为列表创建一些项目
for x in range(25):
img = x % (il_max+1)
396 / 565
412. EVT_LiST_COL_END_DRAG:当用户完成对一个列表边框的拖动时,触发该事件
例13.3显示了一些列表事件的处理,并也提供了方法的一些演示。
例13.3 一些不同列表事件和属性的一个例子
import wx
import sys, glob, random
import data
class DemoFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1,
”Other wx.ListCtrl Stuff”,
size=(700,500))
self.list = None
self.editable = False
self.MakeMenu()
self.MakeListCtrl()
def MakeListCtrl(self, otherflags=0):
# if we already have a listctrl then get rid of it
if self.list:
self.list.Destroy()
if self.editable:
otherflags |= wx.LC_EDIT_LABELS
# load some images into an image list
il = wx.ImageList(16,16, True)
for name in glob.glob(“smicon??.png”):
bmp = wx.Bitmap(name, wx.BITMAP_TYPE_PNG)
il_max = il.Add(bmp)
# create the list control
self.list = wx.ListCtrl(self, -1, style=wx.LC_REPORT|otherflags)
# assign the image list to it
412 / 565
498. 图15.3
例15.4是产生上图的代码
例15.4 使用树列表控件
import wx
import wx.gizmos
import data
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title=”TreeListCtrl”, size=(400,500))
# Create an image list
il = wx.ImageList(16,16)
# Get some standard images from the art provider and add them
# to the image list
self.fldridx = il.Add(
wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, (16,16)))
self.fldropenidx = il.Add(
498 / 565
525. 图17.1
例17.1显示了我们已经讨论过的打印构架和我们将要接触的打印对话框机
制。
例17.1 打印构架的一个较长的例子
import wx
import os
FONTSIZE = 10
class TextDocPrintout(wx.Printout):
”””
A printout class that is able to print simple text documents.
Does not handle page numbers or titles, and it assumes that no
lines are longer than what will fit within the page width. Those
features are left as an exercise for the reader. ;-)
”””
def __init__(self, text, title, margins):
525 / 565
544. 图18.1
例18.1是产生图18.1的代码。
例18.1 剪贴板交互示例
#-*- encoding:UTF-8 -*-
import wx
t1_text = ”””
The whole contents of this control
will be placed in the system’s
clipboard when you click the copy
button below.
“””
t2_text = ”””
If the clipboard contains a text
data object then it will be placed
in this control when you click
the paste button below. Try
copying to and pasting from
other applications too!
“””
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title=”Clipboard”,
544 / 565