Differences between revisions 2 and 3
Revision 2 as of 2007-10-03 11:32:50
Size: 61989
Editor: hairui
Comment:
Revision 3 as of 2007-10-03 11:48:08
Size: 58726
Editor: hairui
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
[[TableOfContents]]
                  
= 把窗口部件放入框架中 =

在你的wxPython中,所有的用户交互行为都发生在一个窗口部件容器中,它通常被称作窗口,在wxPython 中被称为框架。在这一章中,我们将讨论wxPython中的几个不同样式的框架。这个主要的wx.Frame有几个不同的框架样式,这些样式可以改变wx.Frame的外观。另外,wxPython提供了小型框架和实现多文档界面的框架。框架可以使用分隔条来划分为不同的部分,并且可以通过滚动条的使用来包含比框架本身大的面板(panel)。
== 使用模式对话框工作 ==


模式对话框用于与用户进行快速的交互或在用户可以执行程序的下一步之前,对话框中的信息必须被输入的时候。在wxPython中,有几个标准的函数用来显示基本的模式对话框。这些对话框包括警告框,单行文本域,和从列表中选择。在随后的部分,我们将给你展示这些对话框,以及如何使用这些预定义的函数来减轻你的工作量。
Line 8: Line 7:
== 框架的寿命 ==


我们将通过讨论框架最基本的元素:创建和除去它们,来作为我们的开始。创建框架包括了解可以应用的所有样式元素;框架的去除可能比你原本想像的要复杂。

 
=== 如何创建一个框架? ===


在本书中我们已经见过了许多的框架创建的例子,但是我们仍将再回顾一下框架创建的初步原则。


'''创建一个简单的框架'''

框架是类wx.Frame的实例。例8.1显示了一个非常简单的框架创建的例子。


'''例8.1'''
'''创建基本的wx.Frame'''
{{{#!python
import wx

if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = wx.Frame(None, -1, "A Frame", style=wx.DEFAULT_FRAME_STYLE,
        size=(200, 100))
    frame.Show()
    app.MainLoop()
}}}

上面的代码创建一个带有标题的框架,其大小是(200,100)。表8.1中的默认样式提供了标准框架的装饰如关闭框、最小化和最大化框。结果如图8.1所示。

图8.1

attachment:w8.1.gif


wx.Frame的构造函数类似于我们在第7章见到的其它窗口部件的构造函数:

{{{#!python
wx.Frame(parent, id=-1, title="", pos=wx.DefaultPosition,
 size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,
 name="frame")
}}}
这里有超过十余种之多的专用于wx.Frame的样式标记,我们将在下一部分涵盖它们。默认样式为你提供了最小化和最大化框、系统下拉菜单、可调整尺寸的粗边框和一个标题。

这里没有与一个wx.Frame挂钩的事件类型。但是,由于一个wx.Frame是你的屏幕上用户最可能去关闭的元素,所以你通常想去为关闭事件定义一个处理器,以便子窗口和数据被妥善的处理。


'''创建框架的子类'''

你将很少直接创建wx.Frame的实例。正如我们在本书中所见过的其它例子一样,一个典型的wxPython应用程序创建wx.Frame的子类并创建那些子类的实例。这是因为wx.Frame的独特的情形——虽然它自身定义了很少的行为,但是带有独自的初始化程序的子类是放置有关你的框架的布局和行为的最合理的地方。不创建子类而构造你应用程序的特定的布局是有可能,但除了最简单的应用程序以外,那是不容易的。例8.2展示了wx.Frame子类的例子。


'''例8.2'''
'''一个简单的框架子类'''
{{{#!python
import wx

class SubclassFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'Frame Subclass',
=== 如何创建一个模式对话框? ===


模式对话框阻塞了别的窗口部件接收用户事件,直到该模式对话框被关闭;换句话说,在它存在期间,用户一直被置于对话模式中。如图9.1所示,你不能总是根据外观来区别对话框和框架。在wxPython中,对话框与框架间的区别不是基于它们的外观的,而主要是它们处理事件的办法的实质。


'''图9.1'''
'''一个模式对话框'''

attachment:w9.1.gif


对话框的创建和配置与框架稍微有些不同。例9.1显示了产生图9.1的代码。所显示的对话框上的按钮被敲击后,该对话框就关闭了,并且一条消息被输出到stdout(标准输出)。


'''例9.1'''
'''定义一个模式对话框'''
{{{#!python
import wx

class SubclassDialog(wx.Dialog):
    def __init__(self):#初始化对话框
        wx.Dialog.__init__(self, None, -1, 'Dialog Subclass',
Line 71: Line 31:
        panel = wx.Panel(self, -1)
        button = wx.Button(panel, -1, "Close Me", pos=(15, 15))
        self.Bind(wx.EVT_BUTTON, self.OnCloseMe, button)
        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)

    def OnCloseMe(self, event):
        self.Close(True)

    def OnCloseWindow(self, event):
        self.Destroy()

if __name__ == '__main__':
    app = wx.PySimpleApp()
    SubclassFrame().Show()
    app.MainLoop()
}}}

运行结果如图8.2所示

图8.2

attachment:w8.2.gif


我们在许多其它的例子中已经见过了这种基本的结构,因此让我们来讨论上面代码中特定于框架的部分。wx.Frame.__init__方法与wx.Frame构造函数有相同的信息。子类自身的构造器除了self没有其它参数,它允许你作为程序员去定义参数,所定义的参数将传递给其父类,并且使你可以不用重复指定与父类相同的参数。

同样值得注意的是,框架的子窗口部件被放置在一个面板(panel)中。面板(panel)是类wx.Panel的实例,它是其它有较少功能的窗口部件的容器。你基本上应该使用一个wx.Panel作为你的框架的顶级子窗口部件。有一件事情就是,多层次的构造可以使得更多的代码能够重用,如相同的面板和布局可以被用于多个框架中。在框架中使用wx.Panel给了你一些对话框的功能。这些功能以成对的方式表现。其一是,在MS Windows操作系统下,wx.Panel实例的默认背景色以白色代替了灰色。其二,面板(panel)可以有一个默认的项目,该项目在当回车键被按下时自动激活,并且面板(panel)以与对话框大致相同的办法响应tab键盘事件,以改变或选择默认项目。

  
                   
=== 有些什么不同的框架样式? ===


wx.Frame有许多的可能的样式标记。通常,默认样式就是你想要的,但也有一些有用的变种。我们将讨论的第一组样式控制框架的形状和尺寸。尽管不是强制性的,但是这些标记应该被认为是互斥的——一个给定的框架应该只使用它们中的一个。表8.1说明了形状和尺寸标记。


'''表8.1 框架的形状和尺寸标记'''

||wx.FRAME_NO_TASKBAR||一个完全标准的框架,除了一件事:在Windows系统和别的支持这个特性的系统下,它不显示在任务栏中。当最小化时,该框架图标化到桌面而非任务栏。||
||wx.FRAME_SHAPED||非矩形的框架。框架的确切形状使用SetShape()方法来设置。窗口的形状将在本章后面部分讨论。||
||wx.FRAME_TOOL_WINDOW||该框架的标题栏比标准的小些,通常用于包含多种工具按钮的辅助框架。在Windows操作系统下,工具窗口将不显示在任务栏中。||
||wx.ICONIZE||窗口初始时将被最小化显示。这个样式仅在Windows系统中起作用。||
||wx.MAXIMIZE||窗口初始时将被最大化显示(全屏)。这个样式仅在Windows系统中起作用。||
||wx.MINIMIZE||同wx.ICONIZE。||

上面这组样式中,屏幕画面最需要的样式是wx.FRAME_TOOL_WINDOW。图8.3显示了一个小的结合使用了wx.FRAME_TOOL_WINDOW、wx.CAPTION和wx.SYSTEM_MENU样式的例子。


'''图8.3'''

attachment:w8.3.gif


这里有两个互斥的样式,它们控制一个框架是否位于别的框架的上面,无论别的框架是否获得了焦点。这对于那些小的不是始终可见的对话框是有用的。表8.2说明了这两个样式。最后,这还有一些用于放置在你的窗口上的装饰。如果你没有使用默认样式,那么这些装饰将不被自动放置到你的窗口上,你必须添加它们,否则容易导致窗口不能关闭或移动。表8.3给出了这些装饰的列表。


'''表8.2'''
'''针对窗口漂浮行为的样式'''

||wx.FRAME_FLOAT_ON_PARENT||框架将漂浮在其父窗口(仅其父窗口)的上面。(很明显,要使用这个样式,框架需要有一个父窗口)。其它的框架可以遮盖这个框架。||
||wx.STAY_ON_TOP||该框架将始终在系统中其它框架的上面。(如果你有多个框架使用了这个样式,那么它们将相互重叠,但对于系统中其它的框架,它们仍在上面。)||

默认的样式wx.DEFAULT_FRAME_STYLE等同于wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.CLOSE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU |wx.CAPTION。这个样式创建了一个典型的窗口,你可以调整大小,最小化,最大化,或关闭。一个很好的主意就是当你想要使用除默认样式以外的样式时,将默认样式与其它的样式组合在一起,以确保你有正确的一套装饰。例如,要创建一个工具框架,你可以使用style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_TOOL_WINDOW。记住,你可以使用^操作符来去掉不要的样式。


'''表8.3 用于装饰窗口的样式'''

||wx.CAPTION||给窗口一个标题栏。如果你要放置最大化框、最小化框、系统菜单和上下文帮助,那么你必须包括该样式。||
||wx.FRAME_EX_CONTEXTHELP||这是用于Windows操作系统的,它在标题栏的右角放置问号帮助图标。这个样式是与wx.MAXIMIZE_BOX和WX.MINIMIZE_BOX样式互斥的。它是一个扩展的样式,并且必须使用两步来创建,稍后说明。||
||wx.FRAME_EX_METAL||在Mac OS X上,使用这个样式的框架有一个金属质感的外观。这是一个附加样式,必须使用SetExtraStyle方法来设置。||
||wx.MAXIMIZE_BOX||在标题栏的标准位置放置一个最大化框。||
||wx.MINIMIZE_BOX||在标题栏的标准位置放置一个最小化框。||
||wx.CLOSE_BOX||在标题栏的标准位置放置一个关闭框。||
||wx.RESIZE_BORDER||给框架一个标准的可以手动调整尺寸的边框。||
||wx.SIMPLE_BORDER||给框架一个最简单的边框,不能调整尺寸,没有其它装饰。该样式与所有其它装饰样式是互斥的||
||wx.SYSTEM_MENU||在标题栏上放置一个系统菜单。这个系统菜单的内容与你所使用的装饰样式有关。例如,如果你使用wx.MINIMIZE_BOX,那么系统菜单项就有“最小化”选项。||

  
                   
=== 如何创建一个有额外样式信息的框架? ===


wx.FRAME_EX_CONTEXTHELP是一个扩展样式,意思是样式标记的值太大以致于不能使用通常的构造函数来设置(因为底层C++变量类型的特殊限制)。通常你可以在窗口部件被创建后,使用SetExtraStyle方法来设置额外的样式,但是某些样式,比如wx.FRAME_EX_CONTEXTHELP,必须在本地UI(用户界面)对象被创建之前被设置。在wxPython中,这需要使用稍微笨拙的方法来完成,即分两步构建。之后标题栏中带有我们熟悉的问号图标的框架就被创建了。如图8.4所示。

图8.4

attachment:w8.4.gif


标记值必须使用SetExtraStyle()方法来设置。有时,额外样式信息必须在框架被实例化前被设置,这就导致了一个问题:你如何对于一个不存在的实例调用一个方法?在接下来的部分,我们将展示实现这种操作的两个机制。


'''添加额外样式信息'''

在wxPython中,额外样式信息在创建之前通过使用专门的类wx.PreFrame来被添加,它是框架的一种局部实例。你可以在预框架(preframe)上设置额外样式位,然后使用这个预框架(preframe)来创建实际的框架。例8.3显示了在一个子类的构造器中如何完成这两步(two-step)的构建。注意,在wxPython中它实际上是三步(在C++ wxWidgets工具包中,它是两步(two-step),我们只是沿用这个叫法而已)。


'''例8.3'''
{{{#!python
import wx

class HelpFrame(wx.Frame):

    def __init__(self):
        pre = wx.PreFrame() #1 预构建对象
        pre.SetExtraStyle(wx.FRAME_EX_CONTEXTHELP)
        pre.Create(None, -1, "Help Context", size=(300, 100),
                style=wx.DEFAULT_FRAME_STYLE ^
                (wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX)) #2 创建框架
        self.PostCreate(pre) #3 底层C++指针的传递

if __name__ == '__main__':
    app = wx.PySimpleApp()
    HelpFrame().Show()
    app.MainLoop()

}}}

'''#1''' 创建wx.PreFrame()的一个实例(关于对话框,这有一个类似的wx.PreDialog()——其它的wxWidgets窗口部件有它们自己的预类)。在这个调用之后,你可以做你需要的其它初始化工作。


'''#2''' 调用Create()方法创建框架。


'''#3''' 这是特定于wxPython的,并且不由C++完成。PostCreate方法做一些内部的内务处理,它实例化一个你在第一步中创建的封装了C++的对象。


'''添加额外样式信息的通用方法'''

先前的算法有点笨拙,但是它可以被重构得容易一点,以便于管理维护。第一步是创建一个公用函数,它可以管理任何分两步的创建。例8.4提供了一个例子,它使用Python的内省性能来调用以变量形式被传递的函数。这个例子用于在Python的一个新的框架实例化期间的__init__方法中被调用。


'''例8.4'''
'''一个公用的两步式创建函数'''
{{{#!python
def twoStepCreate(instance, preClass, preInitFunc, *args,**kwargs):
 pre = preClass()
 preInitFunc(pre)
 pre.Create(*args, **kwargs)
 instance.PostCreate(pre)
}}}
在例8.4中,函数要求三个必须的参数。instance参数是实际被创建的实例。preClass参数是临时的预类的类对象——对框架预类是wx.PreFrame。preInitFunc是一个函数对象,它通常作为回调函数用于该实例的初始化。这三个参数之后,我们可以再增加任意数量的其它可选参数。

这个函数的第一行,pre = preClass(),内省地实例化这个预创建对象,使用作为参数传递过来的类对象。下面一行根据参数preInitFunc内省地调用回调函数,它通常设置扩展样式标记。然后pre.Create()方法被调用,它使用了可选的参数。最后,PostCreate方法被调用来将内在的值从pre移给实例。至此,instance参数已经完全被创建了。假设twoStepCreate已被导入,那么上面的公用函数可以如例8.5被使用。


'''例8.5'''
'''另一个两步式的创建,使用了公用函数'''
{{{#!python
import wx

class HelpFrame(wx.Frame):

def __init__(self, parent, ID, title,pos=wx.DefaultPosition, size=(100,100),style=wx.DEFAULT_DIALOG_STYLE):
 twoStepCreate(self, wx.PreFrame, self.preInit, parent,
 id, title, pos, size, style)

def preInit(self, pre):
 pre.SetExtraStyle(wx.FRAME_EX_CONTEXTHELP)
}}}

类wx.PreFrame和函数self.preInit被传递给公用函数,并且preInit方法被定义为回调函数。

  
                   
=== 当关闭一个框架时都发生了什么? ===


当你关闭一个框架时,它最终消失了。除非这个框架被明确地告诉不关闭。换句话说,那关闭不是直接了当的。在wxPython的窗口部件关闭体系之后的用意是,给正在关闭的窗口部件充足的机会来关闭或释放它所占用任何非wxPython资源。如果你占用了某种昂贵的外部资源,如一个大的数据结构或一个数据库连接,那么该意图是特别受欢迎的。

诚然,在C++ wxWidgets世界里,由于C++不为你管理内在分配的清理工作,管理资源是更严肃的问题。在wxPython中,对于多步的关闭过程的显式需求就很少,但它对于在过程中使用额外的钩子仍然是有用的。(随便说一下,我们在这一节中从单词“框架”切换到单词“窗口部件”是故意的——因为在本节中的所有内容都适用于所有顶级窗口部件,如框架或对话框)。


'''何时用户触发关闭过程'''

关闭过程最常由用户触发,如敲击一个关闭框或选择系统菜单中的关闭项或当应用程序响应其它某个事件而调用框架的Close方法。当上述情况发生时,wxPython架构引发一个EVT_CLOSE事件。像wxPython 架构中的其它别的事件一样,你可以在绑定一个事件处理器以便一个EVT_CLOSE事件发生时调用。

如果你不声明你自己的事件处理器,那么默认的行为将被调用。默认的行为对于框架和对话框是不同的。

1、默认情况下,框架处理器调用Destroy()方法并删除该框架和它的所有的组件。

2、默认情况下,对话框的关闭处理器不销毁该对话框——它仅仅模拟取消按钮的按下,并隐藏对话框。该对话框对象仍继续存在在内存中。因此,如果需要的话,应用程序可以从它的数据输入部件获取值。当应用程序完成了对对话框的使用后,应该调用对话框的Destroy()方法。

如果你编写你自己的关闭处理器,那么你可以使用它来关闭或删除任何外部的资源,但是,如果你选择去删除框架的话,显式地调用Destroy()方法是你的责任。尽管Destroy()经常被Close()调用,但是只调用Close()方法不能保证框架的销毁。在一定的情形下,决定不销毁框架是完全可以的,如当用户取消了关闭。然而,你仍然需要一个方法来销毁该框架。如果你选择不去销毁窗口,那么调用关闭事件的wx.CloseEvent.Veto()方法来通知相关部分:框架拒绝关闭,是一个好的习惯。

如果你选择在你的程序的别处而非关闭处理器中关闭你的框架,例如从一个不同的用户事件像一个菜单项,那么我们建议使用的机制是调用框架的Close()方法。这将启动一个和系统关闭行为相同的过程。如果你要确保框架一定被删除,那么你可以直接调用Destroy()方法;然而,如果你这样做了,可能会导致框架所管理的资源或数据没有被释放或保存。


'''什么时候系统触发关闭过程'''

如果关闭事件是由系统自己触发的,对于系统关闭或类似情况,你也有一种办法管理该事件。wx.App 类接受一个EVT_QUERY_END_SESSION事件,如果需要的话,该事件使你能够否决应用程序的关闭,如果所有运行的应用已经批准了系统或GUI环境的关闭的话,那么随后会有一个EVT_END_SESSION事件。你选择去否决关闭的行为是与依赖于具体系统的。

最后,值得注意的是,调用一个窗口部件的Destroy()方法不意味该部件被立即销毁。销毁实际上是当事件循环在未来空闲时(任何未被处理的事件被处理之后)才被处理的。这就防止了处理已不存在的窗口部件的事件。

在接下来的两节,我们的讨论将从一个框架的生命周期切换到在框架生命周期里,你能够用框架来做些什么。


                   
== 使用框架 ==


框架包含了许多方法和属性。其中最重要的是那些查找框架中任意窗口部件的方法,和滚动框架中内容的方法。在这一节,我们将讨论如何实现这些。

 
=== wx.Frame有那些方法和属性? ===


这部分中的表包含了wx.Frame和它的父类wx.Window的最基本的属性。这些属性和方法的许多在本书中的其它地方有更详细的说明。表8.4包含了wx.Frame的一些公共的可读、可修改的属性。


'''表8.4'''
'''wx.Frame的公共属性'''

||GetBackgroundColor(),SetBackgroundColor(wx.Color)||背景色是框架中没有被其子窗口部件覆盖住的那些部分的颜色。你可以传递一个wx.Color或颜色名给设置方法。任何传递给需要颜色的wxPython方法的字符串,都被解释为对函数wx.NamedColour()的调用。||
||GetId(),SetId(int)||返回或设置窗口部件的标识符。||
||GetMenuBar(),SetMenuBar(wx.MenuBar)||得到或设置框架当前使用的的菜单栏对象,如果没有菜单栏,则返回None。||
||GetPosition(),GetPositionTuple(),SetPosition(wx.Point)||以一个wx.Point或Python元组的形式返回窗口左上角的x,y的位置。对于顶级窗口,该位置是相对于显示区域的坐标,对于子窗口,该位置是相对于父窗口的坐标。||

GetSize()
GetSizeTuple()
SetSize(wx.Size):C++版的get*或set*方法被覆盖。默认的get*或set*使用一个wx.Size对象。GetSizeTuple()方法以一个Python元组的形式返回尺寸。也可以参看访问该信息的另外的方法SetDimensions()。

GetTitle()
SetTitle(String):得到或设置框架标题栏的字符串。

表8.5显示了一些wx.Frame的非属性类的更有用的方法。其中要牢记的是Refresh(),你可以用它来手动触发框架的重绘。


'''表8.5'''
'''wx.Frame的方法'''

Center(direction=wx.BOTH):框架居中(注意,非美语的拼写Centre,也被定义了的)。参数的默认值是wx.BoTH,在此情况下,框是在两个方向都居中的。参数的值若是wx.HORIZONTAL或wx.VERTICAL,表示在水平或垂直方向居中。

Enable(enable=true):如果参数为true,则框架能够接受用户的输入。如果参数为False,则用户不能在框架中输入。相对应的方法是Disable()。

GetBestSize():对于wx.Frame,它返回框架能容纳所有子窗口的最小尺寸。

Iconize(iconize):如果参数为true,最小化该框架为一个图标(当然,具体的行为与系统有关)。如果参数为False,图标化的框架恢复到正常状态。

IsEnabled():如果框架当前有效,则返回True。

IsFullScreen():如果框架是以全屏模式显示的,则返回True,否则False。细节参看ShowFullScreen。

IsIconized():如果框架当前最小化为图标了,则返回True,否则False。

IsMaximized():如果框架当前是最大化状态,则返回True,否则False。

IsShown():如果框架当前可见,则返回True。

IsTopLevel():对于顶级窗口部件如框架或对话框,总是返回True,对于其它类型的窗口部件返回False。

Maximize(maximize):如果参数为True,最大化框架以填充屏幕(具体的行为与系统有关)。这与敲击框架的最大化按钮所做的相同,这通常放大框架以填充桌面,但是任务栏和其它系统组件仍然可见。

Refresh(eraseBackground=True,
rect=None):触发该框架的重绘事件。如果rect是none,那么整个框架被重画。如果指定了一个矩形区域,那么仅那个矩形区域被重画。如果eraseBackground为True,那么这个窗口的北影也将被重画,如果为False,那么背景将不被重画。

SetDimensions(x, y, width, height,
sizeFlags=wx.SIZE_AUTO):使你能够在一个方法调用中设置窗口的尺寸和位置。位置由参数x和y决定,尺寸由参数width和height决定。前四个参数中,如果有的为-1,那么这个-1将根据参数sizeFlags的值作相应的解释。表8.6包含了参数sizeFlags的可能取值。

Show(show=True):如果参数值为True,导致框架被显示。如果参数值为False,导致框架被隐藏。Show(False)等同于Hide()。

ShowFullScreen(show,
style=wx.FULLSCREEN_ALL):如果布尔参数是True,那么框架以全屏的模式被显示——意味着框架被放大到填充整个显示区域,包括桌面上的任务栏和其它系统组件。如果参数是False,那么框架恢复到正常尺寸。style参数是一个位掩码。默认值wx.FULLSCREEN_ALL指示wxPython当全屏模式时隐藏所有窗口的所有样式元素。后面的这些值可以通过使用按位运算符来组合,以取消全屏模式框架的部分装饰:wx.FULLSCREEN_NOBORDER, wx.FULLSCREEN_NOCAPTION,
wx.FULLSCREEN_NOMENUBAR,
wx.FULLSCREEN_NOSTATUSBAR,
wx.FULLSCREEN_NOTOOLBAR。

表8.5中说明的SetDimensions()方法在用户将一个尺寸指定为-1时,使用尺寸标记的一个位掩码来决定默认行为。表8.6说明了这些标记。

这些方法没有涉及框架所包含的孩子的位置问题。这个问题要求框架的孩子自已去说明它。


'''表8.6'''
'''关于SetDimensions方法的尺寸标记'''

wx.ALLOW_MINUS_ONE:一个有效的位置或尺寸。

wx.SIZE_AUTO:转换为一个wxPython默认值。

wx.SIZE_AUTO_HEIGHT:一个有效的高度,或一个wxPython默认高度。

wx.SIZE_AUTO_WIDTH:一个有效的宽度,或一个wxPython默认宽度。

wx.SIZE_USE_EXISTING:使用现有的尺寸。

  
                   
=== 如何查找框架的子窗口部件? ===


有时候,你将需要查找框架或面板(panel)上的一个特定的窗口部件,并且你没有它的相关引用。如第6章所示的这种情况的一个公用的应用程序,它查找与所选菜单相关的实际的菜单项对象(因为事件不包含对它的一个引用)。另一种情况就是,当你想基于一个项的事件去改变系统中其它任一窗口部件的状态时。例如,你可能有一个按钮和一个菜单项,它们互相改变彼此的开关状态。当按钮被敲击时,你需要去得到菜单项以触发它。例8.6显示了一个摘自第7章的一个小的例子。在这个代码中,FindItemById()方法用来去获得与事件对象所提供的ID相关的菜单项。该项的标签被用来驱动所要求的颜色的改变。


'''例8.6'''
'''通过ID查找项目的函数'''
{{{#!python
def OnColor(self, event):
 menubar = self.GetMenuBar()
 itemId = event.GetId()
 item = menubar.FindItemById(itemId)
 color = item.GetLabel()
 self.sketch.SetColor(color)
}}}
在wxPython中,有三种查找子窗口部件的方法,它们的行为都很相似。这些方法对任何作为容器的窗口部件都是适用的,不单单是框架,还有对话框和面板(panel)。你可以通过内部的wxPython ID查寻一个窗口部件,或通过传递给构造函数的名字(在name参数中),或通过文本标签来查寻。文本标签被定义为相应窗口部件的标题,如按钮和框架。

这三种方法是:

 1.wx.FindWindowById(id, parent=None)

 1.wx.FindWindowByName(name, parent=None)

 1.wx.FindWindowByLabel(label, parent=None)

这三种情况中,parent参数可以被用来限制为对一个特殊子层次的搜索(也就是,它等同于父类的Find方法)。还有,FindWindowByName()首先按name参数查找,如果没有发现匹配的,它就调用FindWindowByLabel()去查找一个匹配。

 
=== 如何创建一个带有滚动条的框架? ===


在wxPython中,滚动条不是框架本身的一个元素,而是被类wx.ScrolledWindow控制。你可以在任何你要使用wx.Panel的地方使用wx.ScrolledWindow,并且滚动条移动所有在滚动窗口中的项目。图8.5和图8.6显示了滚动条,包括它的初始状态和滚动后的状态。从图8.5到图8.6,左上的按钮移出了视野,右下的按钮移进了视野。

在这一节,我们将讨论如何去创建一个带有滚动条的窗口以及如何在你的程序中处理滚动行为。


'''图8.5'''

attachment:w8.5.gif


'''图8.6'''

attachment:w8.5.gif



'''如何创建滚动条'''

例8.7显示了用于创建滚动窗口的代码。


'''例8.7'''
'''创建一个简单的滚动窗口'''
{{{#!python
import wx

class ScrollbarFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'Scrollbar Example',
                size=(300, 200))
        self.scroll = wx.ScrolledWindow(self, -1)
        self.scroll.SetScrollbars(1, 1, 600, 400)
        self.button = wx.Button(self.scroll, -1, "Scroll Me", pos=(50, 20))
        self.Bind(wx.EVT_BUTTON, self.OnClickTop, self.button)
        self.button2 = wx.Button(self.scroll, -1, "Scroll Back", pos=(500, 350))
        self.Bind(wx.EVT_BUTTON, self.OnClickBottom, self.button2)

    def OnClickTop(self, event):
        self.scroll.Scroll(600, 400)
        
    def OnClickBottom(self, event):
        self.scroll.Scroll(1, 1)
        okButton = wx.Button(self, wx.ID_OK, "OK", pos=(15, 15))
        okButton.SetDefault()
        cancelButton = wx.Button(self, wx.ID_CANCEL, "Cancel",
                pos=(115, 15))
Line 435: Line 38:
    frame = ScrollbarFrame()
    frame.Show()
    app.MainLoop()
}}}

wx.ScrolledWindow的构造函数几乎与wx.Panel的相同:
{{{#!python
wx.ScrolledWindow(parent, id=-1, pos=wx.DefaultPosition,
 size=wx.DefaultSize, style=wx.HSCROLL | wx.VSCROLL,
 name="scrolledWindow")
}}}
所有的这些属性的行为都如你所愿,尽管size属性是它的父亲中的面板的物理尺寸,而非滚动窗口的逻辑尺寸。


'''指定滚动区域的尺寸'''

有几个自动指定滚动区域尺寸的方法。手工指定最多的方法如例8.1所示,使用了方法SetScrollBars:
{{{#!python
SetScrollbars(pixelsPerUnitX, pixelsPerUnitY, noUnitsX, noUnitsY,
 xPos=0, yPos=0, noRefresh=False)
}}}
其中关键的概念是滚动单位,它是滚动条的一次移动所引起的窗口中的转移距离。前面的两个参数pixelsPerUnitX和PixelsPerUnitY使你能够在两个方向设置滚动单位的大小。接下来的两个参数noUnitsX和noUnitsY使你能够按滚动单位设置滚动区域的尺寸。换句话说,滚动区域的象素尺寸是(pixelsPerUnitX* noUnitsX, pixelsPerUnitY * noUnitsY)。例8.7通过将滚动单位设为1像素而避免了可能造成的混淆。参数xPos和yPos以滚动单位(非像素)为单位,它设置滚动条的初始位置,如果参数noRefresh为true,那么就阻止了在因SetScrollbars()的调用而引起的滚动后的窗口的自动刷新。

还有另外的三个方法,你可以用来设置滚动区域的尺寸,然后单独设置滚动率。你可能发现这些方法更容易使用,因为它们使你能够更直接地指定尺寸。你可以如下以像素为单位使用滚动窗口的SetVirtualSize()方法来直接设置尺寸。
{{{#!python
self.scroll.SetVirtualSize((600, 400))
}}}
使用方法FitInside(),你可以在滚动区域中设置窗口部件,以便滚动窗口绑定它们。这个方法设置滚动窗口的边界,以使滚动窗口刚好适合其中的所有子窗口:
{{{#!python
self.scroll.FitInside()
}}}
通常使用FitInside()的情况是,当在滚动窗口中正好有一个窗口部件(如文本域),并且该窗口部件的逻辑尺寸已被设置。如果我们在例8.7中使用了FitInside(),那么一个更小的滚动区域将被创建,因为该区域将正好匹配右下按钮的边缘,而没有多余的内边距。

最后,如果滚动窗口中有一个sizer设置,那么使用SetSizer()设置滚动区域为sizer所管理的窗口部件的尺寸。这是在一个复杂的布局中最常用的机制。关于sizer的更多细节参见第11章。

对于上述所有三种机制,滚动率需要去使用SetScrollRate()方法单独设置,如下所示:
{{{#!python
self.scroll.SetScrollRate(1, 1)
}}}
参数分别是x和y方向的滚动单位尺寸。大于0的尺寸都是有效的。


'''滚动条事件'''

在例8.7中的按钮事件处理器,使用Scroll()方法程序化地改变滚动条的位置。这个方法需要滚动窗口的x和y坐标,使用的是滚动单位。

在第7章中,我们答应了你可以捕获的来自滚动条的事件列表,因为它们也被用来去控制滑块。表8.7列出了所有被滚动窗口内在处理的滚动事件。通常,许多你不会用到,除非你建造自定义窗口部件。


'''表8.7 滚动条的事件'''

||EVT_SCROLL||当任何滚动事件被触发时发生。||
||EVT_SCROLL_BOTTOM||当用户移动滚动条到它的范围的最末端时触发(底边或右边,依赖于方向)。||
||EVT_SCROLL_ENDSCROLL||在微软的Windows中,任何滚动会话的结束都将触发该事件,不管是因鼠标拖动或按键按下。||
||EVT_SCROLL_LINEDOWN||当用户向下滚动一行时触发。||
||EVT_SCROLL_LINEUP||当用户向上滚动一行时触发。||
||EVT_SCROLL_PAGEDOWN||当用户向下滚动一页时触发。||
||EVT_SCROLL_PAGEUP||当用户向上滚动一页时触发。||
||EVT_SCROLL_THUMBRELEASE||用户使用鼠标拖动滚动条滚动不超过一页的范围,并释放鼠标后,触发该事件。||
||EVT_SCROLL_THUMBTRACK||滚动条在一页内被拖动时不断的触发。||
||EVT_SCROLL_TOP||当用户移动滚动条到它的范围的最始端时触发,可能是顶端或左边,依赖于方向而定。||

行和页的准确定义依赖于你所设定的滚动单位,一行是一个滚动单位,一页是滚动窗口中可见部分的全部滚动单位的数量。对于表中所列出的每个EVT_SCROLL*事件,都有一个相应的EVT_SCROLLWIN*事件(它们由wx.ScrolledWindow产生)来回应。

还有一个wxPython的特殊的滚动窗口子类:wx.lib.scrolledpanel.ScrolledPanel,它使得你能够在面板上自动地设置滚动,该面板使用一个sizer来管理子窗口部件的布局。wx.lib.scrolledpanel.ScrolledPanel增加的好处是,它让用户能够使用tab键来在子窗口部件间切换。面板自动滚动,使新获得焦点的窗口部件进入视野。要使用wx.lib.scrolledpanel.ScrolledPanel,就要像一个滚动窗口一样声明它,然后,在所有的子窗口被添加后,调用下面的方法:
{{{#!python
SetupScrolling(self, scroll_x=True, scroll_y=True, rate_x=20,
        rate_y=20)
}}}
rate_x和rate_y是窗口的滚动单位,该类自动根据sizer所计算的子窗口部件的尺寸设定虚拟尺寸(virtual size)。
记住,当确定滚动窗口中的窗口部件的位置的时候,该位置总是窗口部件的物理位置,它相对于显示器中的滚动窗口的实际原点,而非窗口部件相对于显示器虚拟尺寸(virtual size)的逻辑位置。这始终是成立的,即使窗口部件不再可见。例如,在敲击了图8.5中的Scroll Me按钮后,该按钮所报告的它的位置是(-277,-237)。如果这不的你所想要的,那么使用CalcScrolledPosition(x,y)和。CalcUnscrolledPosition(x, y)方法在显示器坐标和逻辑坐标之间切换。在这两种情况中,在按钮敲击并使滚动条移动到底部后,你传递指针的坐标,并且滚动窗口返回一个(x,y)元组,如下所示:
{{{#!python
CalcUnscrolledPostion(-277, -237) #
}}}
  
    app.MainLoop()
    dialog = SubclassDialog()
    result = dialog.ShowModal()#显示模式对话框
    if result == wx.ID_OK:
        print "OK"
    else:
        print "Cancel"
    dialog.Destroy()
}}}

与前一章的wx.Frame的例子比较,这儿有两个需要注意的事情。在__init__方法中,按钮是被直接添加到wx.Dialog,而非wx.Panel。面板在对话框中的使用比在框架中少的多,部分原因是因为对话框与框架相比倾向简单化,但主要是因为wx.Panel特性(标准系统背景和tab键横向切换控件焦点)已经默认存在于wx.Dialog中。

要显示为模式对话框,使用ShowModal()方法。这与用于框架的的Show()方法在对程序的执行上有不同的作用。在调用ShowModal()后你的应用程序将处于等待中,直到对话框被取消。

模式将保持到对话框方法EndModal(retCode)被调用,该方法关闭对话框。参数retCode是由ShowModal()方法返回的一个整数值。典型的,应用程序利用这个返回值来知道用户是如何关闭对话框的,以控制以后的操作。但是结束这个模式并没有销毁或甚至关闭对话框。保持对话框的存在可能是一件好事,因为这意味你可以把用户选择的信息存储为对话框实例的数据成员,并且即使在对话框被关闭后也能从对话框重新获得那些信息。在接下来的部分,我们将看一些我们使用对话框处理程序中用户输入的数据的例子。

由于例9.1中没有定义事件处理器,你可能会惊奇对话框是如何响应按钮敲击的。这个行为已经定义在wxDialog中了。有两个预定义的wxPython ID号,它们在对话框中有特殊的意思。当对话框中的一个使用wx.ID_OK ID的wx.Button被敲击时,模式就结束了,对话框也关闭了,wx.ID_OK就是ShowModal()调用返回的值。同样,一个使用wx.ID_CANCEL ID的按钮做相同的事情,但是ShowModal()的返回值是wx.ID_CANCEL。

例9.1显示了处理模式对话框的一个典型的方法。在对话框被调用后,返回值被用作if语句中的测试。在这种情况下,我们简单地打印结果,在更复杂的例子中,wx.ID_OK将执行用户在对话框中所要求的动作,如打开文件或选择颜色。

典型的,你在完成对对话框的使用后,你应该显式地销毁它。这通知C++对象它应该自我销毁,然后这将使得它的Python部分被作为垃圾回收。如果你希望在你的应用程序中,以后再次使用该对话框时不重建它,以加速对话框的响应时间,那么你可以保持对该对话框的一个引用,并当你需要再次激活它时,简单地调用它的ShowModal()方法。当应用程序准备退出时,确保已销毁了它,否则MainLoop()将仍将它作为一个存在的顶级窗口,并且程序将不能正常退出。
Line 511: Line 61:
== 可选的框架类型 ==


框架不限于其中带有窗口部件的普通的矩形,它可以呈现其它的形状。你也可以创建MDI(多文档界面)框架,它其中包含别的框架。或者你也可以去掉框架的标题栏,并且仍然可以使用户能拖动框架。
=== 如何创建一个警告框? ===


经由一个对话框与用户交互的最简单的三个办法分别是:wx.MessageDialog,它是一个警告框、wx.TextEntryDialog,它提示用户去输入一些短的文本、wx.SingleChoiceDialog,它使用户能够从一个有效选项列表中进行选择。在接下来的三个小节中,我们将论这些简单的对话框。

消息对话框显示一个短的消息,并使用户通过按下按钮来作响应。通常,消息框被用作去显示重要的警告、yes/no问题、或询问用户是否继续某种操作。图9.2显示了一个典型的消息框。


'''图9.2'''

attachment:w9.2.gif


使用消息框是十分的简单。例9.2显示了创建一个消息框的两种办法。


'''例9.2'''
'''创建一个消息框'''
{{{#!python
import wx

if __name__ == "__main__":
    app = wx.PySimpleApp()

# 方法一,使用类
    dlg = wx.MessageDialog(None, "Is this explanation OK?",
                          'A Message Box',
                          wx.YES_NO | wx.ICON_QUESTION)
    retCode = dlg.ShowModal()
    if (retCode == wx.ID_YES):
        print "yes"
    else:
        print "no"
    dlg.Destroy()
}}}

#1 方法二,使用函数
    retCode = wx.MessageBox("Is this way easier?", "Via Function",
            wx.YES_NO | wx.ICON_QUESTION)

例9.2创建了两个消息框,一个在另一个的后面。这第一个方法是创建类wx.MessageDialog的一个实例,并使用ShowModal()来显示它。


'''使用wx.MessageDialog类'''

使用wx.MessageDialog的构造函数,你可以设置对话框的消息和按钮,构造函数如下:

wx.MessageDialog(parent, message, caption="Message box",
    style=wx.OK | wx.CANCEL, pos=wx.DefaultPosition)

message参数是实际显示在对话框中的文本。如果消息字符串包含\n字符,那么文本将在此换行。caption参数显示在对话框的标题栏中。pos参数使你可以指定对话框显示在屏幕上的位置——在微软Windows下,这个参数将被忽略。

wx.MessageDialog的样式标记分为两类。第一类控制显示在对话框中的按钮。表9.1说明了这些样式。


'''表9.1 wx.MessageDialog的按钮样式'''

||wx.CANCEL||包括一个cancel(取消)按钮。这个按钮有一个ID值wx.ID_CANCEL。||
||wx.NO_DEFAULT||在一个wx.YES_NO对话框中,No(否)按钮是默认的。||
||wx.OK||包括一个OK按钮,这个按钮有一个ID值wx.ID_OK。||
||wx.YES_DEFAULT||在一个wx.YES_NO对话框中,Yes按钮是默认的。这是默认行为。||
||wx.YES_NO||包括Yes和No按钮,各自的ID值分别是wx.ID_YES和wx.ID_NO。||

第二套样式标记控制紧挨着消息文本的图标。它们显示在表9.2中。


'''表9.2 wx.MessageDialog的图标样式'''

||wx.ICON_ERROR||表示一个错误的图标。||
||wx.ICON_EXCLAMATION||表示警告的图标。||
||wx.ICON_HAND||同wx.ICON_ERROR。||
||wx.ICON_INFORMATION||信息图标,字母i。||
||wx.ICON_QUESTION||问号图标。||

最后,你可以使用样式wx.STAY_ON_TOP将对话框显示在系统中任何其它窗口的上面,包括系统窗口和wxPython应用程序窗口。

你在例9.2所见到的,对话框通过使用ShowModal()被调用。根据所显示的按钮,返回的结果是以下值之一:wx.ID_OK, wx.ID_CANCEL,wx.ID_YES, 或 wx.ID_NO。如同其它对话框的情况,你通常使用这些值来控制程序的执行。


'''使用wx.MessageBox()函数'''

例9.2中的#1显示了一个调用消息框的更简短的方法。这个便利的函数wx.MessageBox()创建对话框,调用ShowModal(),并且返回下列值之一:wx.YES, wx.NO, wx.CANCEL, 或 wx.OK。函数的形式比MessageDialog的构造函数更简单,如下所示:
{{{#!python
wx.MessageBox(message, caption="Message", style=wx.OK)
}}}
在这个例子中,参数message, caption, style的意思和构造函数中的相同,你可以使用所有相同的样式标记。正如我们贯穿本章将看到的,在wxPython预定义的几个对话框都有便利的函数。在你为单一的使用创建对话框的时候,你的选择有一个优先的问题。如果你计划束缚住对话框以便多次调用它,那么你可能会优先选择去实例化对象以便你能够束缚该引用,而不使用函数的方法,尽管这对于这些简单的对话框来说,所节约的时间可以忽略不计。

要在你的消息框中显示大量的文本(例如,终端用户许可证的显示),你可以使用wxPython特定的类wx.lib.dialogs.ScrolledMessageDialog,它包含如下的构造函数:
{{{#!python
wx.lib.dialogs.ScrolledMessageDialog(parent, msg, caption,
        pos=wx.wxDefaultPosition, size=(500,300))
}}}
这个对话框不使用本地消息框控件,它根据别的wxPython窗口部件来创建一个对话框。它只显示一个OK按钮,并且没有更多的样式信息。

                   
=== 如何从用户得到短的文本? ===


这第二个简单类型的对话框是wx.TextEntryDialog,它被用于从用户那里得到短的文本输入。它通常用在在程序的开始时要求用户名或密码的时候,或作为一个数据输入表单的基本替代物。图9.3显示了一个典型的文本对话框。


'''图9.3'''
'''文本输入标准对话框'''

attachment:w9.3.gif


例9.3显示了产生图9.3的代码


'''例9.3'''
{{{#!python
import wx

if __name__ == "__main__":
    app = wx.PySimpleApp()
    dialog = wx.TextEntryDialog(None,
            "What kind of text would you like to enter?",
            "Text Entry", "Default Value", style=wx.OK|wx.CANCEL)
    if dialog.ShowModal() == wx.ID_OK:
        print "You entered: %s" % dialog.GetValue()

    dialog.Destroy()
}}}
在前一小节,我们创建了一个对话框类的实例,在这里,我们要用到的对话框类是wx.TextEntryDialog。该类的构造函数比简单消息对话框要复杂一些:
{{{#!python
wx.TextEntryDialog(parent, message, caption="Please enter text",
    defaultValue="", style=wx.OK | wx.CANCEL | wx.CENTRE,
    pos=wx.DefaultPosition)
}}}
message参数是显示在对话框中的文本提示,而caption显示在标题栏中。defaultValue显示在文本框中的默认值。style可以包括wx.OK和wx.CANCEL,它显示适当的按钮。

几个wx.TextCtrl的样式也可以用在这里。最有用的应该是wx.TE_PASSWORD,它掩饰所输入的真实密码。你也可以使用wx.TE_MULTILINE来使用户能够在对话框中输入多行文本,也可以使用wx.TE_LEFT, wx.TE_CENTRE, 和 wx.TE_RIGHT来调整所输入的文本的对齐位置。

例9.3的最后显示了在文本框和对话框之间的另一区别。用户所输入的信息被存储在该对话框实例中,并且以后必须应用程序获取。在这种情况下,你可以使用对话框的GetValue()方法来得到该值。记住,如果用户按下Cancel(取消)去退出该对话框,这意味他们不想去使用他所键入的值。你也可以在程序中使用SetValue()方法来设置该值。

下面这些是使用文本对话框的便利函数:

1、wx.GetTextFromUser()
2、wx.GetPasswordFromUser()
3、wx.GetNumberFromUser()

其中和例9.3的用处最近似的是wx.GetTextFromUser():
{{{#!python
wx.GetTextFromUser(message, caption="Input text",
    default_value="", parent=None)
}}}
这里的message, caption, default_value, 和 parent与wx.TextEntryDialog的构造函数中的一样。如果用户按下OK,该函数的返回值是用户所输入的字符串。如果用户按下Cancel,该函数返回空字符串。

如果你希望用户输入密码,你可以使用wx.GetPasswordFromUser()函数:
{{{#!python
wx.GetPasswordFromUser(message, caption="Input text",
    default_value="", parent=None)
}}}
这里的参数意义和前面的一样。用户的输入被显示为星号,如果用户按下OK,该函数的返回值是用户所输入的字符串。如果用户按下Cancel,该函数返回空字符串。

最后,你可以使用wx.GetNumberFromUser()要求用户输入一个数字:
{{{#!python
wx.GetNumberFromUser(message, prompt, caption, value, min=0,
    max=100, parent=None)
}}}
这里的参数的意义有一点不同,message是显在prompt上部的任意长度的消息,value参数是默认显示在文本框中的长整型值。min和max参数为用户的输入限定一个范围。如果用户按下OK按钮退出的话,该方法返回所输入的值,并转换为长整型。如果这个值不能转换为一个数字,或不在指定的范围内,那么该函数返回-1,这意味如果你将该函数用于负数的范围的话,你可能要考虑一个转换的方法。
Line 517: Line 225:
=== 如何创建一个MDI框架? ===


还记得MDI吗?许多人都不记得了。MDI是微软90年代初的创新,它使得一个应用程序中的多个子窗口能被一个单一的父窗口控制,本质上为每个应用程序提供了一个独立的桌面。在大多数应用程序中,MDI要求应用程序中的所有窗口同时最小化,并保持相同的z轴次序(相对系统中的其它部分)。我们建议仅当用户期望同时看到所有的应用程序窗口的情况下使用MDI,例如一个游戏。图8.7显示了一个典型的MDI环境。

在wxPython中MDI是被支持的,在Windows操作系统下通过使用本地窗口部件来实现MDI,在其它的操作系统中通过模拟子窗口实现MDI。例8.8提供了一简单的MDI的例子。


'''图8.7'''

attachment:w8.7.gif



'''例8.8'''
'''如何创建一个MDI窗口'''
{{{#!python
import wx

class MDIFrame(wx.MDIParentFrame):
    def __init__(self):
        wx.MDIParentFrame.__init__(self, None, -1, "MDI Parent",
                size=(600,400))
        menu = wx.Menu()
        menu.Append(5000, " Window")
        menu.Append(5001, "E ")
        menubar = wx.MenuBar()
        menubar.Append(menu, " ")
        self.SetMenuBar(menubar)
        self.Bind(wx.EVT_MENU, self.OnNewWindow, id=5000)
        self.Bind(wx.EVT_MENU, self.OnExit, id=5001)

    def OnExit(self, evt):
        self.Close(True)

    def OnNewWindow(self, evt):
        win = wx.MDIChildFrame(self, -1, "Child Window")
        win.Show(True)

if __name__ == '__main__':
=== 如何用对话框显示选项列表? ===


如果给你的用户一个空的文本输入域显得太自由了,那么你可以使用wx.SingleChoiceDialog来让他们在一组选项中作单一的选择。图9.4显示了一个例子。


'''图9.4'''
'''一个单选对话框'''

attachment:w9.4.gif


例9.4显示了产生图9.4的代码


'''例9.4'''
'''显示一个选择列表对话框'''
{{{#!python
import wx

if __name__ == "__main__":
Line 558: Line 247:
    frame = MDIFrame()
    frame.Show()
    app.MainLoop()
}}}
MDI的基本概念是十分简单的。父窗口是wx.MDIParentFrame的一个子类,子窗口如同任何其它的wxPython窗口部件一样被添加,除了它们是wx.MDIChildFrame的子类。wx.MDIParentFrame的构造函数与wx.Frame的基本相同,如下所示:
{{{#!python
wx.MDIParentFrame(parent, id, title, pos = wx.DefaultPosition,
        size=wxDefaultSize,
        style=wx.DEFAULT_FRAME_STYLE | wx.VSCROLL | wx.HSCROLL,
        name="frame")
}}}
不同的一点是wx.MDIParentFrame在默认情况下有滚动条。wx.MDIChildFrame的构造函数是相同的,除了它没有滚动条。如例8.8所示,添加一个子框架是通过创建一个以父框架为父亲的框架来实现的。

你可以通过使用父框架的Cascade()或Tile()方法来同时改变所有子框架的位置和尺寸,它们模拟相同名字的菜单项。调用Cascade(),导致一个窗口显示在其它的上面,如图8.7的所示,而Tile()使每个窗口有相同的尺寸并移动它们以使它们不重叠。要以编程的方式在子窗口中移动焦点,要使用父亲的方法ActivateNext()和ActivatePrevious()
    choices = ["Alpha", "Baker", "Charlie", "Delta"]
    dialog = wx.SingleChoiceDialog(None, "Pick A Word", "Choices",
            choices)
    if dialog.ShowModal() == wx.ID_OK:
        print "You selected: %s\n" % dialog.GetStringSelection()

    dialog.Destroy()
    
wx.SingleChoiceDialog的构造函数如下所示:

wx.SingleChoiceDialog(parent, message, caption, choices,
        clientData=None, style=wx.OK | wx.CANCEL | wx.CENTRE,
        pos=wx.DefaultPosition)
}}}
message和caption参数的意义与前面的一样,分别显示在对话框和标题栏中。choices参数要求一个字符串的列表,它们是你呈现在对话框中的选项。style参数有三个项,这是默认的,分别是OK按钮、Cancle按钮和使对话框在屏幕中居中。centre选项和pos参数在Windows操作系统上不工作。

如果你想在用户看见对话框之前,设置它的默认选项,使用SetSelection(selection)方法。参数selection是选项的索引值,而非实际选择的字符串。在用户选择了一个选项后,你即可以使用GetSelection()——它返回所选项的索引值,也可以使用GetStringSelection()——它返回实际所选的字符串,来得到它。

有两个用于单选对话框的便利函数。第一个是wx.GetSingleChoice,它返回用户所选的字符串:

wx.GetSingleChoice(message, caption, aChoices, parent=None)

参数message, caption, 和parent的意义和wx.SingleChoiceDialog构造函数的一样。aChoices参数是选项的列表。如果用户按下OK,则返回值是所选的字符串,如果用户按下Cancel,则返回值是空字符串。这意味如果空字符是一个有效的选择的话,那么你就不该使用这个函数。

第二个是wx.GetSingleChoiceIndex:
{{{#!python
wx.GetSingleChoiceIndex(message, caption, aChoices, parent=None)
}}}
这个函数与第一个有相同的参数,但是返回值不同。如果用户按下OK,则返回值是所选项的索引,如果用户按下Cancel,则返回值是-1。

                   
=== 如何显示进度条? ===


在许多程序中,程序需要自己做些事情而不受用户输入的干扰。这时就需要给用户一些可见的显示,以表明程序正在做一些事情及完成的进度。在wxPython中,这通常使用一个进度条来管理,如图9.5所示。


'''图9.5'''

attachment:w9.5.gif


例9.5显示了产生图9.5的代码


'''例9.5'''
'''生成一个进度条'''
{{{#!python
import wx

if __name__ == "__main__":
    app = wx.PySimpleApp()
    progressMax = 100
    dialog = wx.ProgressDialog("A progress box", "Time remaining", progressMax,
            style=wx.PD_CAN_ABORT | wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME)
    keepGoing = True
    count = 0
    while keepGoing and count progressMax:
        count = count + 1
        wx.Sleep(1)
        keepGoing = dialog.Update(count)

    dialog.Destroy()
}}}
进度条的所有选项在构造函数中被设置,构造函数如下:
{{{#!python
wx.ProgressDialog(title, message, maximum=100, parent=None,
        style=wx.PD_AUTO_HIDE | wx.PD_APP_MODAL)
}}}
这些参数不同于其它对话框的。参数title被放置在窗口的标题栏,message被显示在对话框中。maximum是你用来显示进度计数的最大值。

表9.3 列出了特定于wx.ProgressDialog六个样式,它们影响进度条的行为。


'''表9.3 wx.ProgressDialog的样式'''

||wx.PD_APP_MODAL||如果设置了这个样式,进度条对整个应用程序是模式的,这将阻塞所有的用户事件。如果没有设置这个样式,那么进度条仅对它的父窗口是模式的。||
||wx.PD_AUTO_HIDE||进度条将自动隐藏自身直到它达到它的最大值。||
||wx.PD_CAN_ABORT||在进度条上放上一个Cancel按钮,以便用户停止。如何响应来自该对话框的取消将在以后说明。||
||wx.PD_ELAPSED_TIME||显示该对话框已经出现了多长时间。||
||wx.PD_ESTIMATED_TIME||显示根据已花的时间、当前的计数值和计数器的最大值所估计出的完成进度所需的总时间。||
||wx.PD_REMAINING_TIME||显示要完成进度所估计的剩余时间,或(所需总时间-已花时间)。||

要使用进度条,就要调用它的唯一的方法Update(value,newmsg="")。value参数是进度条的新的内部的值,调用update将导致进度条根据新的计数值与最大计算值的比例重绘。如果使用可选的参数newmsg,那么进度条上的文本消息将变为该字符串。这让你可以给用户一个关于当前进度的文本描述。

这个Update()方法通常返回True。但是,如果用户通过Cancel按钮已经取消了该对话框,那么下次的Update()将返回False。这是你响应用户的取消请求的机会。要检测用户的取消请求,我们建议你尽可能频繁地Update()。

                   
== 使用标准对话框 ==


大多数操作系统都为像文件选择、字体选择和颜色选择这些任务提供了标准对话框。这为平台提供了一致感观。你也可以使用来自于wxPython的这些对话框,它们也为你的应用程序提供了一致的感观。如果你使用wxPython,那么它为你提供了类似的对话框,即使所在的平台没有提供系统对话框。
Line 574: Line 341:
=== 什么是小型框架,我们为何要用它? ===


小型框架是一个有两个例外的矩形框架:它有一个较小的标题区域,并且在微软的Windows下或GTK下,它不在任务栏中显示。图8.8显示了一个较小标题域的一个例子。


'''图8.8'''
'''一个小型框架'''

attachment:w8.8.gif


创建小型框架的代码基本上等同于创建一个矩形框架,唯一的不同是父类是wx.MiniFrame。例8.9显示了这个代码。


'''例8.9'''
'''创建一个小型框架'''
{{{#!python
import wx

class MiniFrame(wx.MiniFrame):
    def __init__(self):
        wx.MiniFrame.__init__(self, None, -1, 'Mini Frame',
                size=(300, 100))
        panel = wx.Panel(self, -1, size=(300, 100))
        button = wx.Button(panel, -1, "Close Me", pos=(15, 15))
        self.Bind(wx.EVT_BUTTON, self.OnCloseMe, button)
        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)

    def OnCloseMe(self, event):
        self.Close(True)

    def OnCloseWindow(self, event):
        self.Destroy()

if __name__ == '__main__':
=== 如何使用文件选择对话框? ===


在wxPython中,wx.FileDialog为主流的平台使用本地操作系统对话框,对其它操作系统使用非本地相似的外观。微软Windows的版本如图9.6所示。


'''图9.6'''

attachment:w9.6.gif


你可以设置文件对话框开始在任一目录,你也可以使用通配符过滤来限制去显示某种文件类型。例9.6显示了一个基本的例子。


'''例9.6'''
'''使用wx.FileDialog'''
{{{#!python
import wx
import os

if __name__ == "__main__":
Line 611: Line 363:
    MiniFrame().Show()
    app.MainLoop()
}}}
wx.MiniFrame的构造函数等同于wx.Frame的,然而,wx.MiniFrame支持额外的样式标记。如表8.8所示。


'''表8.8'''
'''wx.MiniFrame的样式标记'''

||wx.THICK_FRAME||在Windows或Motif下,使用粗边框绘制框架。||
||wx.TINY_CAPTION_HORIZONTAL||代替wx.CAPTION而显示一个较小的水平标题。||
||wx.TINY_CAPTION_VERTICAL||代替wx.CAPTION而显示一个较小的垂直标题。||

典型的,小型框架被用于工具框窗口中,在工具框窗口中始终是有效的,它们不影响任务栏。较小的标题使得它们更有效的利用空间,并且明显地区别于标准的框架。


                   
=== 如何创建一个非矩形的框架? ===


在大多数应用程序中,框架都是矩形,因为矩形有一个不错的规则的形状,并且绘制和维护相对简单。可是,有时候你需要打破直线的制约。在wxPython中,你可以给框架一个任一的形状。如果一个备用的形状被定义了,那么框架超出该形状的部分不将被绘制,并且不响应鼠标事件;对于用户而言,它们不是框架的一部分。图8.9显示了一个非矩形的窗口,显示的背景是文本编辑器中的代码。

事件被设置来以便双击时开关这个非标准形状,鼠标右键单击时关闭这个窗口。这个例子使用了来自wxPython demo的images模块作为vippi的图像的资源,wxPython的吉祥物。


'''图8.9'''

attachment:w8.9.gif


例8.10显示了在这个非矩形框架后面的代码。这个例子比我们见过的其它一些稍微精细点,以显示如何在缺少典型的窗口界面装饰的情况下管理像窗口关闭之类的事情。


'''例8.10'''
'''绘制符合形状的窗口'''
{{{#!python
import wx
import images

class ShapedFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "Shaped Window",
                style = wx.FRAME_SHAPED | wx.SIMPLE_BORDER |
                wx.FRAME_NO_TASKBAR)
        self.hasShape = False

#1 获取图像
        self.bmp = images.getVippiBitmap()
        self.SetClientSize((self.bmp.GetWidth(), self.bmp.GetHeight()))

#2 绘制图像
        dc = wx.ClientDC(self)
        dc.DrawBitmap(self.bmp, 0,0, True)

        self.SetWindowShape()
        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
        self.Bind(wx.EVT_RIGHT_UP, self.OnExit)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_WINDOW_Create, self.SetWindowShape)#3 绑定窗口创建事件

    def SetWindowShape(self, evt=None):#4 设置形状
        r = wx.RegionFromBitmap(self.bmp)
        self.hasShape = self.SetShape(r)

    def OnDoubleClick(self, evt):
        if self.hasShape:
            self.SetShape(wx.Region())#5 重置形状
            self.hasShape = False
        else:
            self.SetWindowShape()

    def OnPaint(self, evt):
        dc = wx.PaintDC(self)
        dc.DrawBitmap(self.bmp, 0,0, True)

    def OnExit(self, evt):
        self.Close()

if __name__ == '__main__':
    wildcard = "Python source (*.py)|*.py|" \
            "Compiled Python (*.pyc)|*.pyc|" \
            "All files (*.*)|*.*"
    dialog = wx.FileDialog(None, "Choose a file", os.getcwd(),
            "", wildcard, wx.OPEN)
    if dialog.ShowModal() == wx.ID_OK:
        print dialog.GetPath()

    dialog.Destroy()
}}}
文件对话框是我们这章已见过的最复杂的对话框,它有几个属性可以通过编程的方式读写。它的构造函数使得你能够设置它的一些属性:
{{{#!python
wx.FileDialog(parent, message="Choose a file", defaultDir="",
        defaultFile="", wildcard="*.*", style=0,
        pos=wx.DefaultPosition)
}}}
message参数出现在窗口的标题栏中。defaultDir参数告诉对话框初始的时候显示哪个目录。如果这个参数为空或表示的目录不存在,那么对话框开始在当前目录。defaultFile是默认保存为的文件。wildcard参数使你可以基于给定的模式来过滤列表,使用通常的*和?作为通配符。通配符可以是单个模式,如*.py或格式如 描述 | 模式 | 描述 | 模式 的一系列模式——类似于例9.6中所用。

"Python source (*.py)|*.py|Compiled Python (*.pyc)|*.pyc|
        All files (*.*)|*.*"

如果有一个多个项目的模式,那么它们显示在图9.6所示的下拉菜单中。pos参数不保证被基本的系统所支持。


'''选择一个文件'''

wx.FileDialog的两个最重要的样式标记是wx.OPEN和wx.SAVE,它们表明对话框的类型并影响对话框的行为。

用于打开文件的对话框有两个标记,它们进一步影响对话框的行为。wx.HIDE_READONLY标记灰化复选框,使用户以只读模式打开文件。wx.MULTIPLE标记使用户可以在一个目录中选择打开多个文件。

保存文件对话框有一个有用的标记wx.OVERWRITE_PROMPT,它使得保存文件时,如果有相同的文件存在,则提示用户是否覆盖。

两种文件对话框都可以使用wx.CHANGE_DIR标记。当使用这个标记时,文件的选择也可改变应用程序的工作目录为所选文件所在的目录。这使得下次文件对话框打开在相同的目录,而不需要应用程序再在别处存储该值。

和本章迄今为止我人们所见过的其它对话框不一样,文件对话框的属性directory,filename, style, message, 和wildcard是可以通过方法来得到和设置的。这些方法使用Get/Set命名习惯。

在用户退出对话框后,如果返回值是wx.OK,那么你可以使用方法GetPath()来得到用户的选择,该函数的返回值是字符串形式的文件全路径名。如果对话框是一个使用了wx.MULTIPLE标记的打开对话框,则用GetPaths()代替GetPath()。该方法返回路径字符串的一个Python列表。如果你需要知道在用户选择时使用了下拉菜单中的哪个项,你可以使用GetFilterIndex(),它返回项目的索引。要通过编程改变索引,使用方法SetFilterIndex()。

这后面的是一个使用文件对话框的便利函数:
{{{#!python
wx.FileSelector(message, default_path="", default_filename="",
    default_extension="", wildcard="*.*'', flags=0, parent=None,
    x=-1, y=-1)
}}}
message, default_path, default_filename, 和 wildcard参数意义与构造函数的基本相同,尽管参数的名字不同。flags参数通常被称作style,default_extension参数是保存为文件时默认的后缀(如果用户没有指定后缀的情况下)。如果用户按下OK,返回值是字符串形式的路径名,如果用户按下Cancel则返回一个空字符串。


'''选择一个目录'''

如果用户想去选择一个目录而非一个文件,使用wx.DirDialog,它呈现一个目录树的视图,如图9.7所示。

这个目录选择器比文件对话框简单些。例9.7显示了相关的代码。


'''例9.7'''
'''显示一个目录选择对话框'''
{{{#!python
import wx

if __name__ == "__main__":
Line 691: Line 424:
    ShapedFrame().Show()
    app.MainLoop()
}}}

'''#1''' 在从images模块得到图像后,我们将窗口内部的尺寸设置为位图的尺寸。你也可以根据一个标准的图像文件来创建这个wxPython位图,这将在第16章中作更详细的讨论。


'''#2''' 这里,我们在窗口中绘制这个图像。这决不是一个必然的选择。你可以像其它窗口一样在该形状窗口中放置窗口部件和文本(尽管它们必须在该形状的区域内)。


'''#3''' 这个事件在大多数平台上是多余的,它强制性地在窗口被创建后调用SetWindowShape()。但是,GTK的实现要求在该形状被设置以前,窗口的本地UI对象被创建和确定,因此当窗口创建发生时我们使用窗口创建事件去通知并在它的处理器中设置形状。


'''#4''' 我们使用全局方法wx.RegionFromBitmap去创建设置形状所需的wx.Region对象。这是创建不规则形状的最容易的方法。你也可以根据一个定义多边形的点的列表来创建一个wx.Region。图像的透明部分的用途是定义区域的边界。


'''#5''' 双击事件开关窗口的形状。要回到标准的矩形,要使用一个空的wx.Region作为参数来调用SetShape()。

除了没有标准的关闭框或标题栏等外,不规则形状框架的行为像一个普通的框架一样。任何框架都可以改变它的形状,因为SetShape()方法是wx.Frame类的一部分,它可以被任何子类继承。在wx.SplashScreen中,符合形状的框架是特别的有用。

                   
=== 如何拖动一个没有标题栏的框架? ===


前一个例子的明显结果是这个没有标题栏的框架不能被拖动的,这儿没有拖动窗口的标准方法。要解决这个问题,我们需要去添加事件处理器来在当拖动发生时移动该窗口。例8.11显示与前一例子相同形状的窗口,但增加了对于处理鼠标左键敲击和鼠标移动的一些事件。这个技术可以适用于任何其它的框架,甚至是框架内你想要移动的窗口(例如绘画程序中的元素)。


'''例8.11'''
'''使用户能够从框架来拖动框架的事件'''
{{{#!python
import wx
import images

class ShapedFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "Shaped Window",
                style = wx.FRAME_SHAPED | wx.SIMPLE_BORDER )
        self.hasShape = False
        self.delta = wx.Point(0,0)
        self.bmp = images.getVippiBitmap()
        self.SetClientSize((self.bmp.GetWidth(), self.bmp.GetHeight()))
        dc = wx.ClientDC(self)
        dc.DrawBitmap(self.bmp, 0,0, True)
        self.SetWindowShape()
        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)

#1 新事件
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_MOTION, self.OnMouseMove)

        self.Bind(wx.EVT_RIGHT_UP, self.OnExit)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_WINDOW_Create, self.SetWindowShape)

    def SetWindowShape(self, evt=None):
        r = wx.RegionFromBitmap(self.bmp)
        self.hasShape = self.SetShape(r)

    def OnDoubleClick(self, evt):
        if self.hasShape:
            self.SetShape(wx.Region())
            self.hasShape = False
        else:
            self.SetWindowShape()

    def OnPaint(self, evt):
        dc = wx.PaintDC(self)
        dc.DrawBitmap(self.bmp, 0,0, True)

    def OnExit(self, evt):
        self.Close()

    def OnLeftDown(self, evt):#2 鼠标按下
        self.CaptureMouse()
        pos = self.ClientToScreen(evt.GetPosition())
        oigin = self.GetPosition()
        self.delta = wx.Point(pos.x - oigin.x, pos.y - oigin.y)

    def OnMouseMove(self, evt):#3 鼠标移动
        if evt.Dragging() and evt.LeftIsDown():
            pos = self.ClientToScreen(evt.GetPosition())
            newPos = (pos.x - self.delta.x, pos.y - self.delta.y)
            self.Move(newPos)

    def OnLeftUp(self, evt):#4 鼠标释放
        if self.HasCapture():
            self.ReleaseMouse()



if __name__ == '__main__':
    app = wx.PySimpleApp()
    ShapedFrame().Show()
    app.MainLoop()
}}}

'''#1''' 我们为三个事件增加了相应的处理器,以作相应的工作。这三个事件是鼠标左键按下,鼠标左键释放和鼠标移动。


'''#2''' 拖动事件从鼠标左键按下开始。这个事件处理器做两件事。首先它捕获这个鼠标,直到鼠标被释放,以防止鼠标事件被改善到其它窗口部件。第二,它计算事件发生的位置和窗口左上角之间的偏移量,这个偏移量将被用来计算窗口的新位置。


'''#3''' 这个处理器当鼠标移动时被调用,它首先检查看该事件是否是一个鼠标左键按下,如果是,它使用这个新的位置和前面计算的偏移量来确定窗口的新位置,并移动窗口。


'''#4''' 当鼠标左键被释放时,ReleaseMouse()被调用,这使得鼠标事件又可以被发送到其它的窗口部件。

这个拖动技术可以被完善以适合其它的需要。例如,仅在一个定义的区域内鼠标敲击才开始一个拖动,你可以对鼠标按下事件的位置做一个测试,使敲击发生在右边的位置时,才能拖动。


  
                   
== 使用分割窗 ==


分割窗是一种特殊的容器窗口部件,它管理两个子窗口。这两个子窗口可以被水平的堆放或彼此左右相邻。在两个子窗口之间的是一个窗框,它是一个可移动的边框,移动它就改变了两个子窗口的尺寸。分割窗经常被用于主窗口的侧边栏。图8.10显示了一个分割窗的样板。

当你有两个信息面板并且想让用户自主决定每个面板的尺寸时,可以使用分割窗。Mac OS X Finder窗口就是一个分割窗的例子,并且许多的文本编辑器或制图软件都用它来维护一个打开的文件的列表。

 
=== 创建一个分割窗 ===


在wxPython中,分割窗是类wx.SplitterWindow的实例。和大多数其它的wxPython窗口部件不一样,分隔窗口在被创建后,可使用前要求进一步的初始化。它的构造函数是十分简单的。


'''图8.10'''

attachment:w8.10.gif

{{{#!python
wx.SplitterWindow(parent, id=-1, pos=wx.DefaultPosition,
        size=wx.DefaultSize, style=wx.SP_3D,
        name="splitterWindow")
}}}
它的这些参数都有标准的含义——parent是窗口部件的容器,pos是窗口部件在它的父容器中位置,size是它的尺寸。

在创建了这个分割窗后,在它可以被使用前,你必须对这个窗口调用三个方法中的一处。如果你想初始时只显示一个子窗口,那么调用Initialize(window),参数window是这个单一的子窗口(通常是一种wx.Panel)。在这种情况下,窗口将在以后响应用户的动作时再分割。

要显示两个子窗口,使用SplitHorizontally (window1,window2,sashPosition=0)或SplitVertically(window1, window2, sashPosition=0)。两个方法的工作都是相似的,参数window1和window2包含两个子窗口,参数sashPosition包含分割条的初始位置。对于水平分割(水平分割条)来说,window1被放置在window2的顶部。如果sashPosition是一个正数,它代表顶部窗口的初始高度(也就是分割条距顶部的像素值)。如果sashPosition是一个负数,它定义了底部窗口的尺寸,或分割条距底部的像素值。如果sashPosition是0,那么这个分割条位于正中。对于垂直分割(垂直分割条),window1位于左边,window2位于右边。正值的sashPosition设置window1的尺寸,也就是分割条距左边框的像素值。类似的,负值sashPosition设置右边子窗口的尺寸,0值将分割条放置在正中。如果你的子窗口复杂的话,我们建议你在布局中使用sizer,以便于当分割条被移动时很好地调整窗口的大小。

 
=== 一个分割窗的例子 ===


例8.12显示了如何创建有一个子窗口的分割窗并且在以后响应菜单项的分割。这个例子也使用了一些事件,这些事件我们以后讨论。注意,我们不计划在开始可见的子面板使用Hide()方法来隐藏。我们这样做是因为我们开始时不告诉分割窗去管理那个子面板的尺寸和位置,所以我们使用这种方法来隐藏它。如果我们在开始就要分割和显示这两个子面板,那么我们就不必考虑这些。


'''例8.12'''
'''如何创建你自己的分割窗'''
{{{#!python
import wx

class SplitterExampleFrame(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title=title)
        self.MakeMenuBar()
        self.minpane = 0
        self.initpos = 0
        self.sp = wx.SplitterWindow(self)# 创建一个分割窗
        self.p1 = wx.Panel(self.sp, style=wx.SUNKEN_BORDER)# 创建子面板
        self.p2 = wx.Panel(self.sp, style=wx.SUNKEN_BORDER)
        self.p1.SetBackgroundColour("pink")
        self.p2.SetBackgroundColour("sky blue")
        self.p1.Hide()# 确保备用的子面板被隐藏
        self.p2.Hide()

        self.sp.Initialize(self.p1)# 初始化分割窗
        
        self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING,
                  self.OnSashChanging, self.sp)
        self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED,
                  self.OnSashChanged, self.sp)


    def MakeMenuBar(self):
        menu = wx.Menu()
        item = menu.Append(-1, "Split horizontally")
        self.Bind(wx.EVT_MENU, self.OnSplitH, item)
        self.Bind(wx.EVT_Update_UI, self.OnCheckCanSplit, item)
        item = menu.Append(-1, "Split vertically")
        self.Bind(wx.EVT_MENU, self.OnSplitV, item)
        self.Bind(wx.EVT_Update_UI, self.OnCheckCanSplit, item)
        item = menu.Append(-1, "Unsplit")
        self.Bind(wx.EVT_MENU, self.OnUnsplit, item)
        self.Bind(wx.EVT_Update_UI, self.OnCheckCanUnsplit, item)

        menu.AppendSeparator()
        item = menu.Append(-1, "Set initial sash position")
        self.Bind(wx.EVT_MENU, self.OnSetPos, item)
        item = menu.Append(-1, "Set minimum pane size")
        self.Bind(wx.EVT_MENU, self.OnSetMin, item)
      
        menu.AppendSeparator()
        item = menu.Append(wx.ID_EXIT, "E ")
        self.Bind(wx.EVT_MENU, self.OnExit, item)

        mbar = wx.MenuBar()
        mbar.Append(menu, "Splitter")
        self.SetMenuBar(mbar)

        
    def OnSashChanging(self, evt):
        print "OnSashChanging:", evt.GetSashPosition()
    
    def OnSashChanged(self, evt):
        print "OnSashChanged:", evt.GetSashPosition()
    

    def OnSplitH(self, evt):# 响应水平分割请求
        self.sp.SplitHorizontally(self.p1, self.p2, self.initpos)
    
    def OnSplitV(self, evt):# 响应垂直分割请求
        self.sp.SplitVertically(self.p1, self.p2, self.initpos)

    def OnCheckCanSplit(self, evt):
        evt.Enable(not self.sp.IsSplit())

    def OnCheckCanUnsplit(self, evt):
        evt.Enable(self.sp.IsSplit())

    def OnUnsplit(self, evt):
        self.sp.Unsplit()
    
    def OnSetMin(self, evt):
        minpane = wx.GetNumberFromUser(
            "Enter the minimum pane size",
            "", "Minimum Pane Size", self.minpane,
            0, 1000, self)
        if minpane != -1:
            self.minpane = minpane
            self.sp.SetMinimumPaneSize(self.minpane)

    def OnSetPos(self, evt):
        initpos = wx.GetNumberFromUser(
            "Enter the initial sash position (to be used in the Split call)",
            "", "Initial Sash Position", self.initpos,
            -1000, 1000, self)
        if initpos != -1:
            self.initpos = initpos
    

    def OnExit(self, evt):
        self.Close()


app = wx.PySimpleApp(redirect=True)
frm = SplitterExampleFrame(None, "Splitter Example")
frm.SetSize((600,500))
frm.Show()
app.SetTopWindow(frm)
app.MainLoop()
}}}
分割窗只能分割一次,对已分割的窗口再分割将会失败,从而导致分割方法返回False(成功时返回True)。要确定窗口当前是否被分割了,调用方法IsSplit()。在例8.12中,为了确保相应的菜单项有效,就采用这个方法。

如果你想不分割窗口,那么使用Unsplit(toRemove=None)。参数toRemove是实际要移除的wx.Window对象,并且必须是这两个子窗口中的一个。如果toRemove是None,那么底部或右部的窗口将被移除,这根据分割的方向而定。默认情况下,被移除的窗口是没有被wxPython删除的,所以以后你可以再把它添加回来。unsplit方法在取消分割成功时返回True。如果分割窗当前没有被分割,或toRemove参数不是两个子窗口中的一个,那么该方法返回False。

要确保你对想要的子窗口有一个正确的引用,那么使用GetWindow1()和GetWindow2()方法。GetWindow1()方法返回顶部或左边的子窗口,而GetWindow2()方法返回底部或右边的窗口。由于没有一个直接的设置方法来改变一个子窗口,所以使用方法ReplaceWindow(winOld, winNew),winOld是你要替换的wx.Window对象,winNew是要显示的新窗口。


  
                   
=== 改变分割的外观 ===


有许多样式标记用来控制显示在屏幕上的分割窗的外观。注意,由于分割与平台有关,所以不是所有列出的标记都将对任何平台起作用。表8.9说明了这些有效的标记。

我们将在接下来的部分看到,你也可以用你的程序来改变分割的显示,以响应用户的动作或你自己的要求。


'''表8.9'''
'''分割窗的样式'''

||wx.SP_3D||绘制三维的边框和分割条。这是一个默认样式。||
||wx.SP_3DBORDER||绘制三维样式的边框,不包括分割条。||
||wx.SP_3DSASH||绘制三维样式的分割条,不包括边框。||
||wx.SP_BORDER||绘制窗口的边框,非三维的样式。||
||wx.SP_LIVE_Update||改变响应分割条移动的默认行为。如果没有设置这个标记,那么当用户拖动分割条时,将绘制一条线来标明分割条的新位置。子窗口的尺寸没有被实际地更新,直到完成分割条拖放。如果设置了这个标记,那么当分割条在被拖动时,子窗口的尺寸将不断地变化。||
||wx.SP_NOBORDER||不绘制任何边框。||
||wx.SP_NO_XP_THEME||在Windows XP系统下,分割条不使用XP的主题样式,它给窗口一个更经典的外观。||
||wx.SP_PERMIT_UNSPLIT||如果设置了这个样式,那么窗口始终不被分割。如果不设置,你可以通过设置大于0的最小化的窗格尺寸来防止窗口被分割。||

 
=== 以程序的方式处理分割 ===


一旦分割窗被创建,你就可以使用窗口的方法来处理分割条的位置。特别是,你可以使用方法SetSashPosition(position,redraw=True)来移动分割条。position是以像素单位的新的位置,它是分割条距窗口顶部或左边的距离。用在分割方法中的负值,表示位置从底部或右边算起。如果redraw为True,则窗口立即更新。否则它等待常规窗口的刷新。如果你的像素值在范围外的话,设置方法的行为将不被定义。要得到当前分割条的位置,使用GetSashPosition()方法。

在默认的分割行为下,用户可以在两个边框间随意移到分割条。移动分割条到一边,使得别一子窗口的尺寸为0,这导致窗口此时成未分割状态。要防止这种情况,你可以使用方法SetMinimumPaneSize(paneSize)来指定子窗口的最小尺寸。paneSize参数是子窗口的最小像素尺寸。这样,用户就不能通过拖放来使子窗口更小,程序同样也不能使子窗口更小。如前所述,你可以使用wx.SP_PERMIT_UNSPLIT样式来达到相同的效果。要得到当前最小子窗口尺寸,使用方法GetMinimumPaneSize()。

改变窗口的分割模式,使用方法SetSplitMode(mode),参数mode取下列常量值之一:wx.SPLIT_VERTICAL、wx.SPLIT_HORIZONTAL。如果模式改变了,那么顶部窗口变为左边,而底部变为右边(反之亦然)。该方法不引起窗口的重绘,你必须显式地进行强制重绘。你可以使用GetSplitMode()来得到当前的分割模式,它返回上面两个常量值之一。如果窗口当前是未分割状态,那么GetSplitMode()方法返回最近的分割模式。

典型的,如果wx.SP_LIVE_Update样式没有被设置,那么子窗口仅在分割条拖动会话结束时改变尺寸。如果你想在其它时刻强制子窗口重绘,你可以使用方法UpdateSize()。

 
=== 响应分割事件 ===


分割窗触发wx.SplitterEvent类事件。这儿有四个不同的分割窗的事件类型,如表8.10所示。


'''表8.10 分割窗的事件类型'''

||EVT_SPLITTER_DCLICK||当分割条被双击时触发。捕捉这个事件不阻塞标准的不分割行为,除非你调用事件的Veto()方法。||
||EVT_SPLITTER_SASH_POS_CHANGED||分割条的改变结束后触发,但在此之前,改变将在屏幕上显示(因此你可以再作用于它)。这个事件可以使用Veto()来中断。||
||EVT_SPLITTER_SASH_POS_CHANGING||当分割条在被拖动时,不断触发该事件。这个事件可以通过使用事件的Veto()方法来中断,如果被中断,那么分割条的位置不被改变。||
||EVT_SPLITTER_UNSPLIT||变成未分割状态时触发。||

这个分割事件类是wx.CommandEvent的子类。从分割事件的实例,你可以访问关于分割窗当前状态的信息。对于涉及到分割条移动的两个事件,调用GetSashPosition()得到分割条相对于左或顶部的位置,这依据分割条的方向而定。在位置正在变化事件中,调用SetSashPosition(pos),将用线条表示新的位置。在位置已改变事件中,SetSashPosition(pos)方法将移动分割条。对于双击事件,你可以使用事件的GetX() 和GetY()方法得到敲击的确切位置。对于未分割事件,你可以使用GetWindowBeingRemoved()方法来得到哪个窗口被移除了。
    dialog = wx.DirDialog(None, "Choose a directory:",
          style=wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
    if dialog.ShowModal() == wx.ID_OK:
        print dialog.GetPath()
    dialog.Destroy()
}}}

'''图9.7'''

attachment:w9.7.gif


这个对话框的所有的功能几乎都在构造函数中:
{{{#!python
wx.DirDialog(parent, message="Choose a directory", defaultPath="",
    style=0, pos = wx.DefaultPosition, size = wx.DefaultSize,
    name="wxDirCtrl")
}}}
由于message参数显示在对话框中,所以你不需要一个钩子去改变标题栏。defaultPath告诉对话框选择的默认路径,如果它为空,那么对话框显示文件系统的根目录。pos和size参数在微软Windows下被忽略,name参数在所有的操作系统下都被忽略。该对话框的样式标记wx.DD_NEW_DIR_BUTTON给对话框一个用于创建目录的一个按钮。这个标记在老版的微软Windows中不工作。

wx.DirDialog类的path, message, 和style属性都有相应的get*和set*方法。你可以使用GetPath()方法来在对话框被调用后获取用户的选择。这个对话框也有一个便利的函数:
{{{#!python
wx.DirSelector(message=wx.DirSelectorPromptStr, default_path="",
    style=0, pos=wxDefaultPosition, parent=None)
}}}
所有的参数和前面的构造函数相同。如果OK被按下,则该函数返回所选择的字符串形式的目录名,如果按下Cancel,则返回空字符串。
Line 1005: Line 453:
=== 如何使用字体选择对话框? ===


在wxPython中,字体选择对话框与文件对话框是不同的,因为它使用了一个单独的帮助类来管理它所呈现的信息。图9.8显示了微软Windows版的字体对话框。

例9.8 显示了产生图9.8的代码,并且与前面的对话框例子看起来也有些不同。


'''例9.8'''
'''字体对话框'''
{{{#!python
import wx

if __name__ == "__main__":
    app = wx.PySimpleApp()
    dialog = wx.FontDialog(None, wx.FontData())
    if dialog.ShowModal() == wx.ID_OK:
        data = dialog.GetFontData()
        font = data.GetChosenFont()
        colour = data.GetColour()
        print 'You selected: "%s", %d points\n' % (
                font.GetFaceName(), font.GetPointSize())
    dialog.Destroy()

}}}
'''图9.8'''

attachment:w9.8.gif


wx.FontDialog 的构造函数比前面的那些简单的多:

wx.FontDialog(parent, data)

你不能为该对话框设置一个消息或标题,并且被通常作为样式标记传递的信息被包含在data参数中,该参数是类wx.FontData。wx.FontData类自己只有一个有用的方法:GetFontData(),该方法返回字体数据的实例。

wx.FontData的实例使你能够设置管理字体对话框显示的值,并且也能够容纳用户输入的信息。例如,在例9.8中的代码调用了wx.FontData实例的两个get*方法来确定所选字体的细节。wx.FontData的构造函数没有参数——所有的属性必须通过使用表9.4中的方法来设置。


'''表9.4'''
'''wx.FontData的方法'''

||GetAllowSymbols(),SetAllowSymbols(allowSymbols)||决定是否在对话框中仅显示符号字体(如dingbats)。参数是布尔值。只在Windows中有意义。该属性的初始值是True。||
||GetChosenFont(),SetChosenFont(font)||以wx.Font对象的方式返回用户所选的字体。如果用户选择了取消,那么返回None。wx.Font类将在第12章作更详细的讨论。||
||GetColour(),SetColour(colour)||返回在对话框的颜色选择部分所选的颜色。set*方法使你可以预先设定默认值。get*方法返回一个wx.Colour实例。set*方法中的colour只能是一个wx.Colour或一个颜色的字符串名。该属性的初始值是black。||
||GetEnableEffects(),EnableEffects(enable)||在该对话框的Windows版本中,该属性控制是否显示字体的所选颜色、中间是否有直线通过、是否带下划线等特性。||
||GetInitialFont(),SetInitialFont(font)||返回对话框初值的字体值(即当前所用的字体)。这个属性可以在对话框显示之前通过应用程序显式的来设置。它的初始值是None。||
||SetRange(min, max)||设置字体尺寸(磅)的有效范围。仅用于微软的Windows系统。最初值是0~0,意味没有范围的限制。||
||GetShowHelp(),SetShowHelp()||如果为True,那么该对话框的微软Windows版本将显示一个帮助按钮。初始值为False||。

有一个使用字体对话框的便利的函数,它回避了wx.FontData类:
{{{#!python
wx.GetFontFromUser(parent, fontInit)
}}}
fontInit参数是wx.Font的一个实例,它用作对话框的初始值。该函数的返回值是一个wx.Font实例。如用户通过OK关闭了对话框,则方法wx.Font.Ok()返回True,否则返回False。


                   
=== 如何使用颜色对话框? ===


颜色对话框类似于字体对话框,因为它使用了一个外部的数据类来管理它的信息。图9.9显示了这类对话框的微软版本。

例9.9显示了生成该对话框的代码,它几乎与前面的字体对话框相同。


'''例9.9'''
{{{#!python
import wx

if __name__ == "__main__":
    app = wx.PySimpleApp()
    dialog = wx.ColourDialog(None)
    dialog.GetColourData().SetChooseFull(True)
    if dialog.ShowModal() == wx.ID_OK:
        data = dialog.GetColourData()
        print 'You selected: %s\n' % str(data.GetColour().Get())
    dialog.Destroy()
}}}

'''图9.9'''

attachment:w9.9.gif


用于颜色选择器的wxPython的类是wx.ColourDialog。它的构造函数很简单,没有太多的参数:

wx.ColourDialog(parent, data=None)

data参数是类wx.ColourData的实例,它比相应字体的更简单。它只包含默认的没有参数的构造函数和后面的三个属性:

1、GetChooseFull/SetChooseFull(flag):仅在微软Windows下工作。当设置后,将显示完整的对话框,包括自定义颜色选择器。如果不设置,则自定义颜色选择器不被显示。

2、GetColour/SetColour(colour):当图表被关闭后,调用get*来看用户的选择。最初它被设置为black。如果在对话框显示之前设置了它,那么对话框最初显示为该颜色。

3、GetCustomColour(i)/SetCustomColour(i, colour):根据自定义的颜色数组中的索引i来返回或设置元素。i位于[0,15]之间。初始时,所有的自定义颜色都是白色。

一个回避了wx.ColorData的使用颜色对话框的便利函数是:

wx.GetColourFromUser(parent, colInit)

colInit是wx.Colour的一个实例,并且当对话框显示时它是对话框的初始的值。函数的返回值也是一个wx.Colour的实例。如果用户通过OK关闭了对话框,那么方法wx.Colour.OK()返回True。如果用户通过Cancel关闭了对话框,那么方法wx.Colour.OK()返回False。

 
=== 如何使用户能够浏览图像? ===


如果你在你的程序中做图形处理,那么在他们浏览文件树时使用缩略图是有帮助的。用于该目的的wxPython对话框被称为wx.lib.imagebrowser.ImageDialog。图9.10显示了一个例子。

例9.10显示了用于该图像浏览对话框的简单的代码。


'''例9.10'''
'''创建一个图像浏览对话框'''
{{{#!python
import wx
import wx.lib.imagebrowser as imagebrowser

if __name__ == "__main__":
    app = wx.PySimpleApp()
    dialog = imagebrowser.ImageDialog(None)
    if dialog.ShowModal() == wx.ID_OK:
        print "You Selected File: " + dialog.GetFile()
    dialog.Destroy()
}}}

'''图9.10'''

attachment:w9.10.gif


wx.lib.imagebrowser.ImageDialog类是十分简单的,并有相对较少的选项供程序员去设置。要改变该对话框的行为的话,请查阅改变显示的文件类型的Python源码。类的构造函数要求两个参数。

ImageDialog(parent, set_dir=None)

set_dir参数是对话框显示时所在的目录。如果不设置,那么使用应用程序当前的工作目录。在对话框被关闭后,GetFile()返回所选文件的完整路径字符串,GetDirectory()只返回目录部分。


                 
== 创建向导 ==


向导是一系列被链接在一起的简单对话框,它使得用户一步步地跟随它们。通常它们被用于指导用户的安装或一个复杂的配置过程。图9.11显示了一个向导示例。


'''图9.11'''

attachment:w9.11.gif


在wxPython中,一个向导是一系列的页面,它由类wx.wizard.Wizard的一个实例控制。向导实例管理用户的页面切换事件。这些页面自身也是类wx.wizard.WizardPageSimple或wx.wizard.WizardPage的实例。这两种类的实例,它们只不过是附加了必要的管理页面链接逻辑的wx.Panel的实例。已证明这两个实例之间的区别仅当用户按下Next按钮时。wx.wizard.WizardPage的实例使你能够动态地决定浏览哪页,而wx.wizard.WizardPageSimple的实例要求向导被显示前,顺序被预先设置。例9.11显示了产生图9.11的代码。


'''例9.11'''
'''创建一个简单的静态向导'''
{{{#!python
import wx
import wx.wizard

class TitledPage(wx.wizard.WizardPageSimple):#1 创建页面样板
    def __init__(self, parent, title):
        wx.wizard.WizardPageSimple.__init__(self, parent)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(self.sizer)
        titleText = wx.StaticText(self, -1, title)
        titleText.SetFont(
                wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
        self.sizer.Add(titleText, 0,
                wx.ALIGN_CENTRE | wx.ALL, 5)
        self.sizer.Add(wx.StaticLine(self, -1), 0,
                wx.EXPAND | wx.ALL, 5)

if __name__ == "__main__":
    app = wx.PySimpleApp()
    wizard = wx.wizard.Wizard(None, -1, "Simple Wizard")# 创建向导实例

    # 创建向导页面
    page1 = TitledPage(wizard, "Page 1")
    page2 = TitledPage(wizard, "Page 2")
    page3 = TitledPage(wizard, "Page 3")
    page4 = TitledPage(wizard, "Page 4")

    page1.sizer.Add(wx.StaticText(page1, -1,
            "Testing the wizard"))
    page4.sizer.Add(wx.StaticText(page4, -1,
            "This is the last page."))

    #2 创建页面链接
    wx.wizard.WizardPageSimple_Chain(page1, page2)
    wx.wizard.WizardPageSimple_Chain(page2, page3)
    wx.wizard.WizardPageSimple_Chain(page3, page4)

    wizard.FitToPage(page1)#3 调整向导的尺寸

    if wizard.RunWizard(page1):#4 运行向导
        print "Success"

    wizard.Destroy()

}}}

'''#1''' 为了便于移植的目的,我们创建了一个简单的小的页面,它包含了一个静态文本标题。通常情况下,这儿还包含一些表单元素,可能还有一些要用户输入的数据。


'''#2''' wx.wizard.WizardPageSimple_Chain()函数是一个便利的方法,它以两个页面为参数相互地调用它们的SetNext()和SetPrev()方法。


'''#3''' FitToSize()根据页面参数及该页链条上的所有页面调整向导的大小。该方法只能在页面链接被创建后调用。


'''#4''' 该方法的参数是向导开始时的页面。向导在它到达一个没有下一页的页面时知道去关闭。如果用户浏览了整个向导并通过按下Finish按钮退出的话,RunWizard()方法返回True。

创建wx.wizard.Wizard实例是使用向导的第一步。其构造函数如下:
{{{#!python
wx.wizard.Wizard(parent, id=-1, title=wx.EmptyString,
    bitmap=wx.NullBitmap, pos=wx.DefaultPosition)
}}}
在这里的parent, id, title, pos和wx.Panel的意义相同。如果设置了bitmap参数,那么该参数将显示在每一页上。这儿只有一个样式标记:wx.wizard.WIZARD_EX_HELPBUTTON,它显示一个帮助按钮。这是一个扩展的标记,需要使用第8章所说的两步创建过程。

通常,你将调用例9.11的#3中所示的FitToSize()来管理窗口的尺寸,但是你也可以通过调用带有一个元组或wx.Size实例的SetPageSize()来设置一个最小的尺寸。GetPageSize()方法返回当前的尺寸,在这两种情况下,该尺寸仅用于对话框中的单个的页面部分,而作为一个整体的对话框将更大一些。

你可以管理该类中的页面。方法GetCurrentPage()返回当前被显示的页面,如果该向导当前没有被显示,该方法返回None。你可以通过调用HasNextPage()和HasPrevPage()来确定当前页是否有下一页或上一页。
使用RunWizard()来运行该向导,如例9.11中#4的说明。

向导产生的命令事件如表9.5所示,你可以捕获这些事件,以便作专门的处理。这些事件对象属于类wx.wizard.WizardEvent,它们提供了两个可用的方法。GetPage()返回wx.WizardPage的实例,该实例在向导事件产生时是有效,而非作为事件结果被显示的实例。如果事件是前进一页,那么GetDirection()返回True,如果事件是后退一页,那么GetDirection()返回False。


'''表9.5 wx.wizard.WizardDialog的事件'''

||EVT_WIZARD_CANCEL||当用户按下Cancel按钮时产生。该事件可以使用Veto()来否决,这种情况下,对话框将不会消失。||
||EVT_WIZARD_FINISHED||当用户按下Finish按钮时产生。||
||EVT_WIZARD_HELP||当用户按下Help按钮时产生。||
||EVT_WIZARD_PAGE_CHANGED||在页面被切换后产生。||
||EVT_WIZARD_PAGE_CHANGING||当用户已请求了一个页面切换时产生,这时页面还没有发生切换。这个事件可以被否决(例如,如果页面上有一个必须被填写的字段)。||

wx.wizard.WizardPageSimple类被当作一个面板一样。它的构造函数使你可以设置上一页和下一页,如下所示:

wx.wizard.WizardPageSimple(parent=None, prev=None, next=None)

如果你想在构造器中设置它们,你可以使用SetPrev()和SetNext()方法。如果那样太麻烦,你可以使用wx.wizard.WizardPageSimple_Chain(),它设置两页间的链接关系。

向导页的复杂版:wx.wizard.WizardPage,稍微不同。它没有显式地设置前一页和下一页,而是使你能够使用更复杂的逻辑去定义下一步到哪儿。它的构造函数如下:
{{{#!python
wx.WizardPage(parent, bitmap=wx.NullBitmap, resource=None)
}}}
如果bitmap参数被设置了,那么该参数覆盖父向导中所设置的位图。resource参数从一个wxPython资源装载页面。要处理页面逻辑,就要覆盖GetPrev()和GetNext()方法来返回你想要向导下一步的位置。该类的一个典型的用法是根据用户对当前页的响应动态地决定接下来的页面。

                   
== 显示启动提示 ==


许多应用程序都使用启动提示来作为一种向用户介绍该程序的特性等信息的方法。在wxPython中有一个非常简单的机制用来显示启动提示。图9.12显示了一个提示窗口的示例。


'''图9.12'''

attachment:w9.12.gif


例9.12显示了相关代码


'''例9.12'''
{{{#!python
import wx

if __name__ == "__main__":
    app = wx.PySimpleApp()
    provider = wx.CreateFileTipProvider("tips.txt", 0)
    wx.ShowTip(None, provider, True)
}}}
有两个便利的函数用来管理启动提示。第一个如下创建一个wx.TipProvider:
{{{#!python
wx.CreateFileTipProvider(filename, currentTip)
}}}
filename是包含提示字符串的文件的名字。currentTip是该文件中用于一开始显示的提示字符的索引,该文件中的第一个提示字符串的索引是0。

提示文件是一个简单的文本文件,其中的每一行是一个不同的提示。空白行被忽略,以#开始的行被当作注释并也被忽略。下面是上例所使用的提示文件中的内容:

You can do startup tips very easily.
Feel the force, Luke.

提示的提供者(provider)是类wx.PyTipProvider的一个实例。如果你需要更细化的功能,你可以创建你自己的wx.TipProvider的子类并覆盖GetTip()函数。

显示提示的函数是wx.ShowTip():
{{{#!python
wx.ShowTip(parent, tipProvider, showAtStartup)
}}}
parent是父窗口,tipProvider通常创建自wx.CreateFileTipProvider。showAtStartup控制启动提示显示时,复选框是否被选择。该函数的返回值是复选框的状态值,以便你使用该值来决定你的应用程序下次启动时是否显示启动提示。


                   
== 使用验证器(validator)来管理对话框中的数据 ==


验证器是一个特殊的wxPython对象,它简化了对话框中的数据管理。当我们在第三章中讨论事件时,我们简要的提及到如果一个窗口部件有一个验证器,那么该验证器能够被事件系统自动调用。我们已经见过了几个wxPython窗口部件类的构造函数中将验证器作为参数,但是我们还没有讨论它们。

验证器有三个不相关的功能:

1、在对话框关闭前验证控件中的数据
2、自动与对话框传递数据
3、验证用户键入的数据

 
=== 如何使用验证器来确保正确的数据? ===


验证器对象是wx.Validator的子类。父类是抽象的,不能直接使用。尽管在C++ wxWidget集中有一对预定义的验证器类,但是在wxPython中,你需要定义你自己的验证器类。正如我们在别处所见的,你的Python类需要继承自Python特定的子类:wx.PyValidator,并且能够覆盖该父类的所有方法。一个自定义的验证器子类必须覆盖方法Clone(),该方法应该返回验证器的相同的副本。

一个验证器被关联到你的系统中的一个特定的窗口部件。这可以用两种方法之一来实现。第一种方法,如果窗口部件许可的话,验证器可以被作为一个参数传递给该窗口部件的构造函数。如果该窗口部件的构造函数没有一个验证器参数,你仍然可以通过创建一个验证器实例并调用该窗口部件的SetValidator(validator)方法来关联一个验证器。

要验证控件中的数据,你可以先在你的验证器子类中覆盖Validate(parent)方法。parent参数是验证器的窗口部件的父窗口(对话框或面板)。如果必要,可以使用这个来从对话框中其它窗口部件得到数据,或者你可以完全忽略该参数。你可以使用self.GetWindow()来得到正在被验证的窗口部件的一个引用。你的Validate(parent)方法的返回值是一个布尔值。True值表示验证器的窗口部件中的数据已验证了。False表示有问题。你可以根据Validate()方法来使用x.MessageBox()去显示一个警告,但是你不应该做其它的任何可以在wxPython应用程序中引发事件的事情。

Validate()的返回值是很重要的。它在你使用OK按钮(该按钮使用wx.ID_OK ID)企图关闭一个对话框时发挥作用。作为对OK按钮敲击处理的一部分,wxPython调用对话框中有验证器的窗口部件的Validate()函数。如果任一验证器返回False,那么对话框不将关闭。例9.13显示了一个带有验证器的示例对话框,它检查所有文本控件中有无数据。


'''例9.13'''
'''检查所有文本控件有无数据的验证器'''
{{{#!python
import wx

about_txt = """\
The validator used in this example will ensure that the text
controls are not empty when you press the Ok button, and
will not let you leave if any of the Validations fail."""


class NotEmptyValidator(wx.PyValidator):# 创建验证器子类
     def __init__(self):
         wx.PyValidator.__init__(self)

     def Clone(self):
         """
         Note that every validator must implement the Clone() method.
         """
         return NotEmptyValidator()

     def Validate(self, win):#1 使用验证器方法
         textCtrl = self.GetWindow()
         text = textCtrl.GetValue()

         if len(text) == 0:
             wx.MessageBox("This field must contain some text!", "Error")
             textCtrl.SetBackgroundColour("pink")
             textCtrl.SetFocus()
             textCtrl.Refresh()
             return False
         else:
             textCtrl.SetBackgroundColour(
                 wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
             textCtrl.Refresh()
             return True

     def TransferToWindow(self):
         return True

     def TransferFromWindow(self):
         return True



class MyDialog(wx.Dialog):
    def __init__(self):
        wx.Dialog.__init__(self, None, -1, "Validators: validating")

        # Create the text controls
        about = wx.StaticText(self, -1, about_txt)
        name_l = wx.StaticText(self, -1, "Name:")
        email_l = wx.StaticText(self, -1, "Email:")
        phone_l = wx.StaticText(self, -1, "Phone:")

        #2 使用验证器
        name_t = wx.TextCtrl(self, validator=NotEmptyValidator())
        email_t = wx.TextCtrl(self, validator=NotEmptyValidator())
        phone_t = wx.TextCtrl(self, validator=NotEmptyValidator())

        # Use standard button IDs
        okay = wx.Button(self, wx.ID_OK)
        okay.SetDefault()
        cancel = wx.Button(self, wx.ID_CANCEL)

        # Layout with sizers
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(about, 0, wx.ALL, 5)
        sizer.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5)
        
        fgs = wx.FlexGridSizer(3, 2, 5, 5)
        fgs.Add(name_l, 0, wx.ALIGN_RIGHT)
        fgs.Add(name_t, 0, wx.EXPAND)
        fgs.Add(email_l, 0, wx.ALIGN_RIGHT)
        fgs.Add(email_t, 0, wx.EXPAND)
        fgs.Add(phone_l, 0, wx.ALIGN_RIGHT)
        fgs.Add(phone_t, 0, wx.EXPAND)
        fgs.AddGrowableCol(1)
        sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5)

        btns = wx.StdDialogButtonSizer()
        btns.AddButton(okay)
        btns.AddButton(cancel)
        btns.Realize()
        sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5)

        self.SetSizer(sizer)
        sizer.Fit(self)
        

app = wx.PySimpleApp()

dlg = MyDialog()
dlg.ShowModal()
dlg.Destroy()

app.MainLoop()

}}}

'''#1''' 该方法测试基本的控件有无数据。如果没有,相应的控件的背景色变为粉红色。


'''#2''' 这几行,对话框中的每个文本控件都关联一个验证器。

图9.13显示了一个文本域为空就企图关闭的对话框。


'''图9.13'''

attachment:w9.13.gif


明确地告诉对话框去核对验证器的代码没有出现在示例中,因为它是wxPython事件系统的一部分。对话框与框架之间的另一区别是对话框有内建的验证器行为,而框架没有。如果你喜欢将验证器用于不在对话框内的控件,那么调用父窗口的Validate()方法。如果父窗口已设置了wx.WS_EX_VALIDATE_RECURSIVELY额外样式,那么所有的子窗口的Validate()方法也被调用。如果任一验证失败,那么Validate返回False。接下来,我们将讨论如何将验证器用于数据传输。


                   
=== 如何使用验证器传递数据? ===


验证器的第二个重要的功能是,当对话框打开时,它自动将数据传送给对话框显示,当该对话框关闭时,自动从对话框把数据传输到一个外部资源。图9.14显示了一个示例对话框。


'''图9.14'''

attachment:w9.14.gif


要实现这个,你必须在你的验证器子类中覆盖两个方法。方法TransferToWindow()在对话框打开时自动被调用。你必须使用这个方法把数据放入有验证器的窗口部件。TransferFromWindow()方法在使用OK按钮关闭对话框窗口时且数据已被验证后被自动调用。你必须使用这个方法来将数据从窗口部件移动给其它的资源。

数据传输必须发生的事实意味着验证器必须对一个外部的数据对象有一些了解,如例9.14所示。在这个例子中,每个验证器都使用一个全局数据字典的引用和一个字典内的对于相关控件重要的关键字来被初始化。

当对话框打开时,TransferToWindow()方法从字典中根据关键字读取数据并把数据放入文本域。当对话框关闭时,TransferFromWindow()方法反向处理并把数据写入字典。这个例子的对话框显示你传输的数据。


'''例9.14'''
'''一个数据传输验证器'''
{{{#!python
import wx
import pprint

about_txt = """\
The validator used in this example shows how the validator
can be used to transfer data to and from each text control
automatically when the dialog is shown and dismissed."""


class DataXferValidator(wx.PyValidator):# 声明验证器
     def __init__(self, data, key):
         wx.PyValidator.__init__(self)
         self.data = data
         self.key = key

     def Clone(self):
         """
         Note that every validator must implement the Clone() method.
         """
         return DataXferValidator(self.data, self.key)

     def Validate(self, win):# 没有验证数据
         return True

     def TransferToWindow(self):# 对话框打开时被调用
         textCtrl = self.GetWindow()
         textCtrl.SetValue(self.data.get(self.key, ""))
         return True

     def TransferFromWindow(self):# 对话框关闭时被调用
         textCtrl = self.GetWindow()
         self.data[self.key] = textCtrl.GetValue()
         return True



class MyDialog(wx.Dialog):
    def __init__(self, data):
        wx.Dialog.__init__(self, None, -1, "Validators: data transfer")

        # Create the text controls
        about = wx.StaticText(self, -1, about_txt)
        name_l = wx.StaticText(self, -1, "Name:")
        email_l = wx.StaticText(self, -1, "Email:")
        phone_l = wx.StaticText(self, -1, "Phone:")

        # 将验证器与窗口部件相关联
        name_t = wx.TextCtrl(self, validator=DataXferValidator(data, "name"))
        email_t = wx.TextCtrl(self, validator=DataXferValidator(data, "email"))
        phone_t = wx.TextCtrl(self, validator=DataXferValidator(data, "phone"))

        # Use standard button IDs
        okay = wx.Button(self, wx.ID_OK)
        okay.SetDefault()
        cancel = wx.Button(self, wx.ID_CANCEL)

        # Layout with sizers
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(about, 0, wx.ALL, 5)
        sizer.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5)
        
        fgs = wx.FlexGridSizer(3, 2, 5, 5)
        fgs.Add(name_l, 0, wx.ALIGN_RIGHT)
        fgs.Add(name_t, 0, wx.EXPAND)
        fgs.Add(email_l, 0, wx.ALIGN_RIGHT)
        fgs.Add(email_t, 0, wx.EXPAND)
        fgs.Add(phone_l, 0, wx.ALIGN_RIGHT)
        fgs.Add(phone_t, 0, wx.EXPAND)
        fgs.AddGrowableCol(1)
        sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5)

        btns = wx.StdDialogButtonSizer()
        btns.AddButton(okay)
        btns.AddButton(cancel)
        btns.Realize()
        sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5)

        self.SetSizer(sizer)
        sizer.Fit(self)
        

app = wx.PySimpleApp()

data = { "name" : "Jordyn Dunn" }
dlg = MyDialog(data)
dlg.ShowModal()
dlg.Destroy()

wx.MessageBox("You entered these values:\n\n" +
              pprint.pformat(data))

app.MainLoop()
}}}
对话框中验证器的传输数据方法的调用自动发生。要在非对话框窗口中使用验证器来传输数据,必须调用父窗口部件的TransDataFromWindow()和TransferDataToWindow()方法。如果该窗口设置了wx.WS_EX_VALIDATE_RECURSIVELY额外样式,那么在所有的子窗口部件上也将调用该传输函数。



                    
=== 如何在数据被键入时验证数据? ===


在数据被传给窗口部件之前,你也可使用验证器来在用户输入数据时验证所输入的数据。这是非常有用的,因为它可以防止将得到的坏的数据传入你的应用程序。图9.12显示了一个例子,其中对话框的文本阐明了该思想。


'''图9.15'''

attachment:w9.15.gif


验证数据的方法的自动化成份少于其它的机制。你必须显式绑定验证器的窗口部件的字符事件给一个函数,如下所示:

self.Bind(wx.EVT_CHAR, self.OnChar)

该窗口部件假设事件源属于验证器。例9.15显示了这个绑定。


'''例9.15 实时验证'''
{{{#!python
import wx
import string

about_txt = """\
The validator used in this example will validate the input on the fly
instead of waiting until the okay button is pressed. The first field
will not allow digits to be typed, the second will allow anything
and the third will not allow alphabetic characters to be entered.
"""


class CharValidator(wx.PyValidator):
    def __init__(self, flag):
         wx.PyValidator.__init__(self)
         self.flag = flag
         self.Bind(wx.EVT_CHAR, self.OnChar)# 绑定字符事件

    def Clone(self):
         """
         Note that every validator must implement the Clone() method.
         """
         return CharValidator(self.flag)

    def Validate(self, win):
         return True

    def TransferToWindow(self):
         return True

    def TransferFromWindow(self):
         return True

    def OnChar(self, evt):# 数据处理
         key = chr(evt.GetKeyCode())
         if self.flag == "no-alpha" and key in string.letters:
              return
         if self.flag == "no-digit" and key in string.digits:
              return
         evt.Skip()


class MyDialog(wx.Dialog):
    def __init__(self):
        wx.Dialog.__init__(self, None, -1, "Validators: behavior modification")

        # Create the text controls
        about = wx.StaticText(self, -1, about_txt)
        name_l = wx.StaticText(self, -1, "Name:")
        email_l = wx.StaticText(self, -1, "Email:")
        phone_l = wx.StaticText(self, -1, "Phone:")

        # 绑定验证器
        name_t = wx.TextCtrl(self, validator=CharValidator("no-digit"))
        email_t = wx.TextCtrl(self, validator=CharValidator("any"))
        phone_t = wx.TextCtrl(self, validator=CharValidator("no-alpha"))

        # Use standard button IDs
        okay = wx.Button(self, wx.ID_OK)
        okay.SetDefault()
        cancel = wx.Button(self, wx.ID_CANCEL)

        # Layout with sizers
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(about, 0, wx.ALL, 5)
        sizer.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5)
        
        fgs = wx.FlexGridSizer(3, 2, 5, 5)
        fgs.Add(name_l, 0, wx.ALIGN_RIGHT)
        fgs.Add(name_t, 0, wx.EXPAND)
        fgs.Add(email_l, 0, wx.ALIGN_RIGHT)
        fgs.Add(email_t, 0, wx.EXPAND)
        fgs.Add(phone_l, 0, wx.ALIGN_RIGHT)
        fgs.Add(phone_t, 0, wx.EXPAND)
        fgs.AddGrowableCol(1)
        sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5)

        btns = wx.StdDialogButtonSizer()
        btns.AddButton(okay)
        btns.AddButton(cancel)
        btns.Realize()
        sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5)

        self.SetSizer(sizer)
        sizer.Fit(self)
        

app = wx.PySimpleApp()

dlg = MyDialog()
dlg.ShowModal()
dlg.Destroy()

app.MainLoop()

}}}
由于OnChar()方法是在一个验证器中,所以它在窗口部件响应字符事件之间被调用。该方法让你可以通过使用Skip()来将事件传送给窗口部件。你必须调用Skip(),否则验证器将妨碍正常的事件处理。验证器执行一个测试来查看用于该控件的字符是否有效。如果该字符无效,那么Skip()不被调用,并且事件处理停止。如果有必须的话,除了wx.EVT_CHAR之外的其它事件也可以被绑定,并在窗口部件响应之前验证器处理那些事件。

对于处理你wxPython应用程序中的数据,验证器是一个强大且灵活的机制。适当地使用它们,可以让你的应用程序的开发和维护更加的顺畅。

                   
Line 1008: Line 1128:
1、wxPython中的大部分用户交互都发生在wx.Frame或wx.Dialog中。wx.Frame代表用户调用的窗口。wx.Frame实例的创建就像其它的wxPython窗口部件一样。wx.Frame的典型用法包括创建子类,子类通过定义子窗口部件,布局和行为来扩展基类。通常,一个框架包含只包含一个wx.Panel的顶级子窗口部件或别的容器窗口。

2、这儿还有各种特定于wx.Frame的样式标记。其中的一些影响框架的尺寸和形状,另一些影响在系统中相对于其它的框架,它将如何被绘制,还有一些定义了在框架边框上有那些界面装饰。在某种情况下,定义一个样式标记需要“两步”的创建过程。

3、通过调用Close()方法可以产生关闭框架的请求。这给了框架一个关闭它所占用的资源的机会。框架也能否决一个关闭请求。调用Destroy()方法将迫使框架立即消失而没有任何延缓。

4、框架中的一个特定的子窗口部件可以使用它的wxPython ID、名字或它的文本标签来发现。

5、通过包括wx.ScrolledWindow类的容器部件可以实现滚动。这儿有几个方法来设置滚动参数,最简单的是在滚动窗口中使用sizer,在这种情况下,wxPython自动确定滚动面板的虚拟尺寸(virtual size)。如果想的话,虚拟尺寸可以被手动设置。

6、这儿有一对不同的框架子类,它们允许不同的外观。类wx.MDIParentFrame可以被用来创建MDI,而wx.MiniFrame可以创建一个带有较小标题栏的工具框样式的窗口。使用SetShape()方法,框架可以呈现出非矩形的形状。形状的区域可以被任何位图定义,并使用简单的颜色掩码来决定区域的边缘。非矩形窗口通常没有标准的标题栏,标题栏使得框架可以被拖动, 但这可以通过显式地处理鼠标事件来管理。

7、位于两个子窗口间的可拖动的分割条能够使用wx.SplitterWindow来实现,分割条可以被用户以交互的方式处理,或以编程的方式处理(如果需要的话)。

在下一章,我们将讨论对话框,它的行为类似于框架。
1、对话框被用于在有一套特殊的信息需要被获取的情况下,处理与用户的交互,这种交互通常很快被完成。在wxPython中,你可以使用通用的wx.Dialog类来创建你自己的对话框,或者你也可以使用预定义的对话框。大多数情况下,通常被使用的对话框都有相应的便利函数,使得这种对话框的使用更容易。

2、对话框可以显示为模式对话框,这意味在对话框可见时,该应用程序中的用户所有的其它输入将被阻塞。模式对话框通过使用ShowModal()方法来被调用,它的返回值依据用户所按的按钮而定(OK或Cancel)。关闭模式对话框并不会销毁它,该对话框的实例可以被再用。

3、在wxPython中有三个通用的简单对话框。wx.MessageDialog显示一个消息对话框。wx.TextEntryDialog使用户能够键入文本,wx.SingleChoiceDialog给用户一个基于列表项的选择。

4、当正在执行一个长时间的后台的任务时,你可以使用wx.ProgressDialog来给用户显示进度信息。用户可以通过wx.FileDialog使用标准文件对话框来选择一个文件。可以使用wx.DirDialog来创建一个标准目录树,它使得用户可以选择一个目录。

5、你可以使用wx.FontDialog和wx.ColorDialog来访问标准的字体选择器和颜色选择器。在这两种情况中,对话框的行为和用户的响应是由一个单独的数据类来控制的。

6、要浏览缩略图,可以使用wxPython特定的类wx.lib.imagebrowser.ImageDialog。这个类使用户能够通过文件系统并选择一个图像。

7、你可以通过使用wx.wizard.Wizard创建一个向导来将一组相关的对话框表单链接起来。对话框表单是wx.wizard.WizardSimplePage或wx.wizard.WizardPage的实例。两者的区别是,wx.wizard.WizardSimplePage的页到页的路线需要在向导被显示之前就安排好,而wx.wizard.WizardPage使你能够在运行时管理页到页的路线的逻辑。

8、使用wx.CreateFileTipProvider和wx.ShowTip函数可以很容易地显示启动提示。

9、验证器是很有用的对象,如果输入的数据不正确的话,它可以自动阻止对话框的关闭。他们也可以在一个显示的对话框和一个外部的对象之间传输数据,并且能够实时地验证数据的输入。



       

使用模式对话框工作

模式对话框用于与用户进行快速的交互或在用户可以执行程序的下一步之前,对话框中的信息必须被输入的时候。在wxPython中,有几个标准的函数用来显示基本的模式对话框。这些对话框包括警告框,单行文本域,和从列表中选择。在随后的部分,我们将给你展示这些对话框,以及如何使用这些预定义的函数来减轻你的工作量。

如何创建一个模式对话框?

模式对话框阻塞了别的窗口部件接收用户事件,直到该模式对话框被关闭;换句话说,在它存在期间,用户一直被置于对话模式中。如图9.1所示,你不能总是根据外观来区别对话框和框架。在wxPython中,对话框与框架间的区别不是基于它们的外观的,而主要是它们处理事件的办法的实质。

图9.1 一个模式对话框

attachment:w9.1.gif

对话框的创建和配置与框架稍微有些不同。例9.1显示了产生图9.1的代码。所显示的对话框上的按钮被敲击后,该对话框就关闭了,并且一条消息被输出到stdout(标准输出)。

例9.1 定义一个模式对话框

   1 import wx
   2 
   3 class SubclassDialog(wx.Dialog):
   4     def __init__(self):#初始化对话框
   5         wx.Dialog.__init__(self, None, -1, 'Dialog Subclass', 
   6                 size=(300, 100))
   7         okButton = wx.Button(self, wx.ID_OK, "OK", pos=(15, 15))
   8         okButton.SetDefault()
   9         cancelButton = wx.Button(self, wx.ID_CANCEL, "Cancel", 
  10                 pos=(115, 15))
  11         
  12 if __name__ == '__main__':
  13     app = wx.PySimpleApp()
  14     app.MainLoop() 
  15     dialog = SubclassDialog()
  16     result = dialog.ShowModal()#显示模式对话框
  17     if result == wx.ID_OK:
  18         print "OK"
  19     else:
  20         print "Cancel"
  21     dialog.Destroy()

与前一章的wx.Frame的例子比较,这儿有两个需要注意的事情。在init方法中,按钮是被直接添加到wx.Dialog,而非wx.Panel。面板在对话框中的使用比在框架中少的多,部分原因是因为对话框与框架相比倾向简单化,但主要是因为wx.Panel特性(标准系统背景和tab键横向切换控件焦点)已经默认存在于wx.Dialog中。

要显示为模式对话框,使用ShowModal()方法。这与用于框架的的Show()方法在对程序的执行上有不同的作用。在调用ShowModal()后你的应用程序将处于等待中,直到对话框被取消。

模式将保持到对话框方法EndModal(retCode)被调用,该方法关闭对话框。参数retCode是由ShowModal()方法返回的一个整数值。典型的,应用程序利用这个返回值来知道用户是如何关闭对话框的,以控制以后的操作。但是结束这个模式并没有销毁或甚至关闭对话框。保持对话框的存在可能是一件好事,因为这意味你可以把用户选择的信息存储为对话框实例的数据成员,并且即使在对话框被关闭后也能从对话框重新获得那些信息。在接下来的部分,我们将看一些我们使用对话框处理程序中用户输入的数据的例子。

由于例9.1中没有定义事件处理器,你可能会惊奇对话框是如何响应按钮敲击的。这个行为已经定义在wxDialog中了。有两个预定义的wxPython ID号,它们在对话框中有特殊的意思。当对话框中的一个使用wx.ID_OK ID的wx.Button被敲击时,模式就结束了,对话框也关闭了,wx.ID_OK就是ShowModal()调用返回的值。同样,一个使用wx.ID_CANCEL ID的按钮做相同的事情,但是ShowModal()的返回值是wx.ID_CANCEL。

例9.1显示了处理模式对话框的一个典型的方法。在对话框被调用后,返回值被用作if语句中的测试。在这种情况下,我们简单地打印结果,在更复杂的例子中,wx.ID_OK将执行用户在对话框中所要求的动作,如打开文件或选择颜色。

典型的,你在完成对对话框的使用后,你应该显式地销毁它。这通知C++对象它应该自我销毁,然后这将使得它的Python部分被作为垃圾回收。如果你希望在你的应用程序中,以后再次使用该对话框时不重建它,以加速对话框的响应时间,那么你可以保持对该对话框的一个引用,并当你需要再次激活它时,简单地调用它的ShowModal()方法。当应用程序准备退出时,确保已销毁了它,否则MainLoop()将仍将它作为一个存在的顶级窗口,并且程序将不能正常退出。

如何创建一个警告框?

经由一个对话框与用户交互的最简单的三个办法分别是:wx.MessageDialog,它是一个警告框、wx.TextEntryDialog,它提示用户去输入一些短的文本、wx.SingleChoiceDialog,它使用户能够从一个有效选项列表中进行选择。在接下来的三个小节中,我们将论这些简单的对话框。

消息对话框显示一个短的消息,并使用户通过按下按钮来作响应。通常,消息框被用作去显示重要的警告、yes/no问题、或询问用户是否继续某种操作。图9.2显示了一个典型的消息框。

图9.2

attachment:w9.2.gif

使用消息框是十分的简单。例9.2显示了创建一个消息框的两种办法。

例9.2 创建一个消息框

   1 import wx
   2 
   3 if __name__ == "__main__":
   4     app = wx.PySimpleApp()
   5 
   6 # 方法一,使用类
   7     dlg = wx.MessageDialog(None, "Is this explanation OK?",
   8                           'A Message Box',
   9                           wx.YES_NO | wx.ICON_QUESTION)
  10     retCode = dlg.ShowModal()
  11     if (retCode == wx.ID_YES):
  12         print "yes"
  13     else:
  14         print "no"
  15     dlg.Destroy()

#1 方法二,使用函数

  • retCode = wx.MessageBox("Is this way easier?", "Via Function",

    • wx.YES_NO | wx.ICON_QUESTION)

例9.2创建了两个消息框,一个在另一个的后面。这第一个方法是创建类wx.MessageDialog的一个实例,并使用ShowModal()来显示它。

使用wx.MessageDialog

使用wx.MessageDialog的构造函数,你可以设置对话框的消息和按钮,构造函数如下:

wx.MessageDialog(parent, message, caption="Message box",

message参数是实际显示在对话框中的文本。如果消息字符串包含\n字符,那么文本将在此换行。caption参数显示在对话框的标题栏中。pos参数使你可以指定对话框显示在屏幕上的位置——在微软Windows下,这个参数将被忽略。

wx.MessageDialog的样式标记分为两类。第一类控制显示在对话框中的按钮。表9.1说明了这些样式。

表9.1 wx.MessageDialog的按钮样式

wx.CANCEL

包括一个cancel(取消)按钮。这个按钮有一个ID值wx.ID_CANCEL。

wx.NO_DEFAULT

在一个wx.YES_NO对话框中,No(否)按钮是默认的。

wx.OK

包括一个OK按钮,这个按钮有一个ID值wx.ID_OK。

wx.YES_DEFAULT

在一个wx.YES_NO对话框中,Yes按钮是默认的。这是默认行为。

wx.YES_NO

包括Yes和No按钮,各自的ID值分别是wx.ID_YES和wx.ID_NO。

第二套样式标记控制紧挨着消息文本的图标。它们显示在表9.2中。

表9.2 wx.MessageDialog的图标样式

wx.ICON_ERROR

表示一个错误的图标。

wx.ICON_EXCLAMATION

表示警告的图标。

wx.ICON_HAND

同wx.ICON_ERROR。

wx.ICON_INFORMATION

信息图标,字母i。

wx.ICON_QUESTION

问号图标。

最后,你可以使用样式wx.STAY_ON_TOP将对话框显示在系统中任何其它窗口的上面,包括系统窗口和wxPython应用程序窗口。

你在例9.2所见到的,对话框通过使用ShowModal()被调用。根据所显示的按钮,返回的结果是以下值之一:wx.ID_OK, wx.ID_CANCEL,wx.ID_YES, 或 wx.ID_NO。如同其它对话框的情况,你通常使用这些值来控制程序的执行。

使用wx.MessageBox()函数

例9.2中的#1显示了一个调用消息框的更简短的方法。这个便利的函数wx.MessageBox()创建对话框,调用ShowModal(),并且返回下列值之一:wx.YES, wx.NO, wx.CANCEL, 或 wx.OK。函数的形式比MessageDialog的构造函数更简单,如下所示:

   1 wx.MessageBox(message, caption="Message", style=wx.OK)

在这个例子中,参数message, caption, style的意思和构造函数中的相同,你可以使用所有相同的样式标记。正如我们贯穿本章将看到的,在wxPython预定义的几个对话框都有便利的函数。在你为单一的使用创建对话框的时候,你的选择有一个优先的问题。如果你计划束缚住对话框以便多次调用它,那么你可能会优先选择去实例化对象以便你能够束缚该引用,而不使用函数的方法,尽管这对于这些简单的对话框来说,所节约的时间可以忽略不计。

要在你的消息框中显示大量的文本(例如,终端用户许可证的显示),你可以使用wxPython特定的类wx.lib.dialogs.ScrolledMessageDialog,它包含如下的构造函数:

   1 wx.lib.dialogs.ScrolledMessageDialog(parent, msg, caption, 
   2         pos=wx.wxDefaultPosition, size=(500,300))

这个对话框不使用本地消息框控件,它根据别的wxPython窗口部件来创建一个对话框。它只显示一个OK按钮,并且没有更多的样式信息。

如何从用户得到短的文本?

这第二个简单类型的对话框是wx.TextEntryDialog,它被用于从用户那里得到短的文本输入。它通常用在在程序的开始时要求用户名或密码的时候,或作为一个数据输入表单的基本替代物。图9.3显示了一个典型的文本对话框。

图9.3 文本输入标准对话框

attachment:w9.3.gif

例9.3显示了产生图9.3的代码

例9.3

   1 import wx
   2 
   3 if __name__ == "__main__":
   4     app = wx.PySimpleApp()
   5     dialog = wx.TextEntryDialog(None, 
   6             "What kind of text would you like to enter?",
   7             "Text Entry", "Default Value", style=wx.OK|wx.CANCEL)
   8     if dialog.ShowModal() == wx.ID_OK:
   9         print "You entered: %s" % dialog.GetValue()
  10 
  11     dialog.Destroy()

在前一小节,我们创建了一个对话框类的实例,在这里,我们要用到的对话框类是wx.TextEntryDialog。该类的构造函数比简单消息对话框要复杂一些:

   1 wx.TextEntryDialog(parent, message, caption="Please enter text", 
   2     defaultValue="", style=wx.OK | wx.CANCEL | wx.CENTRE, 
   3     pos=wx.DefaultPosition)

message参数是显示在对话框中的文本提示,而caption显示在标题栏中。defaultValue显示在文本框中的默认值。style可以包括wx.OK和wx.CANCEL,它显示适当的按钮。

几个wx.TextCtrl的样式也可以用在这里。最有用的应该是wx.TE_PASSWORD,它掩饰所输入的真实密码。你也可以使用wx.TE_MULTILINE来使用户能够在对话框中输入多行文本,也可以使用wx.TE_LEFT, wx.TE_CENTRE, 和 wx.TE_RIGHT来调整所输入的文本的对齐位置。

例9.3的最后显示了在文本框和对话框之间的另一区别。用户所输入的信息被存储在该对话框实例中,并且以后必须应用程序获取。在这种情况下,你可以使用对话框的GetValue()方法来得到该值。记住,如果用户按下Cancel(取消)去退出该对话框,这意味他们不想去使用他所键入的值。你也可以在程序中使用SetValue()方法来设置该值。

下面这些是使用文本对话框的便利函数:

1、wx.GetTextFromUser() 2、wx.GetPasswordFromUser() 3、wx.GetNumberFromUser()

其中和例9.3的用处最近似的是wx.GetTextFromUser():

   1 wx.GetTextFromUser(message, caption="Input text", 
   2     default_value="", parent=None)

这里的message, caption, default_value, 和 parent与wx.TextEntryDialog的构造函数中的一样。如果用户按下OK,该函数的返回值是用户所输入的字符串。如果用户按下Cancel,该函数返回空字符串。

如果你希望用户输入密码,你可以使用wx.GetPasswordFromUser()函数:

   1 wx.GetPasswordFromUser(message, caption="Input text", 
   2     default_value="", parent=None)

这里的参数意义和前面的一样。用户的输入被显示为星号,如果用户按下OK,该函数的返回值是用户所输入的字符串。如果用户按下Cancel,该函数返回空字符串。

最后,你可以使用wx.GetNumberFromUser()要求用户输入一个数字:

   1 wx.GetNumberFromUser(message, prompt, caption, value, min=0, 
   2     max=100, parent=None) 

这里的参数的意义有一点不同,message是显在prompt上部的任意长度的消息,value参数是默认显示在文本框中的长整型值。min和max参数为用户的输入限定一个范围。如果用户按下OK按钮退出的话,该方法返回所输入的值,并转换为长整型。如果这个值不能转换为一个数字,或不在指定的范围内,那么该函数返回-1,这意味如果你将该函数用于负数的范围的话,你可能要考虑一个转换的方法。

如何用对话框显示选项列表?

如果给你的用户一个空的文本输入域显得太自由了,那么你可以使用wx.SingleChoiceDialog来让他们在一组选项中作单一的选择。图9.4显示了一个例子。

图9.4 一个单选对话框

attachment:w9.4.gif

例9.4显示了产生图9.4的代码

例9.4 显示一个选择列表对话框

   1 import wx
   2 
   3 if __name__ == "__main__":
   4     app = wx.PySimpleApp()
   5     choices = ["Alpha", "Baker", "Charlie", "Delta"]
   6     dialog = wx.SingleChoiceDialog(None, "Pick A Word", "Choices",
   7             choices)
   8     if dialog.ShowModal() == wx.ID_OK:
   9         print "You selected: %s\n" % dialog.GetStringSelection()
  10 
  11     dialog.Destroy()
  12     
  13 wx.SingleChoiceDialog的构造函数如下所示
  14 
  15 wx.SingleChoiceDialog(parent, message, caption, choices, 
  16         clientData=None, style=wx.OK | wx.CANCEL | wx.CENTRE, 
  17         pos=wx.DefaultPosition) 

message和caption参数的意义与前面的一样,分别显示在对话框和标题栏中。choices参数要求一个字符串的列表,它们是你呈现在对话框中的选项。style参数有三个项,这是默认的,分别是OK按钮、Cancle按钮和使对话框在屏幕中居中。centre选项和pos参数在Windows操作系统上不工作。

如果你想在用户看见对话框之前,设置它的默认选项,使用SetSelection(selection)方法。参数selection是选项的索引值,而非实际选择的字符串。在用户选择了一个选项后,你即可以使用GetSelection()——它返回所选项的索引值,也可以使用GetStringSelection()——它返回实际所选的字符串,来得到它。

有两个用于单选对话框的便利函数。第一个是wx.GetSingleChoice,它返回用户所选的字符串:

wx.GetSingleChoice(message, caption, aChoices, parent=None)

参数message, caption, 和parent的意义和wx.SingleChoiceDialog构造函数的一样。aChoices参数是选项的列表。如果用户按下OK,则返回值是所选的字符串,如果用户按下Cancel,则返回值是空字符串。这意味如果空字符是一个有效的选择的话,那么你就不该使用这个函数。

第二个是wx.GetSingleChoiceIndex:

   1 wx.GetSingleChoiceIndex(message, caption, aChoices, parent=None)

这个函数与第一个有相同的参数,但是返回值不同。如果用户按下OK,则返回值是所选项的索引,如果用户按下Cancel,则返回值是-1。

如何显示进度条?

在许多程序中,程序需要自己做些事情而不受用户输入的干扰。这时就需要给用户一些可见的显示,以表明程序正在做一些事情及完成的进度。在wxPython中,这通常使用一个进度条来管理,如图9.5所示。

图9.5

attachment:w9.5.gif

例9.5显示了产生图9.5的代码

例9.5 生成一个进度条

   1 import wx
   2 
   3 if __name__ == "__main__":
   4     app = wx.PySimpleApp()
   5     progressMax = 100
   6     dialog = wx.ProgressDialog("A progress box", "Time remaining", progressMax,
   7             style=wx.PD_CAN_ABORT | wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME)
   8     keepGoing = True
   9     count = 0
  10     while keepGoing and count   progressMax:
  11         count = count + 1
  12         wx.Sleep(1)
  13         keepGoing = dialog.Update(count)
  14 
  15     dialog.Destroy()

进度条的所有选项在构造函数中被设置,构造函数如下:

   1 wx.ProgressDialog(title, message, maximum=100, parent=None, 
   2         style=wx.PD_AUTO_HIDE | wx.PD_APP_MODAL)

这些参数不同于其它对话框的。参数title被放置在窗口的标题栏,message被显示在对话框中。maximum是你用来显示进度计数的最大值。

表9.3 列出了特定于wx.ProgressDialog六个样式,它们影响进度条的行为。

表9.3 wx.ProgressDialog的样式

wx.PD_APP_MODAL

如果设置了这个样式,进度条对整个应用程序是模式的,这将阻塞所有的用户事件。如果没有设置这个样式,那么进度条仅对它的父窗口是模式的。

wx.PD_AUTO_HIDE

进度条将自动隐藏自身直到它达到它的最大值。

wx.PD_CAN_ABORT

在进度条上放上一个Cancel按钮,以便用户停止。如何响应来自该对话框的取消将在以后说明。

wx.PD_ELAPSED_TIME

显示该对话框已经出现了多长时间。

wx.PD_ESTIMATED_TIME

显示根据已花的时间、当前的计数值和计数器的最大值所估计出的完成进度所需的总时间。

wx.PD_REMAINING_TIME

显示要完成进度所估计的剩余时间,或(所需总时间-已花时间)。

要使用进度条,就要调用它的唯一的方法Update(value,newmsg="")。value参数是进度条的新的内部的值,调用update将导致进度条根据新的计数值与最大计算值的比例重绘。如果使用可选的参数newmsg,那么进度条上的文本消息将变为该字符串。这让你可以给用户一个关于当前进度的文本描述。

这个Update()方法通常返回True。但是,如果用户通过Cancel按钮已经取消了该对话框,那么下次的Update()将返回False。这是你响应用户的取消请求的机会。要检测用户的取消请求,我们建议你尽可能频繁地Update()。

使用标准对话框

大多数操作系统都为像文件选择、字体选择和颜色选择这些任务提供了标准对话框。这为平台提供了一致感观。你也可以使用来自于wxPython的这些对话框,它们也为你的应用程序提供了一致的感观。如果你使用wxPython,那么它为你提供了类似的对话框,即使所在的平台没有提供系统对话框。

如何使用文件选择对话框?

在wxPython中,wx.FileDialog为主流的平台使用本地操作系统对话框,对其它操作系统使用非本地相似的外观。微软Windows的版本如图9.6所示。

图9.6

attachment:w9.6.gif

你可以设置文件对话框开始在任一目录,你也可以使用通配符过滤来限制去显示某种文件类型。例9.6显示了一个基本的例子。

例9.6 使用wx.FileDialog

   1 import wx
   2 import os
   3 
   4 if __name__ == "__main__":
   5     app = wx.PySimpleApp()
   6     wildcard = "Python source (*.py)|*.py|" \
   7             "Compiled Python (*.pyc)|*.pyc|" \
   8             "All files (*.*)|*.*"
   9     dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), 
  10             "", wildcard, wx.OPEN)
  11     if dialog.ShowModal() == wx.ID_OK:
  12         print dialog.GetPath() 
  13 
  14     dialog.Destroy()

文件对话框是我们这章已见过的最复杂的对话框,它有几个属性可以通过编程的方式读写。它的构造函数使得你能够设置它的一些属性:

   1 wx.FileDialog(parent, message="Choose a file", defaultDir="", 
   2         defaultFile="", wildcard="*.*", style=0, 
   3         pos=wx.DefaultPosition)

message参数出现在窗口的标题栏中。defaultDir参数告诉对话框初始的时候显示哪个目录。如果这个参数为空或表示的目录不存在,那么对话框开始在当前目录。defaultFile是默认保存为的文件。wildcard参数使你可以基于给定的模式来过滤列表,使用通常的*和?作为通配符。通配符可以是单个模式,如*.py或格式如 描述 | 模式 | 描述 | 模式 的一系列模式——类似于例9.6中所用。

"Python source (*.py)|*.py|Compiled Python (*.pyc)|*.pyc|

  • All files (*.*)|*.*"

如果有一个多个项目的模式,那么它们显示在图9.6所示的下拉菜单中。pos参数不保证被基本的系统所支持。

选择一个文件

wx.FileDialog的两个最重要的样式标记是wx.OPEN和wx.SAVE,它们表明对话框的类型并影响对话框的行为。

用于打开文件的对话框有两个标记,它们进一步影响对话框的行为。wx.HIDE_READONLY标记灰化复选框,使用户以只读模式打开文件。wx.MULTIPLE标记使用户可以在一个目录中选择打开多个文件。

保存文件对话框有一个有用的标记wx.OVERWRITE_PROMPT,它使得保存文件时,如果有相同的文件存在,则提示用户是否覆盖。

两种文件对话框都可以使用wx.CHANGE_DIR标记。当使用这个标记时,文件的选择也可改变应用程序的工作目录为所选文件所在的目录。这使得下次文件对话框打开在相同的目录,而不需要应用程序再在别处存储该值。

和本章迄今为止我人们所见过的其它对话框不一样,文件对话框的属性directory,filename, style, message, 和wildcard是可以通过方法来得到和设置的。这些方法使用Get/Set命名习惯。

在用户退出对话框后,如果返回值是wx.OK,那么你可以使用方法GetPath()来得到用户的选择,该函数的返回值是字符串形式的文件全路径名。如果对话框是一个使用了wx.MULTIPLE标记的打开对话框,则用GetPaths()代替GetPath()。该方法返回路径字符串的一个Python列表。如果你需要知道在用户选择时使用了下拉菜单中的哪个项,你可以使用GetFilterIndex(),它返回项目的索引。要通过编程改变索引,使用方法SetFilterIndex()。

这后面的是一个使用文件对话框的便利函数:

   1 wx.FileSelector(message, default_path="", default_filename="", 
   2     default_extension="", wildcard="*.*'', flags=0, parent=None, 
   3     x=-1, y=-1)

message, default_path, default_filename, 和 wildcard参数意义与构造函数的基本相同,尽管参数的名字不同。flags参数通常被称作style,default_extension参数是保存为文件时默认的后缀(如果用户没有指定后缀的情况下)。如果用户按下OK,返回值是字符串形式的路径名,如果用户按下Cancel则返回一个空字符串。

选择一个目录

如果用户想去选择一个目录而非一个文件,使用wx.DirDialog,它呈现一个目录树的视图,如图9.7所示。

这个目录选择器比文件对话框简单些。例9.7显示了相关的代码。

例9.7 显示一个目录选择对话框

   1 import wx
   2 
   3 if __name__ == "__main__":
   4     app = wx.PySimpleApp()
   5     dialog = wx.DirDialog(None, "Choose a directory:",
   6           style=wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
   7     if dialog.ShowModal() == wx.ID_OK:
   8         print dialog.GetPath()
   9     dialog.Destroy()

图9.7

attachment:w9.7.gif

这个对话框的所有的功能几乎都在构造函数中:

   1 wx.DirDialog(parent, message="Choose a directory", defaultPath="", 
   2     style=0, pos = wx.DefaultPosition, size = wx.DefaultSize, 
   3     name="wxDirCtrl") 

由于message参数显示在对话框中,所以你不需要一个钩子去改变标题栏。defaultPath告诉对话框选择的默认路径,如果它为空,那么对话框显示文件系统的根目录。pos和size参数在微软Windows下被忽略,name参数在所有的操作系统下都被忽略。该对话框的样式标记wx.DD_NEW_DIR_BUTTON给对话框一个用于创建目录的一个按钮。这个标记在老版的微软Windows中不工作。

wx.DirDialog类的path, message, 和style属性都有相应的get*和set*方法。你可以使用GetPath()方法来在对话框被调用后获取用户的选择。这个对话框也有一个便利的函数:

   1 wx.DirSelector(message=wx.DirSelectorPromptStr, default_path="", 
   2     style=0, pos=wxDefaultPosition, parent=None)

所有的参数和前面的构造函数相同。如果OK被按下,则该函数返回所选择的字符串形式的目录名,如果按下Cancel,则返回空字符串。

如何使用字体选择对话框?

在wxPython中,字体选择对话框与文件对话框是不同的,因为它使用了一个单独的帮助类来管理它所呈现的信息。图9.8显示了微软Windows版的字体对话框。

例9.8 显示了产生图9.8的代码,并且与前面的对话框例子看起来也有些不同。

例9.8 字体对话框

   1 import wx
   2 
   3 if __name__ == "__main__":
   4     app = wx.PySimpleApp()
   5     dialog = wx.FontDialog(None, wx.FontData())
   6     if dialog.ShowModal() == wx.ID_OK:
   7         data = dialog.GetFontData()
   8         font = data.GetChosenFont()
   9         colour = data.GetColour()
  10         print 'You selected: "%s", %d points\n' % (
  11                 font.GetFaceName(), font.GetPointSize())
  12     dialog.Destroy()

图9.8

attachment:w9.8.gif

wx.FontDialog 的构造函数比前面的那些简单的多:

wx.FontDialog(parent, data)

你不能为该对话框设置一个消息或标题,并且被通常作为样式标记传递的信息被包含在data参数中,该参数是类wx.FontData。wx.FontData类自己只有一个有用的方法:GetFontData(),该方法返回字体数据的实例。

wx.FontData的实例使你能够设置管理字体对话框显示的值,并且也能够容纳用户输入的信息。例如,在例9.8中的代码调用了wx.FontData实例的两个get*方法来确定所选字体的细节。wx.FontData的构造函数没有参数——所有的属性必须通过使用表9.4中的方法来设置。

表9.4 wx.FontData的方法

GetAllowSymbols(),SetAllowSymbols(allowSymbols)

决定是否在对话框中仅显示符号字体(如dingbats)。参数是布尔值。只在Windows中有意义。该属性的初始值是True。

GetChosenFont(),SetChosenFont(font)

以wx.Font对象的方式返回用户所选的字体。如果用户选择了取消,那么返回None。wx.Font类将在第12章作更详细的讨论。

GetColour(),SetColour(colour)

返回在对话框的颜色选择部分所选的颜色。set*方法使你可以预先设定默认值。get*方法返回一个wx.Colour实例。set*方法中的colour只能是一个wx.Colour或一个颜色的字符串名。该属性的初始值是black。

GetEnableEffects(),EnableEffects(enable)

在该对话框的Windows版本中,该属性控制是否显示字体的所选颜色、中间是否有直线通过、是否带下划线等特性。

GetInitialFont(),SetInitialFont(font)

返回对话框初值的字体值(即当前所用的字体)。这个属性可以在对话框显示之前通过应用程序显式的来设置。它的初始值是None。

SetRange(min, max)

设置字体尺寸(磅)的有效范围。仅用于微软的Windows系统。最初值是0~0,意味没有范围的限制。

||GetShowHelp(),SetShowHelp()||如果为True,那么该对话框的微软Windows版本将显示一个帮助按钮。初始值为False||。

有一个使用字体对话框的便利的函数,它回避了wx.FontData类:

   1 wx.GetFontFromUser(parent, fontInit)

fontInit参数是wx.Font的一个实例,它用作对话框的初始值。该函数的返回值是一个wx.Font实例。如用户通过OK关闭了对话框,则方法wx.Font.Ok()返回True,否则返回False。

如何使用颜色对话框?

颜色对话框类似于字体对话框,因为它使用了一个外部的数据类来管理它的信息。图9.9显示了这类对话框的微软版本。

例9.9显示了生成该对话框的代码,它几乎与前面的字体对话框相同。

例9.9

   1 import wx
   2 
   3 if __name__ == "__main__":
   4     app = wx.PySimpleApp()
   5     dialog = wx.ColourDialog(None)
   6     dialog.GetColourData().SetChooseFull(True)
   7     if dialog.ShowModal() == wx.ID_OK:
   8         data = dialog.GetColourData()
   9         print 'You selected: %s\n' % str(data.GetColour().Get())
  10     dialog.Destroy()

图9.9

attachment:w9.9.gif

用于颜色选择器的wxPython的类是wx.ColourDialog。它的构造函数很简单,没有太多的参数:

wx.ColourDialog(parent, data=None)

data参数是类wx.ColourData的实例,它比相应字体的更简单。它只包含默认的没有参数的构造函数和后面的三个属性:

1、GetChooseFull/SetChooseFull(flag):仅在微软Windows下工作。当设置后,将显示完整的对话框,包括自定义颜色选择器。如果不设置,则自定义颜色选择器不被显示。

2、GetColour/SetColour(colour):当图表被关闭后,调用get*来看用户的选择。最初它被设置为black。如果在对话框显示之前设置了它,那么对话框最初显示为该颜色。

3、GetCustomColour(i)/SetCustomColour(i, colour):根据自定义的颜色数组中的索引i来返回或设置元素。i位于[0,15]之间。初始时,所有的自定义颜色都是白色。

一个回避了wx.ColorData的使用颜色对话框的便利函数是:

wx.GetColourFromUser(parent, colInit)

colInit是wx.Colour的一个实例,并且当对话框显示时它是对话框的初始的值。函数的返回值也是一个wx.Colour的实例。如果用户通过OK关闭了对话框,那么方法wx.Colour.OK()返回True。如果用户通过Cancel关闭了对话框,那么方法wx.Colour.OK()返回False。

如何使用户能够浏览图像?

如果你在你的程序中做图形处理,那么在他们浏览文件树时使用缩略图是有帮助的。用于该目的的wxPython对话框被称为wx.lib.imagebrowser.ImageDialog。图9.10显示了一个例子。

例9.10显示了用于该图像浏览对话框的简单的代码。

例9.10 创建一个图像浏览对话框

   1 import wx
   2 import wx.lib.imagebrowser as imagebrowser
   3 
   4 if __name__ == "__main__":
   5     app = wx.PySimpleApp()
   6     dialog = imagebrowser.ImageDialog(None)   
   7     if dialog.ShowModal() == wx.ID_OK:
   8         print "You Selected File: " + dialog.GetFile()  
   9     dialog.Destroy()

图9.10

attachment:w9.10.gif

wx.lib.imagebrowser.ImageDialog类是十分简单的,并有相对较少的选项供程序员去设置。要改变该对话框的行为的话,请查阅改变显示的文件类型的Python源码。类的构造函数要求两个参数。

ImageDialog(parent, set_dir=None)

set_dir参数是对话框显示时所在的目录。如果不设置,那么使用应用程序当前的工作目录。在对话框被关闭后,GetFile()返回所选文件的完整路径字符串,GetDirectory()只返回目录部分。

创建向导

向导是一系列被链接在一起的简单对话框,它使得用户一步步地跟随它们。通常它们被用于指导用户的安装或一个复杂的配置过程。图9.11显示了一个向导示例。

图9.11

attachment:w9.11.gif

在wxPython中,一个向导是一系列的页面,它由类wx.wizard.Wizard的一个实例控制。向导实例管理用户的页面切换事件。这些页面自身也是类wx.wizard.WizardPageSimple或wx.wizard.WizardPage的实例。这两种类的实例,它们只不过是附加了必要的管理页面链接逻辑的wx.Panel的实例。已证明这两个实例之间的区别仅当用户按下Next按钮时。wx.wizard.WizardPage的实例使你能够动态地决定浏览哪页,而wx.wizard.WizardPageSimple的实例要求向导被显示前,顺序被预先设置。例9.11显示了产生图9.11的代码。

例9.11 创建一个简单的静态向导

   1 import wx
   2 import wx.wizard
   3 
   4 class TitledPage(wx.wizard.WizardPageSimple):#1 创建页面样板
   5     def __init__(self, parent, title):
   6         wx.wizard.WizardPageSimple.__init__(self, parent)
   7         self.sizer = wx.BoxSizer(wx.VERTICAL)
   8         self.SetSizer(self.sizer)
   9         titleText = wx.StaticText(self, -1, title)
  10         titleText.SetFont(
  11                 wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
  12         self.sizer.Add(titleText, 0,
  13                 wx.ALIGN_CENTRE | wx.ALL, 5)
  14         self.sizer.Add(wx.StaticLine(self, -1), 0,
  15                 wx.EXPAND | wx.ALL, 5)
  16 
  17 if __name__ == "__main__":
  18     app = wx.PySimpleApp()
  19     wizard = wx.wizard.Wizard(None, -1, "Simple Wizard")# 创建向导实例
  20 
  21     # 创建向导页面
  22     page1 = TitledPage(wizard, "Page 1")
  23     page2 = TitledPage(wizard, "Page 2")
  24     page3 = TitledPage(wizard, "Page 3")
  25     page4 = TitledPage(wizard, "Page 4")
  26 
  27     page1.sizer.Add(wx.StaticText(page1, -1,
  28             "Testing the wizard"))
  29     page4.sizer.Add(wx.StaticText(page4, -1,
  30             "This is the last page."))
  31 
  32     #2 创建页面链接
  33     wx.wizard.WizardPageSimple_Chain(page1, page2)
  34     wx.wizard.WizardPageSimple_Chain(page2, page3)
  35     wx.wizard.WizardPageSimple_Chain(page3, page4)
  36 
  37     wizard.FitToPage(page1)#3 调整向导的尺寸
  38 
  39     if wizard.RunWizard(page1):#4 运行向导
  40         print "Success"
  41 
  42     wizard.Destroy()

#1 为了便于移植的目的,我们创建了一个简单的小的页面,它包含了一个静态文本标题。通常情况下,这儿还包含一些表单元素,可能还有一些要用户输入的数据。

#2 wx.wizard.WizardPageSimple_Chain()函数是一个便利的方法,它以两个页面为参数相互地调用它们的SetNext()和SetPrev()方法。

#3 FitToSize()根据页面参数及该页链条上的所有页面调整向导的大小。该方法只能在页面链接被创建后调用。

#4 该方法的参数是向导开始时的页面。向导在它到达一个没有下一页的页面时知道去关闭。如果用户浏览了整个向导并通过按下Finish按钮退出的话,RunWizard()方法返回True。

创建wx.wizard.Wizard实例是使用向导的第一步。其构造函数如下:

   1 wx.wizard.Wizard(parent, id=-1, title=wx.EmptyString, 
   2     bitmap=wx.NullBitmap, pos=wx.DefaultPosition) 

在这里的parent, id, title, pos和wx.Panel的意义相同。如果设置了bitmap参数,那么该参数将显示在每一页上。这儿只有一个样式标记:wx.wizard.WIZARD_EX_HELPBUTTON,它显示一个帮助按钮。这是一个扩展的标记,需要使用第8章所说的两步创建过程。

通常,你将调用例9.11的#3中所示的FitToSize()来管理窗口的尺寸,但是你也可以通过调用带有一个元组或wx.Size实例的SetPageSize()来设置一个最小的尺寸。GetPageSize()方法返回当前的尺寸,在这两种情况下,该尺寸仅用于对话框中的单个的页面部分,而作为一个整体的对话框将更大一些。

你可以管理该类中的页面。方法GetCurrentPage()返回当前被显示的页面,如果该向导当前没有被显示,该方法返回None。你可以通过调用HasNextPage()和HasPrevPage()来确定当前页是否有下一页或上一页。 使用RunWizard()来运行该向导,如例9.11中#4的说明。

向导产生的命令事件如表9.5所示,你可以捕获这些事件,以便作专门的处理。这些事件对象属于类wx.wizard.WizardEvent,它们提供了两个可用的方法。GetPage()返回wx.WizardPage的实例,该实例在向导事件产生时是有效,而非作为事件结果被显示的实例。如果事件是前进一页,那么GetDirection()返回True,如果事件是后退一页,那么GetDirection()返回False。

表9.5 wx.wizard.WizardDialog的事件

EVT_WIZARD_CANCEL

当用户按下Cancel按钮时产生。该事件可以使用Veto()来否决,这种情况下,对话框将不会消失。

EVT_WIZARD_FINISHED

当用户按下Finish按钮时产生。

EVT_WIZARD_HELP

当用户按下Help按钮时产生。

EVT_WIZARD_PAGE_CHANGED

在页面被切换后产生。

EVT_WIZARD_PAGE_CHANGING

当用户已请求了一个页面切换时产生,这时页面还没有发生切换。这个事件可以被否决(例如,如果页面上有一个必须被填写的字段)。

wx.wizard.WizardPageSimple类被当作一个面板一样。它的构造函数使你可以设置上一页和下一页,如下所示:

wx.wizard.WizardPageSimple(parent=None, prev=None, next=None)

如果你想在构造器中设置它们,你可以使用SetPrev()和SetNext()方法。如果那样太麻烦,你可以使用wx.wizard.WizardPageSimple_Chain(),它设置两页间的链接关系。

向导页的复杂版:wx.wizard.WizardPage,稍微不同。它没有显式地设置前一页和下一页,而是使你能够使用更复杂的逻辑去定义下一步到哪儿。它的构造函数如下:

   1 wx.WizardPage(parent, bitmap=wx.NullBitmap, resource=None)

如果bitmap参数被设置了,那么该参数覆盖父向导中所设置的位图。resource参数从一个wxPython资源装载页面。要处理页面逻辑,就要覆盖GetPrev()和GetNext()方法来返回你想要向导下一步的位置。该类的一个典型的用法是根据用户对当前页的响应动态地决定接下来的页面。

显示启动提示

许多应用程序都使用启动提示来作为一种向用户介绍该程序的特性等信息的方法。在wxPython中有一个非常简单的机制用来显示启动提示。图9.12显示了一个提示窗口的示例。

图9.12

attachment:w9.12.gif

例9.12显示了相关代码

例9.12

   1 import wx
   2 
   3 if __name__ == "__main__":
   4     app = wx.PySimpleApp()
   5     provider = wx.CreateFileTipProvider("tips.txt", 0)
   6     wx.ShowTip(None, provider, True)

有两个便利的函数用来管理启动提示。第一个如下创建一个wx.TipProvider

   1 wx.CreateFileTipProvider(filename, currentTip)

filename是包含提示字符串的文件的名字。currentTip是该文件中用于一开始显示的提示字符的索引,该文件中的第一个提示字符串的索引是0。

提示文件是一个简单的文本文件,其中的每一行是一个不同的提示。空白行被忽略,以#开始的行被当作注释并也被忽略。下面是上例所使用的提示文件中的内容:

You can do startup tips very easily. Feel the force, Luke.

提示的提供者(provider)是类wx.PyTipProvider的一个实例。如果你需要更细化的功能,你可以创建你自己的wx.TipProvider的子类并覆盖GetTip()函数。

显示提示的函数是wx.ShowTip():

   1 wx.ShowTip(parent, tipProvider, showAtStartup)

parent是父窗口,tipProvider通常创建自wx.CreateFileTipProvider。showAtStartup控制启动提示显示时,复选框是否被选择。该函数的返回值是复选框的状态值,以便你使用该值来决定你的应用程序下次启动时是否显示启动提示。

使用验证器(validator)来管理对话框中的数据

验证器是一个特殊的wxPython对象,它简化了对话框中的数据管理。当我们在第三章中讨论事件时,我们简要的提及到如果一个窗口部件有一个验证器,那么该验证器能够被事件系统自动调用。我们已经见过了几个wxPython窗口部件类的构造函数中将验证器作为参数,但是我们还没有讨论它们。

验证器有三个不相关的功能:

1、在对话框关闭前验证控件中的数据 2、自动与对话框传递数据 3、验证用户键入的数据

如何使用验证器来确保正确的数据?

验证器对象是wx.Validator的子类。父类是抽象的,不能直接使用。尽管在C++ wxWidget集中有一对预定义的验证器类,但是在wxPython中,你需要定义你自己的验证器类。正如我们在别处所见的,你的Python类需要继承自Python特定的子类:wx.PyValidator,并且能够覆盖该父类的所有方法。一个自定义的验证器子类必须覆盖方法Clone(),该方法应该返回验证器的相同的副本。

一个验证器被关联到你的系统中的一个特定的窗口部件。这可以用两种方法之一来实现。第一种方法,如果窗口部件许可的话,验证器可以被作为一个参数传递给该窗口部件的构造函数。如果该窗口部件的构造函数没有一个验证器参数,你仍然可以通过创建一个验证器实例并调用该窗口部件的SetValidator(validator)方法来关联一个验证器。

要验证控件中的数据,你可以先在你的验证器子类中覆盖Validate(parent)方法。parent参数是验证器的窗口部件的父窗口(对话框或面板)。如果必要,可以使用这个来从对话框中其它窗口部件得到数据,或者你可以完全忽略该参数。你可以使用self.GetWindow()来得到正在被验证的窗口部件的一个引用。你的Validate(parent)方法的返回值是一个布尔值。True值表示验证器的窗口部件中的数据已验证了。False表示有问题。你可以根据Validate()方法来使用x.MessageBox()去显示一个警告,但是你不应该做其它的任何可以在wxPython应用程序中引发事件的事情。

Validate()的返回值是很重要的。它在你使用OK按钮(该按钮使用wx.ID_OK ID)企图关闭一个对话框时发挥作用。作为对OK按钮敲击处理的一部分,wxPython调用对话框中有验证器的窗口部件的Validate()函数。如果任一验证器返回False,那么对话框不将关闭。例9.13显示了一个带有验证器的示例对话框,它检查所有文本控件中有无数据。

例9.13 检查所有文本控件有无数据的验证器

   1 import wx
   2 
   3 about_txt = """\
   4 The validator used in this example will ensure that the text
   5 controls are not empty when you press the Ok button, and
   6 will not let you leave if any of the Validations fail."""
   7 
   8 
   9 class NotEmptyValidator(wx.PyValidator):# 创建验证器子类
  10      def __init__(self):
  11          wx.PyValidator.__init__(self)
  12 
  13      def Clone(self):
  14          """
  15          Note that every validator must implement the Clone() method.
  16          """
  17          return NotEmptyValidator()
  18 
  19      def Validate(self, win):#1 使用验证器方法
  20          textCtrl = self.GetWindow()
  21          text = textCtrl.GetValue()
  22 
  23          if len(text) == 0:
  24              wx.MessageBox("This field must contain some text!", "Error")
  25              textCtrl.SetBackgroundColour("pink")
  26              textCtrl.SetFocus()
  27              textCtrl.Refresh()
  28              return False
  29          else:
  30              textCtrl.SetBackgroundColour(
  31                  wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
  32              textCtrl.Refresh()
  33              return True
  34 
  35      def TransferToWindow(self):
  36          return True 
  37 
  38      def TransferFromWindow(self):
  39          return True
  40 
  41 
  42 
  43 class MyDialog(wx.Dialog):
  44     def __init__(self):
  45         wx.Dialog.__init__(self, None, -1, "Validators: validating")
  46 
  47         # Create the text controls
  48         about   = wx.StaticText(self, -1, about_txt)
  49         name_l  = wx.StaticText(self, -1, "Name:")
  50         email_l = wx.StaticText(self, -1, "Email:")
  51         phone_l = wx.StaticText(self, -1, "Phone:")
  52 
  53         #2 使用验证器
  54         name_t  = wx.TextCtrl(self, validator=NotEmptyValidator())
  55         email_t = wx.TextCtrl(self, validator=NotEmptyValidator())
  56         phone_t = wx.TextCtrl(self, validator=NotEmptyValidator())
  57 
  58         # Use standard button IDs
  59         okay   = wx.Button(self, wx.ID_OK)
  60         okay.SetDefault()
  61         cancel = wx.Button(self, wx.ID_CANCEL)
  62 
  63         # Layout with sizers
  64         sizer = wx.BoxSizer(wx.VERTICAL)
  65         sizer.Add(about, 0, wx.ALL, 5)
  66         sizer.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5)
  67         
  68         fgs = wx.FlexGridSizer(3, 2, 5, 5)
  69         fgs.Add(name_l, 0, wx.ALIGN_RIGHT)
  70         fgs.Add(name_t, 0, wx.EXPAND)
  71         fgs.Add(email_l, 0, wx.ALIGN_RIGHT)
  72         fgs.Add(email_t, 0, wx.EXPAND)
  73         fgs.Add(phone_l, 0, wx.ALIGN_RIGHT)
  74         fgs.Add(phone_t, 0, wx.EXPAND)
  75         fgs.AddGrowableCol(1)
  76         sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5)
  77 
  78         btns = wx.StdDialogButtonSizer()
  79         btns.AddButton(okay)
  80         btns.AddButton(cancel)
  81         btns.Realize()
  82         sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5)
  83 
  84         self.SetSizer(sizer)
  85         sizer.Fit(self)
  86         
  87 
  88 app = wx.PySimpleApp()
  89 
  90 dlg = MyDialog()
  91 dlg.ShowModal()
  92 dlg.Destroy()
  93 
  94 app.MainLoop()

#1 该方法测试基本的控件有无数据。如果没有,相应的控件的背景色变为粉红色。

#2 这几行,对话框中的每个文本控件都关联一个验证器。

图9.13显示了一个文本域为空就企图关闭的对话框。

图9.13

attachment:w9.13.gif

明确地告诉对话框去核对验证器的代码没有出现在示例中,因为它是wxPython事件系统的一部分。对话框与框架之间的另一区别是对话框有内建的验证器行为,而框架没有。如果你喜欢将验证器用于不在对话框内的控件,那么调用父窗口的Validate()方法。如果父窗口已设置了wx.WS_EX_VALIDATE_RECURSIVELY额外样式,那么所有的子窗口的Validate()方法也被调用。如果任一验证失败,那么Validate返回False。接下来,我们将讨论如何将验证器用于数据传输。

如何使用验证器传递数据?

验证器的第二个重要的功能是,当对话框打开时,它自动将数据传送给对话框显示,当该对话框关闭时,自动从对话框把数据传输到一个外部资源。图9.14显示了一个示例对话框。

图9.14

attachment:w9.14.gif

要实现这个,你必须在你的验证器子类中覆盖两个方法。方法TransferToWindow()在对话框打开时自动被调用。你必须使用这个方法把数据放入有验证器的窗口部件。TransferFromWindow()方法在使用OK按钮关闭对话框窗口时且数据已被验证后被自动调用。你必须使用这个方法来将数据从窗口部件移动给其它的资源。

数据传输必须发生的事实意味着验证器必须对一个外部的数据对象有一些了解,如例9.14所示。在这个例子中,每个验证器都使用一个全局数据字典的引用和一个字典内的对于相关控件重要的关键字来被初始化。

当对话框打开时,TransferToWindow()方法从字典中根据关键字读取数据并把数据放入文本域。当对话框关闭时,TransferFromWindow()方法反向处理并把数据写入字典。这个例子的对话框显示你传输的数据。

例9.14 一个数据传输验证器

   1 import wx
   2 import pprint
   3 
   4 about_txt = """\
   5 The validator used in this example shows how the validator
   6 can be used to transfer data to and from each text control
   7 automatically when the dialog is shown and dismissed."""
   8 
   9 
  10 class DataXferValidator(wx.PyValidator):# 声明验证器
  11      def __init__(self, data, key):
  12          wx.PyValidator.__init__(self)
  13          self.data = data
  14          self.key = key
  15 
  16      def Clone(self):
  17          """
  18          Note that every validator must implement the Clone() method.
  19          """
  20          return DataXferValidator(self.data, self.key)
  21 
  22      def Validate(self, win):# 没有验证数据
  23          return True
  24 
  25      def TransferToWindow(self):# 对话框打开时被调用
  26          textCtrl = self.GetWindow()
  27          textCtrl.SetValue(self.data.get(self.key, ""))
  28          return True 
  29 
  30      def TransferFromWindow(self):# 对话框关闭时被调用
  31          textCtrl = self.GetWindow()
  32          self.data[self.key] = textCtrl.GetValue()
  33          return True
  34 
  35 
  36 
  37 class MyDialog(wx.Dialog):
  38     def __init__(self, data):
  39         wx.Dialog.__init__(self, None, -1, "Validators: data transfer")
  40 
  41         # Create the text controls
  42         about   = wx.StaticText(self, -1, about_txt)
  43         name_l  = wx.StaticText(self, -1, "Name:")
  44         email_l = wx.StaticText(self, -1, "Email:")
  45         phone_l = wx.StaticText(self, -1, "Phone:")
  46 
  47         # 将验证器与窗口部件相关联
  48         name_t  = wx.TextCtrl(self, validator=DataXferValidator(data, "name"))
  49         email_t = wx.TextCtrl(self, validator=DataXferValidator(data, "email"))
  50         phone_t = wx.TextCtrl(self, validator=DataXferValidator(data, "phone"))
  51 
  52         # Use standard button IDs
  53         okay   = wx.Button(self, wx.ID_OK)
  54         okay.SetDefault()
  55         cancel = wx.Button(self, wx.ID_CANCEL)
  56 
  57         # Layout with sizers
  58         sizer = wx.BoxSizer(wx.VERTICAL)
  59         sizer.Add(about, 0, wx.ALL, 5)
  60         sizer.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5)
  61         
  62         fgs = wx.FlexGridSizer(3, 2, 5, 5)
  63         fgs.Add(name_l, 0, wx.ALIGN_RIGHT)
  64         fgs.Add(name_t, 0, wx.EXPAND)
  65         fgs.Add(email_l, 0, wx.ALIGN_RIGHT)
  66         fgs.Add(email_t, 0, wx.EXPAND)
  67         fgs.Add(phone_l, 0, wx.ALIGN_RIGHT)
  68         fgs.Add(phone_t, 0, wx.EXPAND)
  69         fgs.AddGrowableCol(1)
  70         sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5)
  71 
  72         btns = wx.StdDialogButtonSizer()
  73         btns.AddButton(okay)
  74         btns.AddButton(cancel)
  75         btns.Realize()
  76         sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5)
  77 
  78         self.SetSizer(sizer)
  79         sizer.Fit(self)
  80         
  81 
  82 app = wx.PySimpleApp()
  83 
  84 data = { "name" : "Jordyn Dunn" }
  85 dlg = MyDialog(data)
  86 dlg.ShowModal()
  87 dlg.Destroy()
  88 
  89 wx.MessageBox("You entered these values:\n\n" +
  90               pprint.pformat(data))
  91 
  92 app.MainLoop()

对话框中验证器的传输数据方法的调用自动发生。要在非对话框窗口中使用验证器来传输数据,必须调用父窗口部件的TransDataFromWindow()和TransferDataToWindow()方法。如果该窗口设置了wx.WS_EX_VALIDATE_RECURSIVELY额外样式,那么在所有的子窗口部件上也将调用该传输函数。

如何在数据被键入时验证数据?

在数据被传给窗口部件之前,你也可使用验证器来在用户输入数据时验证所输入的数据。这是非常有用的,因为它可以防止将得到的坏的数据传入你的应用程序。图9.12显示了一个例子,其中对话框的文本阐明了该思想。

图9.15

attachment:w9.15.gif

验证数据的方法的自动化成份少于其它的机制。你必须显式绑定验证器的窗口部件的字符事件给一个函数,如下所示:

self.Bind(wx.EVT_CHAR, self.OnChar)

该窗口部件假设事件源属于验证器。例9.15显示了这个绑定。

例9.15 实时验证

   1 import wx
   2 import string
   3 
   4 about_txt = """\
   5 The validator used in this example will validate the input on the fly
   6 instead of waiting until the okay button is pressed.  The first field
   7 will not allow digits to be typed, the second will allow anything
   8 and the third will not allow alphabetic characters to be entered.
   9 """
  10 
  11 
  12 class CharValidator(wx.PyValidator):
  13     def __init__(self, flag):
  14          wx.PyValidator.__init__(self)
  15          self.flag = flag
  16          self.Bind(wx.EVT_CHAR, self.OnChar)# 绑定字符事件
  17 
  18     def Clone(self):
  19          """
  20          Note that every validator must implement the Clone() method.
  21          """
  22          return CharValidator(self.flag)
  23 
  24     def Validate(self, win):
  25          return True
  26 
  27     def TransferToWindow(self):
  28          return True 
  29 
  30     def TransferFromWindow(self):
  31          return True
  32 
  33     def OnChar(self, evt):# 数据处理
  34          key = chr(evt.GetKeyCode())
  35          if self.flag == "no-alpha" and key in string.letters:
  36               return
  37          if self.flag == "no-digit" and key in string.digits:
  38               return
  39          evt.Skip()
  40 
  41 
  42 class MyDialog(wx.Dialog):
  43     def __init__(self):
  44         wx.Dialog.__init__(self, None, -1, "Validators: behavior modification")
  45 
  46         # Create the text controls
  47         about   = wx.StaticText(self, -1, about_txt)
  48         name_l  = wx.StaticText(self, -1, "Name:")
  49         email_l = wx.StaticText(self, -1, "Email:")
  50         phone_l = wx.StaticText(self, -1, "Phone:")
  51 
  52         # 绑定验证器
  53         name_t  = wx.TextCtrl(self, validator=CharValidator("no-digit"))
  54         email_t = wx.TextCtrl(self, validator=CharValidator("any"))
  55         phone_t = wx.TextCtrl(self, validator=CharValidator("no-alpha"))
  56 
  57         # Use standard button IDs
  58         okay   = wx.Button(self, wx.ID_OK)
  59         okay.SetDefault()
  60         cancel = wx.Button(self, wx.ID_CANCEL)
  61 
  62         # Layout with sizers
  63         sizer = wx.BoxSizer(wx.VERTICAL)
  64         sizer.Add(about, 0, wx.ALL, 5)
  65         sizer.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5)
  66         
  67         fgs = wx.FlexGridSizer(3, 2, 5, 5)
  68         fgs.Add(name_l, 0, wx.ALIGN_RIGHT)
  69         fgs.Add(name_t, 0, wx.EXPAND)
  70         fgs.Add(email_l, 0, wx.ALIGN_RIGHT)
  71         fgs.Add(email_t, 0, wx.EXPAND)
  72         fgs.Add(phone_l, 0, wx.ALIGN_RIGHT)
  73         fgs.Add(phone_t, 0, wx.EXPAND)
  74         fgs.AddGrowableCol(1)
  75         sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5)
  76 
  77         btns = wx.StdDialogButtonSizer()
  78         btns.AddButton(okay)
  79         btns.AddButton(cancel)
  80         btns.Realize()
  81         sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5)
  82 
  83         self.SetSizer(sizer)
  84         sizer.Fit(self)
  85         
  86 
  87 app = wx.PySimpleApp()
  88 
  89 dlg = MyDialog()
  90 dlg.ShowModal()
  91 dlg.Destroy()
  92 
  93 app.MainLoop()

由于OnChar()方法是在一个验证器中,所以它在窗口部件响应字符事件之间被调用。该方法让你可以通过使用Skip()来将事件传送给窗口部件。你必须调用Skip(),否则验证器将妨碍正常的事件处理。验证器执行一个测试来查看用于该控件的字符是否有效。如果该字符无效,那么Skip()不被调用,并且事件处理停止。如果有必须的话,除了wx.EVT_CHAR之外的其它事件也可以被绑定,并在窗口部件响应之前验证器处理那些事件。

对于处理你wxPython应用程序中的数据,验证器是一个强大且灵活的机制。适当地使用它们,可以让你的应用程序的开发和维护更加的顺畅。

本章小结

1、对话框被用于在有一套特殊的信息需要被获取的情况下,处理与用户的交互,这种交互通常很快被完成。在wxPython中,你可以使用通用的wx.Dialog类来创建你自己的对话框,或者你也可以使用预定义的对话框。大多数情况下,通常被使用的对话框都有相应的便利函数,使得这种对话框的使用更容易。

2、对话框可以显示为模式对话框,这意味在对话框可见时,该应用程序中的用户所有的其它输入将被阻塞。模式对话框通过使用ShowModal()方法来被调用,它的返回值依据用户所按的按钮而定(OK或Cancel)。关闭模式对话框并不会销毁它,该对话框的实例可以被再用。

3、在wxPython中有三个通用的简单对话框。wx.MessageDialog显示一个消息对话框。wx.TextEntryDialog使用户能够键入文本,wx.SingleChoiceDialog给用户一个基于列表项的选择。

4、当正在执行一个长时间的后台的任务时,你可以使用wx.ProgressDialog来给用户显示进度信息。用户可以通过wx.FileDialog使用标准文件对话框来选择一个文件。可以使用wx.DirDialog来创建一个标准目录树,它使得用户可以选择一个目录。

5、你可以使用wx.FontDialog和wx.ColorDialog来访问标准的字体选择器和颜色选择器。在这两种情况中,对话框的行为和用户的响应是由一个单独的数据类来控制的。

6、要浏览缩略图,可以使用wxPython特定的类wx.lib.imagebrowser.ImageDialog。这个类使用户能够通过文件系统并选择一个图像。

7、你可以通过使用wx.wizard.Wizard创建一个向导来将一组相关的对话框表单链接起来。对话框表单是wx.wizard.WizardSimplePage或wx.wizard.WizardPage的实例。两者的区别是,wx.wizard.WizardSimplePage的页到页的路线需要在向导被显示之前就安排好,而wx.wizard.WizardPage使你能够在运行时管理页到页的路线的逻辑。

8、使用wx.CreateFileTipProvider和wx.ShowTip函数可以很容易地显示启动提示。

9、验证器是很有用的对象,如果输入的数据不正确的话,它可以自动阻止对话框的关闭。他们也可以在一个显示的对话框和一个外部的对象之间传输数据,并且能够实时地验证数据的输入。

WxPythonInAction/ChapterNine (last edited 2009-12-25 07:16:18 by localhost)