天天看点

wxpython入门(二)基本窗体事件了解

wxpython入门(二)基本窗体事件了解

参考书籍

http://wiki.woodpecker.org.cn/moin/WxPythonInAction

第一章 欢迎使用wxPython

开始wxPython

1.创建最小的空的wxPython

#coding=utf-8

'''

Created on 2010-3-27

@author: sillycat

'''

import wx

class App(wx.App):

def OnInit(self):

frame = wx.Frame(parent=None, title='Bare')

frame.Show()

return True

app = App()

app.MainLoop()

所做的几个事情:

导入了必要的wx包

定义了初始化方法

进入了主事件循环

wx的导入有一定的顺序,比如

import wx

from wx import xrc

必须要先导入wx,再导入xrc

每个wxPython程序必须有一个application对象和至少一个frame对象。application对象必须是wx.App的一个实例或你在OnInit()方法中定义的一个子类的一个实例。当你的应用程序启动的时候,OnInit()方法将被wx.App父类调用。

frame的API

frame.Show(False) # 使框架不可见.

frame.Show(True) # True是默认值,使框架可见.

frame.Hide() # 等同于frame.Show(False)

我们没有为我们的应用程序类定义一个__init__()方法。在Python中,这就意味着父方法wx.App.__init()__将在对象创建时被自动调用。这是一个好的事情。如果你定义你自己的__init__()方法,不要忘了调用其基类的__init()__方法,示例如下:

class App(wx.App):

def __init__(self):

wx.App.__init__(self)

2.扩展这个wxpython程序

第二章 给wxPython程序一个坚实的基础

两个基础对象:

应用程序对象管理主事件循环,主事件循环是你的wxPython程序的动力。启动主事件循环是应用程序对象的工作。没有应用程序对象,你的wxPython应用程序将不能运行。

顶级窗口通常管理最重要的数据,控制并呈现给用户。

创建一个wx.App的子类

1、定义这个子类

2、在定义的子类中写一个OnInit()方法

3、在你的程序的主要部分创建这个类的一个实例

4、调用应用程序实例的MainLoop()方法。这个方法将程序的控制权转交给wxPython

何时省略wx.App的子类

如果在系统中只有一个框架的话,避免创建一个wx.App子类是一个好的主意,在这种情况下,wxPython提供了一个方便的类wx.PySimpleApp。

import wx

if __name__ == '__main__':

app = wx.PySimpleApp()

frame = wx.Frame(parent=None, title='Bare')

frame.Show(True)

app.MainLoop()

理解应用程序对象的生命周期

创建应用程序对象触发OnInit()方法并允许新的窗口对象被创建。在OnInit()之后,这个脚本调用MainLoop()方法,通知wxPython事件现在正在被处理。在窗口被关闭之前应用程序继续它的事件处理。当所有顶级窗口被关闭后,MainLoop()函数返回同时应用程序对象被注销。这之后,这个脚本能够关闭其它的可能存丰的连接或线程。

Script start ---->Application object created ---------------->MainLoop() called-------->Application object destroyed----->Script end

Automatically calls OnInit() method Events are processed this happens after all top-level windows are closed

window objects can be created

定向wxPython程序的输出

Python程序都能够通过两种标准流来输出文本:分别是标准输出流sys.stdout和标准错误流sys.stderr。

import wx

import sys

class Frame(wx.Frame):

def __init__(self, parent, id, title):

print "Frame __init__"

wx.Frame.__init__(self, parent, id, title)

class App(wx.App):

def __init__(self, redirect=True, filename=None):

print "App __init__"

wx.App.__init__(self, redirect, filename)

def OnInit(self):

print "OnInit" #输出到stdout

self.frame = Frame(parent=None, id=-1, title='Startup') #创建框架

self.frame.Show()

self.SetTopWindow(self.frame)

print sys.stderr, "A pretend error message" #输出到stderr

return True

def OnExit(self):

print "OnExit"

if __name__ == '__main__':

app = App(redirect=True) #1 文本重定向从这开始

print "before MainLoop"

app.MainLoop() #2 进入主事件循环

print "after MainLoop"

所有发送到stderr或stdout的文本都可被wxPython重定向到一个框架。参数redirect=True决定了是否重定向。

如果我们将上例中的app = App(redirect=True)改为app = App(False),则输出将全部到控制台

如何关闭wxPython应用程序

管理正常关闭

由于你的wx.App子类的OnExit()方法在最后一个窗口被关闭后且在wxPython的内在的清理过程之前被调用,你可以使用OnExit()方法来清理你创建的任何非wxPython资源(例如一个数据库连接)。即使使用了wx.Exit()来关闭wxPython程序,OnExit()方法仍将被触发。

管理紧急关闭

两种在紧急情况下退出你的wxPython应用程序的方法。

1.你可以调用wx.App的ExitMainLoop()方法。这个方法显式地使用主消息循环终止,使用控制离开MainLoop()函数。这通常将终止应用程序,这个方法实际上等同于关闭所有这里所谓顶级窗口。

2.你也可以调用全局方法wx.Exit()。

正常使用情况下,两种方法我们都不推荐,因为它将导致一些清理函数被跳过。

使用wx.Frame

框架就是用户通常称的窗口。那就是说,框架是一个容器,用户可以将它在屏幕上任意移动,并可将它缩放,它通常包含诸如标题栏、菜单等等。

构造函数:

wx.Frame(parent, id=-1, title="", pos=wx.DefaultPosition,

size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,

name="frame")

parent:框架的父窗口。对于顶级窗口,这个值是None。框架随其父窗口的销毁而销毁。取决于平台,框架可被限制只出现在父窗口的顶部。在多文档界面的情况下,子窗口被限制为只能在父窗口中移动和缩放。

id:关于新窗口的wxPython ID号。你可以明确地传递一个。或传递-1,这将导致wxPython自动生成一个新的ID。

title:窗口的标题

pos:一个wx.Point对象,它指定这个新窗口的左上角在屏幕中的位置。在图形用户界面程序中,通常(0,0)是显示器的左上角。这个默认的(-1,-1)将让系统决定窗口的位置。

size:一个wx.Size对象,它指定这个窗口的初始尺寸。这个默认的(-1,-1)将让系统决定窗口的初始尺寸。

style:指定窗口的类型的常量。你可以使用或运算来组合它们。

name:框架的内在的名字。以后你可以使用它来寻找这个窗口。

使用wxPython的ID

ID号是所有窗口部件的特征。在wxPython中也有一些标准的预定义的ID号,它们有特定的意思(例如,wx.ID_OK和wx.ID_CANCEL是对话框中的OK和Cancel按钮的ID号)。有三种方法来创建一个窗口部件使用的ID号:

1、明确地给构造器传递一个正的整数

2、使用wx.NewId()函数

id = wx.NewId()

frame = wx.Frame.__init__(None, id)

3、传递一个全局常量wx.ID_ANY或-1给窗口部件的构造器

frame = wx.Frame.__init__(None, -1)

id = frame.GetId()

使用wx.Size和wx.Point

wx.Point类表示一个点或位置。构造器要求点的x和y值。如果不设置x,y值,则值默认为0。我们可以使用Set(x,y)和Get()函数来设置和得到x和y值。Get()函数返回一个元组。x和y值可以像下面这样作为属性被访问:

point = wx.Point(10, 12)

x = point.x

y = point.y

wx.Point的实例可以像其它Python对象一样作加、减和比较运算,例如:

a = wx.Point(2, 3)

b = wx.Point(5, 7)

c = a + b

bigger = a > b

在wx.Point的实参中,坐标值一般为整数。如果你需要浮点数坐标,你可以使用类wx.RealPoint,它的用法如同wx.Point。

wx.Size类几乎和wx.Point完全相同,除了实参的名字是width和height。对wx.Size的操作与wx.Point一样

使用wx.Frame的样式

一些窗口部件也定义了一个SetStyle()方法,让你可以在该窗口部件创建后改变它的样式。所有的你能使用的样式元素都有一个常量标识符(如wx.MINIMIZE_BOX)。要使用多个样式,你可以使用或运算符|。例如,wx.DEFAULT_FRAME_STYLE样式就被定义为如下几个基本样式的组合:

wx.MAXIMIZE_BOX | wx.MINIMIZE_BOX | wx.RESIZE_BORDER |wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX

要从一个合成的样式中去掉个别的样式,你可以使用^操作符。例如要创建一个默认样式的窗口,但要求用户不能缩放和改变窗口的尺寸,你可以这样做:

wx.DEFAULT_FRAME_STYLE ^ (wx.RESIZE_BORDER | wx.MINIMIZE_BOX |wx.MAXIMIZE_BOX)

列出了用于wx.Frame的最重要的样式:

wx.CAPTION:在框架上增加一个标题栏,它显示该框架的标题属性。

wx.CLOSE_BOX:指示系统在框架的标题栏上显示一个关闭框,使用系统默认的位置和样式。

wx.DEFAULT_FRAME_STYLE:默认样式。

wx.FRAME_SHAPED:用这个样式创建的框架可以使用SetShape()方法去创建一个非矩形的窗口。

wx.FRAME_TOOL_WINDOW:通过给框架一个比正常更小的标题栏,使框架看起来像一个工具框窗口。在Windows下,使用这个样式创建的框架不会出现在显示所有打开窗口的任务栏上。

wx.MAXIMIZE_BOX:指示系统在框架的标题栏上显示一个最大化框,使用系统默认的位置和样式。

wx.MINIMIZE_BOX:指示系统在框架的标题栏上显示一个最小化框,使用系统默认的位置和样式。

wx.RESIZE_BORDER:给框架增加一个可以改变尺寸的边框。

wx.SIMPLE_BORDER:没有装饰的边框。不能工作在所有平台上。

wx.SYSTEM_MENU:增加系统菜单(带有关闭、移动、改变尺寸等功能)和关闭框到这个窗口。在系统菜单中的改变尺寸和关闭功能的有效性依赖于wx.MAXIMIZE_BOX, wx.MINIMIZE_BOX和wx.CLOSE_BOX样式是否被应用。

为一个框架增加对象和子窗口

给框架增加窗口部件

import wx

class InsertFrame(wx.Frame):

def __init__(self, parent, id):

wx.Frame.__init__(self, parent, id, 'Frame With Button',

size=(300, 100))

panel = wx.Panel(self) #创建画板

button = wx.Button(panel, label="Close", pos=(125, 10),

size=(50, 50)) #将按钮添加到画板

#绑定按钮的单击事件

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()

frame = InsertFrame(parent=None, id=-1)

frame.Show()

app.MainLoop()

给框架增加菜单栏、工具栏和状态栏

import wx

import wx.py.images as images

class ToolbarFrame(wx.Frame):

def __init__(self, parent, id):

wx.Frame.__init__(self, parent, id, 'Toolbars',

size=(300, 200))

panel = wx.Panel(self)

panel.SetBackgroundColour('White')

statusBar = self.CreateStatusBar() #1 创建状态栏

toolbar = self.CreateToolBar() #2 创建工具栏

toolbar.AddSimpleTool(wx.NewId(), images.getPyBitmap(),

"New", "Long help for 'New'") #3 给工具栏增加一个工具

toolbar.Realize() #4 准备显示工具栏

menuBar = wx.MenuBar() # 创建菜单栏

# 创建两个菜单

menu1 = wx.Menu()

menuBar.Append(menu1, "菜单1")

menu2 = wx.Menu()

#6 创建菜单的项目

menu2.Append(wx.NewId(), "Copy", "Copy in status bar")

menu2.Append(wx.NewId(), "Paste", "Paste in status bar")

menu2.AppendSeparator()

menu2.Append(wx.NewId(), "Display", "Display Options")

menuBar.Append(menu2, "菜单2") # 在菜单栏上附上菜单

self.SetMenuBar(menuBar) #在框架上附上菜单栏

if __name__ == '__main__':

app = wx.PySimpleApp()

frame = ToolbarFrame(parent=None, id=-1)

frame.Show()

app.MainLoop()

使用对话框

三种用对话框得到来自用户的信息:

1、消息对话框

examples:

dlg = wx.MessageDialog(None, 'Are you sure to open window?',

'MessageDialog', wx.YES_NO | wx.ICON_QUESTION)

result = dlg.ShowModal()

print result

dlg.Destroy()

examples:

message = "are you sure?"

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

style=wx.OK | wx.CANCEL,pos=wx.DefaultPosition)

result = dlg.ShowModal()

print result

dlg.Destroy()

对于wx.MessageDialog,返回值是下面常量之一: wx.ID_YES, wx.ID_NO, wx.ID_CANCEL, wx.ID_OK。

2、文本输入对话框

examples:

dlg = wx.TextEntryDialog(None, "Who is buried in Grant's tomb?",

'A Question', 'Cary Grant')

if dlg.ShowModal() == wx.ID_OK:

response = dlg.GetValue()

print response

dlg.Destroy()

3、从一个列表中选择

dlg = wx.SingleChoiceDialog(None,'What version of Python are you using?',

'Single Choice',

['2.6.4', '3.1', '2.1.3', '2.2', '2.3.1'])

if dlg.ShowModal() == wx.ID_OK:

response = dlg.GetStringSelection()

dlg.Destroy()

第三章 在事件驱动环境中工作

事件(event):在你的应用程序期间发生的事情,它要求有一个响应。

事件对象(event object):在wxPython中,它具体代表一个事件,其中包括了事件的数据等属性。它是类wx.Event或其子类的实例,子类如wx.CommandEvent和wx.MouseEvent。

事件类型(event type):wxPython分配给每个事件对象的一个整数ID。事件类型给出了关于该事件本身更多的信息。例如,wx.MouseEvent的事件类型标识了该事件是一个鼠标单击还是一个鼠标移动。

事件源(event source):任何wxPython对象都能产生事件。例如按钮、菜单、列表框和任何别的窗口部件。

事件驱动(event-driven):一个程序结构,它的大部分时间花在等待或响应事件上。

事件队列(event queue):已发生的但未处理的事件的一个列表。

事件处理器(event handler):响应事件时所调用的函数或方法。也称作处理器函数或处理器方法。

事件绑定器(event binder):一个封装了特定窗口部件,特定事件类型和一个事件处理器的wxPython对象。为了被调用,所有事件处理器必须用一个事件绑定器注册。

从用户的角度上来看,wxPython程序大部分时间什么也不做,一直闲着直到用户或系统做了些什么来触发这个wxPython程序动作。

事件驱动程序结构的主要特点:

1、在初始化设置之后,程序的大部分时间花在了一个空闭的循环之中。进入这个循环就标志着程序与用户交互的部分的开始,退出这个循环就标志结束。在wxPython中,这个循环的方法是:wx.App.MainLoop(),并且在你的脚本中显式地被调用。当所有的顶级窗口关闭时,主循环退出。

2、程序包含了对应于发生在程序环境中的事情的事件。事件通常由用户的行为触发,但是也可以由系统的行为或程序中其他任意的代码。在wxPython中,所有的事件都是类wx.Event或其子类的一个实例。每个事件都有一个事件类型属性,它使得不同的事件能够被辨别。例如,鼠标释放和鼠示按下事件都被认为是同一个类的实例,但有不同的事件类型。

3、作为这个空闭的循环部分,程序定期检查是否有任何请求响应事情发生。有两种机制使得事件驱动系统可以得到有关事件的通知。最常被wxPython使用的方法是,把事件传送到一个中心队列,由该队列触发相应事件的处理。另一种方法是使用轮询的方法,所有可能引发事件的事件主被主过程定期查询并询问是否有没有处理的事件。

4、当事件发生时,基于事件的系统试着确定相关代码来处理该事件,如果有,相关代码被执行。在wxPython中,原系统事件被转换为wx.Event实例,然后使用wx.EvtHandler.ProcessEvent()方法将事件分派给适当的处理器代码

编写事件处理器

self.Bind(wx.EVT_BUTTON, self.OnClick, aButton)

上例使用了预定义的事件绑定器对象wx.EVT_BUTTON来将aButton对象上的按钮单击事件与方法self.OnClick相关联起来。这个Bind()方法是wx.EvtHandler的一个方法,wx.EvtHandler是所有可显示对象的父类。因此上例代码行可以被放置在任何显示类。

使用wx.EvtHandler的方法工作

Bind(event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY)

使用参数source和不使用参数source的方法

def __init__(self, parent, id):

wx.Frame.__init__(self, parent, id, 'Frame With Button',

size=(300, 100))

panel = wx.Panel(self, -1)

button = wx.Button(panel, -1, "Close", pos=(130, 15),

size=(40, 40))

self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) #1 绑定框架关闭事件

self.Bind(wx.EVT_BUTTON, self.OnCloseMe, button) #2 绑定按钮事件

def OnCloseMe(self, event):

self.Close(True)

def OnCloseWindow(self, event):

self.Destroy()

这行绑定框架关闭事件到self.OnCloseWindow方法。由于这个事件通过该框架触发且用于帧,所以不需要传递一个source参数。

这行将来自按钮对象的按钮敲击事件绑定到self.OnCloseMe方法。这样做是为了让wxPython能够区分在这个框架中该按钮和其它按钮所产生的事件。

examples:

import wx

class MenuEventFrame(wx.Frame):

def __init__(self, parent, id):

wx.Frame.__init__(self, parent, id, 'Menus',

size=(300, 200))

menuBar = wx.MenuBar()

menu1 = wx.Menu()

menuItem = menu1.Append(-1, "关闭")

menuBar.Append(menu1, "系统")

self.SetMenuBar(menuBar)

self.Bind(wx.EVT_MENU, self.OnCloseMe, menuItem)

def OnCloseMe(self, event):

self.Close(True)

if __name__ == '__main__':

app = wx.PySimpleApp()

frame = MenuEventFrame(parent=None, id=-1)

frame.Show()

app.MainLoop()

example 鼠标绑定事件:

import wx

class MouseEventFrame(wx.Frame):

def __init__(self, parent, id):

wx.Frame.__init__(self, parent, id, 'Frame With Button',

size=(300, 100))

self.panel = wx.Panel(self)

self.button = wx.Button(self.panel,

label="Not Over", pos=(100, 15))

self.Bind(wx.EVT_BUTTON, self.OnButtonClick,self.button) #1 绑定按钮事件

self.button.Bind(wx.EVT_ENTER_WINDOW,self.OnEnterWindow) #2 绑定鼠标位于其上事件

self.button.Bind(wx.EVT_LEAVE_WINDOW,self.OnLeaveWindow) #3 绑定鼠标离开事件

def OnButtonClick(self, event):

self.panel.SetBackgroundColour('Green')

self.panel.Refresh()

def OnEnterWindow(self, event):

self.button.SetLabel("Over Me!")

event.Skip()

def OnLeaveWindow(self, event):

self.button.SetLabel("Not Over")

event.Skip()

if __name__ == '__main__':

app = wx.PySimpleApp()

frame = MouseEventFrame(parent=None, id=-1)

frame.Show()

app.MainLoop()

在调用了第一个事件处理器之后,wxPython查看是否有进一步的处理要求。事件处理器通过调用wx.Event的方法Skip()要求更多的处理。如果Skip()方法被调用,那么处理将继续,并且任何定义在超类中的处理器在这一步中被发现并执行。Skip()方法在处理中的任一点或处理器所调用的任何代码中都可以被调用。Skip()方法在事件实例中设置一个标记,在事件处理器方法完成后,wxPython检查这个标记。在例3.3中,OnButtonClick()不调用Skip(),因此在那种情况下,处理器方法结束后,事件处理完成。在另两个事件处理器中调用了Skip(),所以系统将保持搜索“匹配事件绑定”,最后对于原窗口部件的鼠标进入和离开事件调用默认的功能,如鼠标位于其上的事件。

使用Skip()方法

事件的第一个处理器函数被发现并执行完后,该事件处理将终止,除非在处理器返回之前调用了该事件的Skip()方法。调用Skip()方法允许另外被绑定的处理器被搜索,搜索依据3.4.1节中的第四步中声明的规则,因此父类和父窗口被搜索,就如同这第一个处理器不存在一样。在某些情况下,你想继续处理事件,以便原窗口部件的默认行为和你定制的处理能被执行。例3.4显示了一个使用Skip()的例子,它使得程序能够同时响应同一按钮上的鼠标左按键按下和按钮敲击。

examples:

import wx

class DoubleEventFrame(wx.Frame):

def __init__(self, parent, id):

wx.Frame.__init__(self, parent, id, 'Frame With Button',

size=(300, 100))

self.panel = wx.Panel(self, -1)

self.button = wx.Button(self.panel, -1, "Click Me", pos=(100, 15))

self.Bind(wx.EVT_BUTTON, self.OnButtonClick,self.button) #1 绑定按钮敲击事件

self.button.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown) #2 绑定鼠标左键按下事件

def OnButtonClick(self, event):

self.panel.SetBackgroundColour('Green')

self.panel.Refresh()

def OnMouseDown(self, event):

self.button.SetLabel("Again!")

event.Skip() #3 确保继续处理

if __name__ == '__main__':

app = wx.PySimpleApp()

frame = DoubleEventFrame(parent=None, id=-1)

frame.Show()

app.MainLoop()

当绑定低级事件时如鼠标按下或释放,wxPython期望捕获这些低级事件以便生成进一步的事件,为了进一步的事件处理,你必须调用Skip()方法,否则进一步的事件处理将被阻止。

创建自己的事件

创建自定义事件的步骤:

1、定义一个新的事件类,它是wxPython的wx.PyEvent类的子类。如果你想这个事件被作为命令事件,你可以创建wx.PyCommandEvent的子类。

2、创建一个事件类型和一个绑定器对象去绑定该事件到特定的对象。

3、添加能够建造这个新事件实例的代码,并且使用ProcessEvent()方法将这个实例引入事件处理系统。一旦该事件被创建,你就可以像使用其它的wxPython事件一样创建绑定和处理器方法。

examples:

import wx

class TwoButtonEvent(wx.PyCommandEvent): #1 定义事件

def __init__(self, evtType, id):

wx.PyCommandEvent.__init__(self, evtType, id)

self.clickCount = 0

def GetClickCount(self):

return self.clickCount

def SetClickCount(self, count):

self.clickCount = count

myEVT_TWO_BUTTON = wx.NewEventType() #2 创建一个事件类型

EVT_TWO_BUTTON = wx.PyEventBinder(myEVT_TWO_BUTTON, 1) #3 创建一个绑定器对象

class TwoButtonPanel(wx.Panel):

def __init__(self, parent, id=-1, leftText="Left",rightText="Right"):

wx.Panel.__init__(self, parent, id)

self.leftButton = wx.Button(self, label=leftText)

self.rightButton = wx.Button(self, label=rightText,pos=(100,0))

self.leftClick = False

self.rightClick = False

self.clickCount = 0

#4 下面两行绑定更低级的事件

self.leftButton.Bind(wx.EVT_LEFT_DOWN, self.OnLeftClick)

self.rightButton.Bind(wx.EVT_LEFT_DOWN, self.OnRightClick)

def OnLeftClick(self, event):

self.leftClick = True

self.OnClick()

event.Skip() #5 继续处理

def OnRightClick(self, event):

self.rightClick = True

self.OnClick()

event.Skip() #6 继续处理

def OnClick(self):

self.clickCount += 1

if self.leftClick and self.rightClick:

self.leftClick = False

self.rightClick = False

evt = TwoButtonEvent(myEVT_TWO_BUTTON, self.GetId()) #7 创建自定义事件

evt.SetClickCount(self.clickCount) # 添加数据到事件

self.GetEventHandler().ProcessEvent(evt) #8 处理事件

class CustomEventFrame(wx.Frame):

def __init__(self, parent, id):

wx.Frame.__init__(self, parent, id, 'Click Count: 0',size=(300, 100))

panel = TwoButtonPanel(self)

self.Bind(EVT_TWO_BUTTON, self.OnTwoClick, panel) #9 绑定自定义事件

def OnTwoClick(self, event): #10 定义一个事件处理器函数

self.SetTitle("Click Count: %s" % event.GetClickCount())

if __name__ == '__main__':

app = wx.PySimpleApp()

frame = CustomEventFrame(parent=None, id=-1)

frame.Show()

app.MainLoop()

这块不是很懂。可以以后回过头来再看。