wxpython模块学习
WxPython的程序结构
一个WxPython程序一般包含两个对象:应用程序对象和根窗口(可多个)。其中,应用程序对象可以使用wx.App(或子类)实现(用于处理窗口中的事件响应),根窗口可以使用wx.Frame实现。
import wx class MyFrame(wx.Frame): #创建自定义Frame def __init__(self,parent): #def __init__( # self, parent=None, id=None, title=None, pos=None, # size=None, style=None, name=None) wx.Frame.__init__(self,parent,id=-1,title="Hello World",size=(300,300)) #设置窗体 panel = wx.Panel(self) sizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(sizer) txt = wx.StaticText(panel,-1,"Hello World!") #创建静态文本组件 sizer.Add(txt,0,wx.TOP|wx.LEFT,100) self.Center() #将窗口放在桌面环境的中间 class MyApp(wx.App): def OnInit(self): print("开始进入事件循环") self.frame = MyFrame(None) self.frame.Show(True) return True #需要返回一个布尔型,只有初始返回成功,程序才会继续执行 def OnExit(self): print("事件循环结束") import time time.sleep(2) return 0 #返回状态码 app = MyApp() app.MainLoop()
窗口组成:
前面的代码中窗口和应用程序似乎没有关联,为什么?
在应用程序对象中,可以通过SetTopWindow方法来设置根窗口。若是没有设置,是因为应用程序会选择窗口列表中的第一个作为根窗口。上面的程序也只有一个窗口,所以没有必要去显示设置。同样使用SetTopWindow也可以改变根窗口
控件的部分构造参数:
参数 | 说明 |
parent | 父窗口(为None代表为顶级窗口),注意设置父窗口后,在父窗口关闭时,子窗口也会消失 |
id | 上面说过了 |
title | 标题 |
pos | 相对于屏幕的位置默认(-1,-1),自行选择。我们在上面使用center也可以改变窗口在屏幕中的位置 |
size | 大小默认(-1,-1)窗口系统自行选择 |
style | 窗口类型... |
name | 窗口的内在名字 |
事件驱动:
EVT_SIZE = wx.PyEventBinder( wxEVT_SIZE ) EVT_SIZING = wx.PyEventBinder( wxEVT_SIZING ) EVT_MOVE = wx.PyEventBinder( wxEVT_MOVE ) EVT_MOVING = wx.PyEventBinder( wxEVT_MOVING ) EVT_MOVE_START = wx.PyEventBinder( wxEVT_MOVE_START ) EVT_MOVE_END = wx.PyEventBinder( wxEVT_MOVE_END ) EVT_CLOSE = wx.PyEventBinder( wxEVT_CLOSE_WINDOW ) EVT_END_SESSION = wx.PyEventBinder( wxEVT_END_SESSION ) EVT_QUERY_END_SESSION = wx.PyEventBinder( wxEVT_QUERY_END_SESSION ) EVT_PAINT = wx.PyEventBinder( wxEVT_PAINT ) EVT_NC_PAINT = wx.PyEventBinder( wxEVT_NC_PAINT ) EVT_ERASE_BACKGROUND = wx.PyEventBinder( wxEVT_ERASE_BACKGROUND ) EVT_CHAR = wx.PyEventBinder( wxEVT_CHAR ) EVT_KEY_DOWN = wx.PyEventBinder( wxEVT_KEY_DOWN ) EVT_KEY_UP = wx.PyEventBinder( wxEVT_KEY_UP ) EVT_HOTKEY = wx.PyEventBinder( wxEVT_HOTKEY, 1) EVT_CHAR_HOOK = wx.PyEventBinder( wxEVT_CHAR_HOOK ) EVT_MENU_OPEN = wx.PyEventBinder( wxEVT_MENU_OPEN ) EVT_MENU_CLOSE = wx.PyEventBinder( wxEVT_MENU_CLOSE ) EVT_MENU_HIGHLIGHT = wx.PyEventBinder( wxEVT_MENU_HIGHLIGHT, 1) EVT_MENU_HIGHLIGHT_ALL = wx.PyEventBinder( wxEVT_MENU_HIGHLIGHT ) EVT_SET_FOCUS = wx.PyEventBinder( wxEVT_SET_FOCUS ) EVT_KILL_FOCUS = wx.PyEventBinder( wxEVT_KILL_FOCUS ) EVT_CHILD_FOCUS = wx.PyEventBinder( wxEVT_CHILD_FOCUS ) EVT_ACTIVATE = wx.PyEventBinder( wxEVT_ACTIVATE ) EVT_ACTIVATE_APP = wx.PyEventBinder( wxEVT_ACTIVATE_APP ) EVT_HIBERNATE = wx.PyEventBinder( wxEVT_HIBERNATE ) EVT_DROP_FILES = wx.PyEventBinder( wxEVT_DROP_FILES ) EVT_INIT_DIALOG = wx.PyEventBinder( wxEVT_INIT_DIALOG ) EVT_SYS_COLOUR_CHANGED = wx.PyEventBinder( wxEVT_SYS_COLOUR_CHANGED ) EVT_DISPLAY_CHANGED = wx.PyEventBinder( wxEVT_DISPLAY_CHANGED ) EVT_SHOW = wx.PyEventBinder( wxEVT_SHOW ) EVT_MAXIMIZE = wx.PyEventBinder( wxEVT_MAXIMIZE ) EVT_ICONIZE = wx.PyEventBinder( wxEVT_ICONIZE ) EVT_NAVIGATION_KEY = wx.PyEventBinder( wxEVT_NAVIGATION_KEY ) EVT_PALETTE_CHANGED = wx.PyEventBinder( wxEVT_PALETTE_CHANGED ) EVT_QUERY_NEW_PALETTE = wx.PyEventBinder( wxEVT_QUERY_NEW_PALETTE ) EVT_WINDOW_CREATE = wx.PyEventBinder( wxEVT_CREATE ) EVT_WINDOW_DESTROY = wx.PyEventBinder( wxEVT_DESTROY ) EVT_SET_CURSOR = wx.PyEventBinder( wxEVT_SET_CURSOR ) EVT_MOUSE_CAPTURE_CHANGED = wx.PyEventBinder( wxEVT_MOUSE_CAPTURE_CHANGED ) EVT_MOUSE_CAPTURE_LOST = wx.PyEventBinder( wxEVT_MOUSE_CAPTURE_LOST ) EVT_LEFT_DOWN = wx.PyEventBinder( wxEVT_LEFT_DOWN ) EVT_LEFT_UP = wx.PyEventBinder( wxEVT_LEFT_UP ) EVT_MIDDLE_DOWN = wx.PyEventBinder( wxEVT_MIDDLE_DOWN ) EVT_MIDDLE_UP = wx.PyEventBinder( wxEVT_MIDDLE_UP ) EVT_RIGHT_DOWN = wx.PyEventBinder( wxEVT_RIGHT_DOWN ) EVT_RIGHT_UP = wx.PyEventBinder( wxEVT_RIGHT_UP ) EVT_MOTION = wx.PyEventBinder( wxEVT_MOTION ) EVT_LEFT_DCLICK = wx.PyEventBinder( wxEVT_LEFT_DCLICK ) EVT_MIDDLE_DCLICK = wx.PyEventBinder( wxEVT_MIDDLE_DCLICK ) EVT_RIGHT_DCLICK = wx.PyEventBinder( wxEVT_RIGHT_DCLICK ) EVT_LEAVE_WINDOW = wx.PyEventBinder( wxEVT_LEAVE_WINDOW ) EVT_ENTER_WINDOW = wx.PyEventBinder( wxEVT_ENTER_WINDOW ) EVT_MOUSEWHEEL = wx.PyEventBinder( wxEVT_MOUSEWHEEL ) EVT_MOUSE_AUX1_DOWN = wx.PyEventBinder( wxEVT_AUX1_DOWN ) EVT_MOUSE_AUX1_UP = wx.PyEventBinder( wxEVT_AUX1_UP ) EVT_MOUSE_AUX1_DCLICK = wx.PyEventBinder( wxEVT_AUX1_DCLICK ) EVT_MOUSE_AUX2_DOWN = wx.PyEventBinder( wxEVT_AUX2_DOWN ) EVT_MOUSE_AUX2_UP = wx.PyEventBinder( wxEVT_AUX2_UP ) EVT_MOUSE_AUX2_DCLICK = wx.PyEventBinder( wxEVT_AUX2_DCLICK ) EVT_MOUSE_EVENTS = wx.PyEventBinder([ wxEVT_LEFT_DOWN, wxEVT_LEFT_UP, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_UP, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_UP, wxEVT_MOTION, wxEVT_LEFT_DCLICK, wxEVT_MIDDLE_DCLICK, wxEVT_RIGHT_DCLICK, wxEVT_ENTER_WINDOW, wxEVT_LEAVE_WINDOW, wxEVT_MOUSEWHEEL, wxEVT_AUX1_DOWN, wxEVT_AUX1_UP, wxEVT_AUX1_DCLICK, wxEVT_AUX2_DOWN, wxEVT_AUX2_UP, wxEVT_AUX2_DCLICK, ]) # Scrolling from wxWindow (sent to wxScrolledWindow) EVT_SCROLLWIN = wx.PyEventBinder([ wxEVT_SCROLLWIN_TOP, wxEVT_SCROLLWIN_BOTTOM, wxEVT_SCROLLWIN_LINEUP, wxEVT_SCROLLWIN_LINEDOWN, wxEVT_SCROLLWIN_PAGEUP, wxEVT_SCROLLWIN_PAGEDOWN, wxEVT_SCROLLWIN_THUMBTRACK, wxEVT_SCROLLWIN_THUMBRELEASE, ]) EVT_SCROLLWIN_TOP = wx.PyEventBinder( wxEVT_SCROLLWIN_TOP ) EVT_SCROLLWIN_BOTTOM = wx.PyEventBinder( wxEVT_SCROLLWIN_BOTTOM ) EVT_SCROLLWIN_LINEUP = wx.PyEventBinder( wxEVT_SCROLLWIN_LINEUP ) EVT_SCROLLWIN_LINEDOWN = wx.PyEventBinder( wxEVT_SCROLLWIN_LINEDOWN ) EVT_SCROLLWIN_PAGEUP = wx.PyEventBinder( wxEVT_SCROLLWIN_PAGEUP ) EVT_SCROLLWIN_PAGEDOWN = wx.PyEventBinder( wxEVT_SCROLLWIN_PAGEDOWN ) EVT_SCROLLWIN_THUMBTRACK = wx.PyEventBinder( wxEVT_SCROLLWIN_THUMBTRACK ) EVT_SCROLLWIN_THUMBRELEASE = wx.PyEventBinder( wxEVT_SCROLLWIN_THUMBRELEASE ) # Scrolling from wx.Slider and wx.ScrollBar EVT_SCROLL = wx.PyEventBinder([ wxEVT_SCROLL_TOP, wxEVT_SCROLL_BOTTOM, wxEVT_SCROLL_LINEUP, wxEVT_SCROLL_LINEDOWN, wxEVT_SCROLL_PAGEUP, wxEVT_SCROLL_PAGEDOWN, wxEVT_SCROLL_THUMBTRACK, wxEVT_SCROLL_THUMBRELEASE, wxEVT_SCROLL_CHANGED, ]) EVT_SCROLL_TOP = wx.PyEventBinder( wxEVT_SCROLL_TOP ) EVT_SCROLL_BOTTOM = wx.PyEventBinder( wxEVT_SCROLL_BOTTOM ) EVT_SCROLL_LINEUP = wx.PyEventBinder( wxEVT_SCROLL_LINEUP ) EVT_SCROLL_LINEDOWN = wx.PyEventBinder( wxEVT_SCROLL_LINEDOWN ) EVT_SCROLL_PAGEUP = wx.PyEventBinder( wxEVT_SCROLL_PAGEUP ) EVT_SCROLL_PAGEDOWN = wx.PyEventBinder( wxEVT_SCROLL_PAGEDOWN ) EVT_SCROLL_THUMBTRACK = wx.PyEventBinder( wxEVT_SCROLL_THUMBTRACK ) EVT_SCROLL_THUMBRELEASE = wx.PyEventBinder( wxEVT_SCROLL_THUMBRELEASE ) EVT_SCROLL_CHANGED = wx.PyEventBinder( wxEVT_SCROLL_CHANGED ) EVT_SCROLL_ENDSCROLL = EVT_SCROLL_CHANGED # Scrolling from wx.Slider and wx.ScrollBar, with an id EVT_COMMAND_SCROLL = wx.PyEventBinder([ wxEVT_SCROLL_TOP, wxEVT_SCROLL_BOTTOM, wxEVT_SCROLL_LINEUP, wxEVT_SCROLL_LINEDOWN, wxEVT_SCROLL_PAGEUP, wxEVT_SCROLL_PAGEDOWN, wxEVT_SCROLL_THUMBTRACK, wxEVT_SCROLL_THUMBRELEASE, wxEVT_SCROLL_CHANGED, ], 1) EVT_COMMAND_SCROLL_TOP = wx.PyEventBinder( wxEVT_SCROLL_TOP, 1) EVT_COMMAND_SCROLL_BOTTOM = wx.PyEventBinder( wxEVT_SCROLL_BOTTOM, 1) EVT_COMMAND_SCROLL_LINEUP = wx.PyEventBinder( wxEVT_SCROLL_LINEUP, 1) EVT_COMMAND_SCROLL_LINEDOWN = wx.PyEventBinder( wxEVT_SCROLL_LINEDOWN, 1) EVT_COMMAND_SCROLL_PAGEUP = wx.PyEventBinder( wxEVT_SCROLL_PAGEUP, 1) EVT_COMMAND_SCROLL_PAGEDOWN = wx.PyEventBinder( wxEVT_SCROLL_PAGEDOWN, 1) EVT_COMMAND_SCROLL_THUMBTRACK = wx.PyEventBinder( wxEVT_SCROLL_THUMBTRACK, 1) EVT_COMMAND_SCROLL_THUMBRELEASE = wx.PyEventBinder( wxEVT_SCROLL_THUMBRELEASE, 1) EVT_COMMAND_SCROLL_CHANGED = wx.PyEventBinder( wxEVT_SCROLL_CHANGED, 1) EVT_COMMAND_SCROLL_ENDSCROLL = EVT_COMMAND_SCROLL_CHANGED EVT_BUTTON = wx.PyEventBinder( wxEVT_BUTTON, 1) EVT_CHECKBOX = wx.PyEventBinder( wxEVT_CHECKBOX, 1) EVT_CHOICE = wx.PyEventBinder( wxEVT_CHOICE, 1) EVT_LISTBOX = wx.PyEventBinder( wxEVT_LISTBOX, 1) EVT_LISTBOX_DCLICK = wx.PyEventBinder( wxEVT_LISTBOX_DCLICK, 1) EVT_MENU = wx.PyEventBinder( wxEVT_MENU, 1) EVT_MENU_RANGE = wx.PyEventBinder( wxEVT_MENU, 2) EVT_SLIDER = wx.PyEventBinder( wxEVT_SLIDER, 1) EVT_RADIOBOX = wx.PyEventBinder( wxEVT_RADIOBOX, 1) EVT_RADIOBUTTON = wx.PyEventBinder( wxEVT_RADIOBUTTON, 1) EVT_SCROLLBAR = wx.PyEventBinder( wxEVT_SCROLLBAR, 1) EVT_VLBOX = wx.PyEventBinder( wxEVT_VLBOX, 1) EVT_COMBOBOX = wx.PyEventBinder( wxEVT_COMBOBOX, 1) EVT_TOOL = wx.PyEventBinder( wxEVT_TOOL, 1) EVT_TOOL_RANGE = wx.PyEventBinder( wxEVT_TOOL, 2) EVT_TOOL_RCLICKED = wx.PyEventBinder( wxEVT_TOOL_RCLICKED, 1) EVT_TOOL_RCLICKED_RANGE = wx.PyEventBinder( wxEVT_TOOL_RCLICKED, 2) EVT_TOOL_ENTER = wx.PyEventBinder( wxEVT_TOOL_ENTER, 1) EVT_TOOL_DROPDOWN = wx.PyEventBinder( wxEVT_TOOL_DROPDOWN, 1) EVT_CHECKLISTBOX = wx.PyEventBinder( wxEVT_CHECKLISTBOX, 1) EVT_COMBOBOX_DROPDOWN = wx.PyEventBinder( wxEVT_COMBOBOX_DROPDOWN , 1) EVT_COMBOBOX_CLOSEUP = wx.PyEventBinder( wxEVT_COMBOBOX_CLOSEUP , 1) EVT_COMMAND_LEFT_CLICK = wx.PyEventBinder( wxEVT_COMMAND_LEFT_CLICK, 1) EVT_COMMAND_LEFT_DCLICK = wx.PyEventBinder( wxEVT_COMMAND_LEFT_DCLICK, 1) EVT_COMMAND_RIGHT_CLICK = wx.PyEventBinder( wxEVT_COMMAND_RIGHT_CLICK, 1) EVT_COMMAND_RIGHT_DCLICK = wx.PyEventBinder( wxEVT_COMMAND_RIGHT_DCLICK, 1) EVT_COMMAND_SET_FOCUS = wx.PyEventBinder( wxEVT_COMMAND_SET_FOCUS, 1) EVT_COMMAND_KILL_FOCUS = wx.PyEventBinder( wxEVT_COMMAND_KILL_FOCUS, 1) EVT_COMMAND_ENTER = wx.PyEventBinder( wxEVT_COMMAND_ENTER, 1) EVT_HELP = wx.PyEventBinder( wxEVT_HELP, 1) EVT_HELP_RANGE = wx.PyEventBinder( wxEVT_HELP, 2) EVT_DETAILED_HELP = wx.PyEventBinder( wxEVT_DETAILED_HELP, 1) EVT_DETAILED_HELP_RANGE = wx.PyEventBinder( wxEVT_DETAILED_HELP, 2) EVT_IDLE = wx.PyEventBinder( wxEVT_IDLE ) EVT_UPDATE_UI = wx.PyEventBinder( wxEVT_UPDATE_UI, 1) EVT_UPDATE_UI_RANGE = wx.PyEventBinder( wxEVT_UPDATE_UI, 2) EVT_CONTEXT_MENU = wx.PyEventBinder( wxEVT_CONTEXT_MENU ) EVT_THREAD = wx.PyEventBinder( wxEVT_THREAD ) EVT_WINDOW_MODAL_DIALOG_CLOSED = wx.PyEventBinder( wxEVT_WINDOW_MODAL_DIALOG_CLOSED ) EVT_JOY_BUTTON_DOWN = wx.PyEventBinder( wxEVT_JOY_BUTTON_DOWN ) EVT_JOY_BUTTON_UP = wx.PyEventBinder( wxEVT_JOY_BUTTON_UP ) EVT_JOY_MOVE = wx.PyEventBinder( wxEVT_JOY_MOVE ) EVT_JOY_ZMOVE = wx.PyEventBinder( wxEVT_JOY_ZMOVE ) EVT_JOYSTICK_EVENTS = wx.PyEventBinder([ wxEVT_JOY_BUTTON_DOWN, wxEVT_JOY_BUTTON_UP, wxEVT_JOY_MOVE, wxEVT_JOY_ZMOVE, ]) # deprecated wxEVT aliases wxEVT_COMMAND_BUTTON_CLICKED = wxEVT_BUTTON wxEVT_COMMAND_CHECKBOX_CLICKED = wxEVT_CHECKBOX wxEVT_COMMAND_CHOICE_SELECTED = wxEVT_CHOICE wxEVT_COMMAND_LISTBOX_SELECTED = wxEVT_LISTBOX wxEVT_COMMAND_LISTBOX_DOUBLECLICKED = wxEVT_LISTBOX_DCLICK wxEVT_COMMAND_CHECKLISTBOX_TOGGLED = wxEVT_CHECKLISTBOX wxEVT_COMMAND_MENU_SELECTED = wxEVT_MENU wxEVT_COMMAND_TOOL_CLICKED = wxEVT_TOOL wxEVT_COMMAND_SLIDER_UPDATED = wxEVT_SLIDER wxEVT_COMMAND_RADIOBOX_SELECTED = wxEVT_RADIOBOX wxEVT_COMMAND_RADIOBUTTON_SELECTED = wxEVT_RADIOBUTTON wxEVT_COMMAND_SCROLLBAR_UPDATED = wxEVT_SCROLLBAR wxEVT_COMMAND_VLBOX_SELECTED = wxEVT_VLBOX wxEVT_COMMAND_COMBOBOX_SELECTED = wxEVT_COMBOBOX wxEVT_COMMAND_TOOL_RCLICKED = wxEVT_TOOL_RCLICKED wxEVT_COMMAND_TOOL_DROPDOWN_CLICKED = wxEVT_TOOL_DROPDOWN wxEVT_COMMAND_TOOL_ENTER = wxEVT_TOOL_ENTER wxEVT_COMMAND_COMBOBOX_DROPDOWN = wxEVT_COMBOBOX_DROPDOWN wxEVT_COMMAND_COMBOBOX_CLOSEUP = wxEVT_COMBOBOX_CLOSEUP 所有支持的事件类型(上百个)
一个事件是wx.Event类或其子类的一个实例。
事件的绑定和处理:通过使用wx.EvtHandler类中的Bind方法可以将绑定事件的类型,事件对象和事件处理函数关联起来。
代码实战学习:
创建最小的空的wxPython程序
import wx #1 class App(wx.App):#2 def OnInit(self): #3 frame = wx.Frame(parent=None, title='Bare') frame.Show() return True app = App() #4 app.MainLoop() #5
上面的代码的任何一行都不能少,否则将不能工作。这个基本的wxPython程序说明了开发任一wxPython程序所必须的五个基本步骤:
-
导入必须的wxPython包
一旦你导入了wx模块,你就能够创建你的应用程序(application)对象和框架(frame)对象。每个wxPython程序必须有一个application对象和至少一个frame对象。application对象必须是wx.App的一个实例或你在OnInit()方法中定义的一个子类的一个实例。当你的应用程序启动的时候,OnInit()方法将被wx.App父类调用。 -
子类化wxPython应用程序类wx.App
我们通常在OnInit()方法中创建frame对象。上面的wx.Frame接受三个参数,仅第一个(parent=None)是必须的,其余的都有默认值。 调用Show()方法使frame可见,否则不可见。我们可以通过给Show()一个布尔值参数来设定frame的可见性 - 定义一个应用程序的初始化方法
我们没有为我们的应用程序类定义一个__init__()方法。在Python中,这就意味着父方法wx.App.__init()__将在对象创建时被自动调用。这是一个好的事情。如果你定义你自己的__init__()方法,不要忘了调用其基类的__init()__方法,如:
class App(wx.App): def __init__(self): wx.App.__init__(self)
如果你忘了这样做,wxPython将不被初始化并且你的OnInit()方法也将得不到调用。
4、创建一个应用程序类的实例
5、进入这个应用程序的主事件循环
一旦进入主事件循环,控制权将转交给wxPython。wxPythonGUI程序主要响应用户的鼠标和键盘事件。当一个应用程序的所有框架被关闭后,这个app.MainLoop()方法将返回且程序退出。
扩展这个最小的空的wxPython程序
import wx class Frame(wx.Frame): #3 pass class App(wx.App): def OnInit(self): self.frame = Frame(parent=None, title='Spare') #4 self.frame.Show() self.frame.Center() self.SetTopWindow(self.frame) #5 return True if __name__ == '__main__': #6 app = App() app.MainLoop()
- 我们改变了创建frame对象的方法。bare版的程序简单地创建了一个wx.Frame类的实例。在spare版中,我们定义了我们自己的Frame类作为wx.Frame的子类。此时,最终的结果没有什么不同,但是如果你想在你的框架中显示诸如文本、按钮、菜单的话,你可能就想要你自己的Frame类了。
- 我们将对frame实例的引用作为应用程序实例的一个属性
-
在OnInit()方法中,我们调用了这个App类自己的SetTopWindow()方法,并传递给它我们新创建的frame实例。我们不必定义SetTopWindow()方法,因为它继承自wx.App父类。SetTopWindow()方法是一个可选的方法,它让wxPython方法知道哪个框架或对话框将被认为是主要的。一个wxPython程序可以有几个框架,其中有一个是被设计为应用程序的顶级窗口的。
-
这个是Python中通常用来测试该模块是作为程序独立运行还是被另一模块所导入。我们通过检查该模块的__name__属性来实现
创建最终的hello.py程序
import wx class Frame(wx.Frame): #2 wx.Frame子类 """Frame class that displays an image.""" def __init__(self, image, parent=None, id=-1, pos=wx.DefaultPosition, title='Hello, wxPython!'): #3图像参数 """Create a Frame instance and display image.""" #4 显示图像 temp = image.ConvertToBitmap() #转换为位图 size = temp.GetWidth(), temp.GetHeight() wx.Frame.__init__(self, parent, id, title, pos, size) self.bmp = wx.StaticBitmap(parent=self, bitmap=temp) class App(wx.App): #5 wx.App子类 """Application class.""" def OnInit(self): #6 图像处理 image = wx.Image('back_image.jpg', wx.BITMAP_TYPE_JPEG) self.frame = Frame(image) self.frame.Show() self.SetTopWindow(self.frame) return True def main(): #7 app = App() app.MainLoop() if __name__ == '__main__': main()
#2 定义一个wx.Frame的子类,以便我们更容量控制框架的内容和外观。
#3 给我们的框架的构造器增加一个图像参数。这个值通过我们的应用程序类在创建一个框架的实例时提供。同样,我们可以传递必要的值给wx.Frame.__init__()
#4 我们将用wx.StaticBitmap控件来显示这个图像,它要求一个位图。所以我们转换图像到位图。我们也使用图像的宽度和高度创建一个size元组。这个size元组被提供给wx.Frame.__init__()调用,以便于框架的尺寸匹配位图尺寸。
#5 定义一个带有OnInit()方法的wx.App的子类,这是wxPython应用程序最基本的要求。
#6 我们使用与hello.py在同一目录下的名为wxPython.jpg的文件创建了一个图像对象。
#7 main()函数创建一个应用程序的实例并启动wxPython的事件循环。
如何创建和使用一个应用程序对象?
任何wxPython应用程序都需要一个应用程序对象。这个应用程序对象必须是类wx.App或其定制的子类的一个实例。应用程序对象的主要目的是管理幕后的主事件循环。
父类wx.App也定义了一些属性,它们对整个应用程序是全局性的。
创建一个wx.App的子类
四个步骤:
1、定义这个子类
2、在定义的子类中写一个OnInit()方法
3、在你的程序的主要部分创建这个类的一个实例
4、调用应用程序实例的MainLoop()方法。这个方法将程序的控制权转交给wxPython
由于OnInit()方法的存在,并且它是wxPython架构的一部分,所以任何关于你的定制的类的所需的初始化通常都由OnInit()方法管理,而不在Python的__init__方法中。如果由于某些原因你决定需要__init__方法,那么你必须在你的__init__方法中调用父类的__init__方法,如下所示:
wx.App.__init__(self)
通常,你在OnInit()方法中将至少创建一个框架对象,并调用该框架的Show()方法。你也可以有选择地通过调用SetTopWindow()方法来为应用程序指定一个框架作为顶级窗口。顶级窗口被作为那些没有指定父窗口的对话框的默认父窗口。
何时省略wx.App的子类
你没有必要创建你自己的wx.App子类,你通常想这样做是为了能够在OnInit()方法中创建你的顶级框架。 通常,如果在系统中只有一个框架的话,避免创建一个wx.App子类是一个好的主意。
理解应用程序对象的生命周期
你的wxPython应用程序对象的生命周期开始于应用程序实例被创建时,在最后一个应用程序窗口被关闭时结束。这个没有必要与你的wxPython应用程序所在的Python脚本的开始和结束相对应。Python脚本可以在wxPython应用程序创建之前选择做一动作,并可以在wxPython应用程序的MainLoop()退出后做一些清理工作。然而所有的wxPython动作必须在应用程序对象的生命周期中执行。正如我们曾提到过的,这意味你的主框架对象在wx.App对象被创建之前不能被创建。(这就是为什么我们建议在OnInit()方法中创建顶级框架——因为这样一来,就确保了这个应用程序已经存在。)
下图所示,创建应用程序对象触发OnInit()方法并允许新的窗口对象被创建。在OnInit()之后,这个脚本调用MainLoop()方法,通知wxPython事件现在正在被处理。在窗口被关闭之前应用程序继续它的事件处理。当所有顶级窗口被关闭后,MainLoop()函数返回同时应用程序对象被注销。这之后,这个脚本能够关闭其它的可能存在的连接或线程。
如何定向wxPython程序的输出?
(未学习)
如何关闭wxPython应用程序?
当你的应用程序的最后的顶级窗口被用户关闭时,wxPython应用程序就退出了。我们这里所说的顶层窗口是指任何没有父亲的框架,并不只是使用SetTopWindow()方法设计的框架。
管理正常的关闭
管理紧急关闭
(以上未学习)
如何创建和使用顶级窗口对象?
在你的应用程序中一个顶级窗口对象是一个窗口部件(通常是一个框架),它不被别的窗口部件所包含。顶级窗口对象通常是你的应用程序的主窗口,它包含用户与之交互的窗口部件和界面对象。当所有的顶级窗口被关闭时应用程序退出。
你的应用程序至少必须有一个顶级窗口对象。顶级窗口对象通常是类wx.Frame的子类,尽管它也可以是wx.Dialog的子类。大多数情况下,你将为了使用为你的应用程序定义定制的wx.Frame的子类。然而,这儿也存在一定数量的预定义的wx.Dialog的子类,它们提供了许多你可能会在一个应用程序中遇到的典型的对话框。
这儿可能有一个名称上的混淆,那就是“顶级窗口”。一般意义上的顶级窗口是指在你的应用程序中任何没有父容器的窗口部件。你的应用程序必须至少有一个,但是,只要你喜欢可以有多个。但是它们中只有一个可以通过使用SetTopWindow()被wxPython作为主顶级窗口。如果你没有使用SetTopWindow()指定主顶级窗口,那么在wx.App的顶级窗口列表中的第一个框架将被认为是这个主顶级窗口。一个应用程序一次只能有一主顶级窗口。
使用wx.Frame
按照wxPython中的说法,框架就是用户通常称的窗口。那就是说,框架是一个容器,用户可以将它在屏幕上任意移动,并可将它缩放,它通常包含诸如标题栏、菜单等等。在wxPython中,wx.Frame是所有框架的父类。这里也有少数专用的wx.Frame子类,你可以使用它们。
当你创建wx.Frame的子类时,你的类应该调用其父类的构造器wx.Frame.__init__()。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:框架的内在的名字。以后你可以使用它来寻找这个窗口。
记住,这些参数将被传递给父类的构造器方法:wx.Frame.__init__()。
创建wx.Frame子类的方法如下所示:
class MyFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "My Friendly Window", (100, 100), (100, 100))
使用wxPython的ID
在wxPython中,ID号是所有窗口部件的特征。在一个wxPython应用程序中,每个窗口部件都有一个窗口标识。在每一个框架内,ID号必须是唯一的,但是在框架之间你可以重用ID号。然而,我们建议你在你的整个应用程序中保持ID号的唯一性,以防止处理事件时产生错误和混淆。在wxPython中也有一些标准的预定义的ID号,它们有特定的意思(例如,wx.ID_OK和wx.ID_CANCEL是对话框中的OK和Cancel按钮的ID号)。在你的应用程序中重用标准的ID号一般没什么问题,只要你在预期的方式中使用它们。在wxPython中,ID号的最重要的用处是在指定的对象发生的事件和响应该事件的回调函数之间建立唯一的关联。
有三种方法来创建一个窗口部件使用的ID号:
1、明确地给构造器传递一个正的整数 2、使用wx.NewId()函数 3、传递一个全局常量wx.ID_ANY或-1给窗口部件的构造器
使用wx.Size和wx.Point
在你的应用程序中当一个wx.Point或wx.Size实例被要求的时候(例如在另一个对象的构造器中),你不必显式地创建这个实例。你可以传递一个元组给构造器,wxPython将隐含地创建这个wx.Point或wx.Size实例:
frame = wx.Frame(None, -1, pos=(10, 10), size=(100, 100))
使用wx.Frame的样式
每个wxPython窗口部件都要求一个样式参数。
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)
如何为一个框架增加对象和子窗口?
给框架增加窗口部件
# 当点击close按钮时,这个窗口将关闭且应用程序将退出。 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.App() frame = InsertFrame(parent=None, id=-1) frame.Show() app.MainLoop()
类InsertFrame的方法__init__创建了两个子窗口。第一个是wx.Panel,它是其它窗口的容器,它自身也有一点功能。第二个是wx.Button,它是一个平常按钮。接下来,按钮的单击事件和窗口的关闭事件被绑定到了相应的函数,当事件发生时这相应的函数将被调用执行。
大多数情况下,你将创建一个与你的wx.Frame大小一样的wx.Panel实例以容纳你的框架上的所有的内容。这样做可以让定制的窗口内容与其他如工具栏和状态栏分开。 通过tab按钮,可以遍历wx.Panel中的元素,wx.Frame不能。在wxPython中,你只需在子窗口被创建时指定父窗口,这个子窗口就隐式地增加到父对象中了。
你可能想知道,为什么wx.Button被创建时使用了明确的位置和尺寸,而wx.Panel没有。在wxPython中,如果只有一个子窗口的框架被创建,那么那个子窗口被自动重新调整尺寸去填满该框架的客户区域。这个自动调整尺寸将覆盖关于这个子窗口的任何位置和尺寸信息,但仅适用于框架内或对话框内的只有唯一元素的情况。这里按钮是panel的元素,而不是框架的,所以要使用指定的尺寸和位置。如果没有为这个按钮指定尺寸和位置,它将使用默认的位置(panel的左上角)和基于按钮标签的长度的尺寸。
显式地指定所有子窗口的位置和尺寸是十分乏味的。更重要的是,当用户调整窗口大小的时候,这使得子窗口的位置和大小不能作相应调整。为了解决这两个问题,wxPython使用了称为sizers的对象来管理子窗口的复杂布局。
给框架增加菜单栏、工具栏和状态栏。
(未学习)
如何使用一般的对话框?
1、消息对话框
2、文本输入对话框
如果你想从用户那里得到单独一行文本,你可能使用类wx.TextEntryDialog。下面的片断创建了一个文本输入域,当用户单击OK按钮退出时,获得用户输入的值:
import wx #1 class App(wx.App):#2 def __init__(self): wx.App.__init__(self) def OnInit(self): #3 frame = wx.Frame(parent=None, title='Bare') dlg = wx.TextEntryDialog(None, "Who is buried in Grant's tomb?", 'A Question', 'Cary Grant') if dlg.ShowModal() == wx.ID_OK: response = dlg.GetValue() frame.Show() return True app = App() #4 app.MainLoop() #5
3、从一个列表中选择
你可以让用户只能从你所提供的列表中选择,你可以使用类wx.SingleChoiceDialog。下面是一个简单的用法:
import wx #1 class App(wx.App):#2 def __init__(self): wx.App.__init__(self) def OnInit(self): #3 frame = wx.Frame(parent=None, title='Bare') dlg = wx.SingleChoiceDialog(None, 'What version of Python are you using?', 'Single Choice', ['1.5.2', '2.0', '2.1.3', '2.2', '2.3.1']) if dlg.ShowModal() == wx.ID_OK: response = dlg.GetStringSelection() frame.Show() return True app = App() #4 app.MainLoop() #5
wx.SingleChoiceDialog的参数类似于文本输入对话框,只是以字符串的列表代替了默认的字符串文本。要得到所选择的结果有两种方法,GetSelection()方法返回用户选项的索引,而GetStringSelection()返回实际所选的字符串。
总结
-
wxPython程序的实现基于两个必要的对象:应用程序对象和顶级窗口。任何wxPython应用程序都需要去实例化一个wx.App,并且至少有一个顶级窗口。
-
应用程序对象包含OnInit()方法,它在启动时被调用。在这个方法中,通常要初始化框架和别的全局对象。wxPython应用程序通常在它的所有的顶级窗口被关闭或主事件循环退出时结束。
-
应用程序对象也控制wxPython文本输出的位置。默认情况下,wxPython重定向stdout和stderr到一个特定的窗口。这个行为使得诊断启动时产生的错误变得困难了。但是我们可以通过让wxPython把错误消息发送到一个文件或控制台窗口来解决。
-
一个wxPython应用程序通常至少有一个wx.Frame的子类。一个wx.Frame对象可以使用style参数来创建组合的样式。每个wxWidget对象,包括框架,都有一个ID,这个ID可以被应用程序显式地赋值或由wxPython生成。子窗口是框架的内容,框架是它的双亲。通常,一个框架包含一个单一的wx.Panel,更多的子窗口被放置在这个Panel中。框架的唯一的子窗口的尺寸自动随其父框架的尺寸的改变而改变。框架有明确的关于管理菜单栏、工具栏和状态栏的机制。
- 尽管你将使用框架做任何复杂的事情,但当你想简单而快速地得到来自用户的信息时,你可以给用户显示一个标准的对话窗口。对于很多任务都有标准的对话框,包括警告框、简单的文本输入框和列表选择框等等。
要理解事件,我们需要知道哪些术语?
事件(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对象。为了被调用,所有事件处理器必须用一个事件绑定器注册。
wx.EvtHandler:一个wxPython类,它允许它的实例在一个特定类型,一个事件源,和一个事件处理器之间创建绑定。注意,这个类与先前定义的事件处理函数或方法不是同一个东西。
什么是事件驱动编程?
事件驱动程序结构的主要特点:
1、在初始化设置之后,程序的大部分时间花在了一个空闭的循环之中。进入这个循环就标志着程序与用户交互的部分的开始,退出这个循环就标志结束。在wxPython中,这个循环的方法是:wx.App.MainLoop(),并且在你的脚本中显式地被调用。当所有的顶级窗口关闭时,主循环退出。
2、程序包含了对应于发生在程序环境中的事情的事件。事件通常由用户的行为触发,但是也可以由系统的行为或程序中其他任意的代码。在wxPython中,所有的事件都是类wx.Event或其子类的一个实例。每个事件都有一个事件类型属性,它使得不同的事件能够被辨别。例如,鼠标释放和鼠示按下事件都被认为是同一个类的实例,但有不同的事件类型。
3、作为这个空闭的循环部分,程序定期检查是否有任何请求响应事情发生。有两种机制使得事件驱动系统可以得到有关事件的通知。最常被wxPython使用的方法是,把事件传送到一个中心队列,由该队列触发相应事件的处理。另一种方法是使用轮询的方法,所有可能引发事件的事件主被主过程定期查询并询问是否有没有处理的事件。
4、当事件发生时,基于事件的系统试着确定相关代码来处理该事件,如果有,相关代码被执行。在wxPython中,原系统事件被转换为wx.Event实例,然后使用wx.EvtHandler.ProcessEvent()方法将事件分派给适当的处理器代码。下图呈现了这个过程:
事件机制的组成部分是事件绑定器对象和事件处理器。事件绑定器是一个预定义的wxPython对象。每个事件都有各自的事件绑定器。事件处理器是一个函数或方法,它要求一个wxPython事件实例作为参数。当用户触发了适当的事件时,一个事件处理器被调用。
编写事件处理器
在你的wxPython代码中,事件和事件处理器是基于相关的窗口部件的。例如,一个按钮被单击被分派给一个基于该按钮的专用的事件处理器。为了要把一个来自特定窗口部件的事件绑定到一个特定的处理器方法,你要使用一个绑定器对象来管理这个连接。例如:
self.Bind(wx.EVT_BUTTON, self.OnClick, aButton)
上例使用了预定义的事件绑定器对象wx.EVT_BUTTON来将aButton对象上的按钮单击事件与方法self.OnClick相关联起来。这个Bind()方法是wx.EvtHandler的一个方法,wx.EvtHandler是所有可显示对象的父类。因此上例代码行可以被放置在任何显示类。
设计事件驱动程序
对于事件驱动程序的设计,由于没有假设事件何时发生,所以程序员将大量的控制交给了用户。你的wxPython程序中的大多数代码通过用户或系统的行为被直接或间接地执行。例如在用户选择了一个菜单项、或按下一个工具栏按钮、或按下了特定的按键组合后,你的程序中有关保存工作的代码被执行了。
另一方面,事件驱动体系通常是分散性的。响应一个窗口部件事件的代码通常不是定义在该部件的定义中的。例如,响应一个按钮单击事件的代码不必是该按钮定义的一部分,而可以存在在该按钮所附的框架中或其它地方。
事件触发
wx.CloseEvent:当一个框架关闭时触发。这个事件的类型分为一个通常的框架关闭和一个系统关闭事件。 wx.CommandEvent:与窗口部件的简单的各种交互都将触发这个事件,如按钮单击、菜单项选择、单选按钮选择。这些交互有它各自的事件类型。许多更复杂的窗口部件,如列表等则定义wx.CommandEvent的子类。事件处理系统对待命令事件与其它事件不同。 wx.KeyEvent:按键事件。这个事件的类型分按下按键、释放按键、整个按键动作。 wx.MouseEvent:鼠标事件。这个事件的类型分鼠标移动和鼠标敲击。对于哪个鼠标按钮被敲击和是单击还是双击都有各自的事件类型。 wx.PaintEvent:当窗口的内容需要被重画时触发。wx.SizeEvent:当窗口的大小或其布局改变时触发。 wx.TimerEvent:可以由类wx.Timer类创建,它是定期的事件。
如何将事件绑定到处理器?
事件绑定器被用于将一个wxPython窗口部件与一个事件对象和一个处理器函数连接起来。这个连接使得wxPython系统能够通过执行处理器函数中的代码来响应相应窗口部件上的事件。
使用wx.EvtHandler的方法工作
经常使用的wx.EvtHandler的方法是Bind(),它创建事件绑定。该方法的用法如下:
Bind(event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY)
Bind()函数将一个事件和一个对象与一个事件处理器函数关联起来。参数event是必选的,参数handler也是必选的,它是一个可调用的Python对象,通常是一个被绑定的方法或函数。参数handler可以是None,这种情况下,事件没有关联的处理器。参数source是产生该事件的源窗口部件,这个参数在触发事件的窗口部件与用作事件处理器的窗口部件不相同时使用。通常情况下这个参数使用默认值None,这是因为你一般使用一个定制的wx.Frame类作为处理器,并且绑定来自于包含在该框架内的窗口部件的事件。父窗口的__init__是一个用于声明事件绑定的方便的位置。但是如果父窗口包含了多个按钮敲击事件源(比如OK按钮和Cancel按钮),那么就要指定source参数以便wxPython区分它们(?)。下面是该方法的一个例子:
self.Bind(wx.EVT_BUTTON, self.OnClick, button)
演示了使用参数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()
说明:
#1 这行绑定框架关闭事件到self.OnCloseWindow方法。由于这个事件通过该框架触发且用于帧,所以不需要传递一个source参数。
#2 这行将来自按钮对象的按钮敲击事件绑定到self.OnCloseMe方法。这样做是为了让wxPython能够区分在这个框架中该按钮和其它按钮所产生的事件。
Bind()方法中的参数id和id2使用ID号指定了事件的源。一般情况下这没必要,因为事件源的ID号可以从参数source中提取。但是某些时候直接使用ID是合理的。例如,如果你在使用一个对话框的ID号,这比使用窗口部件更容易。如果你同时使用了参数id和id2,你就能够以窗口部件的ID号形式将这两个ID号之间范围的窗口部件绑定到事件。这仅适用于窗口部件的ID号是连续的。
wxPython是如何处理事件的?
代码如下:
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.App() frame = MouseEventFrame(parent=None, id=-1) frame.Show() app.MainLoop()
说明:
MouseEventFrame包含了一个位于中间的按钮。在其上敲击鼠标将导致框架的背景色改变为绿色。#1绑定了鼠标敲击事件。当鼠标指针位于这个按钮上时,按钮上的标签将改变,这用#2绑定。当鼠标离开这个按钮时,标签变回原样,这用#3绑定。
通过观察上面的鼠标事件例子,我们引出了在wxPython中的事件处理的一些问题。#1中,按钮事件由附着在框架上的按钮触发,那么wxPython怎么知道在框架对象中查找绑定而不是在按钮对象上呢?在#2和#3中,鼠标的进入和离开事件被绑定到了按钮,为什么这两个事件不能被绑到框架上呢。这些问题将通过检查wxPython用来决定如何响应事件的过程来得到回答。
Skip():在wxPython中,如果一个动作会触发多个事件,那么应该使用Skip方法来保证每个都被处理到。其实为了保证不遗漏,在每个事件处理的方法中都调用Skip方法应该是一种良好的习惯。
理解事件处理过程
第一步,创建事件
第二步,确定事件对象是否被允许处理事件
第三步 定位绑定器对象
第四步 决定是否继续处理
第五步 决定是否展开
使用Skip()方法
事件的第一个处理器函数被发现并执行完后,该事件处理将终止,除非在处理器返回之前调用了该事件的Skip()方法。调用Skip()方法允许另外被绑定的处理器被搜索,在某些情况下,你想继续处理事件,以便原窗口部件的默认行为和你定制的处理能被执行。
# 同时响应鼠标按下和按钮敲击 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.App() frame = DoubleEventFrame(parent=None, id=-1) frame.Show() app.MainLoop()
#1 这行绑定按钮敲击事件到OnButtonClick()处理器,这个处理器改变框架的背景色。
#2 这行绑定鼠标左键按下事件到OnMouseDown()处理器,这个处理器改变按钮的标签文本。由于鼠标左键按下事件不是命令事件,所以它必须被绑定到按钮(self.button.Bind)而非框架(self.Bind)。
分析:鼠标按下的时候没有变色,文本框内容改变,说明调用了OnMouseDown方法;鼠标按下再释放的时候,变色,说明调用了OnButtonClick方法。如果在OnMouseDown方法中没有Skip(),则事件会被终止,即不会变色。
当用户在按钮上敲击鼠标时,通过直接与底层操作系统交互,鼠标左键按下事件首先被产生。通常情况下,鼠标左键按下事件改变按钮的状态,随着鼠标左键的释放,产生了wx.EVT_BUTTON敲击事件。由于行#3的Skip()语句,DoubleEventFrame维持处理。没有Skip()语句,事件处理规则发现在#2创建的绑定,而在按钮能产生wx.EVT_BUTTON事件之前停止。由于Skip()的调用,事件处理照常继续,并且按钮敲击被创建。
记住,当绑定低级事件时如鼠标按下或释放,wxPython期望捕获这些低级事件以便生成进一步的事件,为了进一步的事件处理,你必须调用Skip()方法,否则进一步的事件处理将被阻止。
在应用程序对象中还包含哪些其它的属性?
(未学习)
如何创建自己的事件?
创建自定义事件的步骤:
1、定义一个新的事件类,它是wxPython的wx.PyEvent类的子类。如果你想这个事件被作为命令事件,你可以创建wx.PyCommandEvent的子类。像许多wxPython中的覆盖一样,一个类的py版本使得wxWidget系统明白用Python写的覆盖C++方法的方法。
2、创建一个事件类型和一个绑定器对象去绑定该事件到特定的对象。
3、添加能够建造这个新事件实例的代码,并且使用ProcessEvent()方法将这个实例引入事件处理系统。一旦该事件被创建,你就可以像使用其它的wxPython事件一样创建绑定和处理器方法。
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()
说明:
#1 这个关于事件类的构造器声明为wx.PyCommandEvent的一个子类。 wx.PyEvent和wx.PyCommandEvent是wxPython特定的结构,你可以用来创建新的事件类并且可以把C++类和你的Python代码连接起来。如果你试图直接使用wx.Event,那么在事件处理期间wxPython不能明白你的子类的新方法,因为C++事件处理不了解该Python子类。如果你wx.PyEvent,一个对该Python实例的引用被保存,并且以后被直接传递给事件处理器,使得该Python代码能被使用。
#2 全局函数wx.NewEventType()的作用类似于wx.NewId();它返回一个唯一的事件类型ID。这个唯一的值标识了一个应用于事件处理系统的事件类型。
#3 这个绑定器对象的创建使用了这个新事件类型作为一个参数。这第二个参数的取值位于[0,2]之间,它代表wxId标识号,该标识号用于wx.EvtHandler.Bind()方法去确定哪个对象是事件的源。
#4 为了创建这个新的更高级的命令事件,程序必需响应特定的用户事件,例如,在每个按钮对象上的鼠标左键按下。依据哪个按钮被敲击,该事件被绑定到OnLeftClick()和OnRightClick()方法。处理器设置了布尔值,以表明按键是否被敲击。
#5 #6 Skip()的调用允许在该事件处理完成后的进一步处理。在这里,这个新的事件不需要skip调用;它在事件处理器完成之前被分派了(self.OnClick())。但是所有的鼠标左键按下事件需要调用Skip(),以便处理器不把最后的按钮敲击挂起。这个程序没有处理按钮敲击事件,但是由于使用了Skip(),wxPython在敲击期间使用按钮敲击事件来正确地绘制按钮。如果被挂起了,用户将不会得到来自按钮按下的反馈。
#7 如果两个按钮都被敲击了,该代码创建这个新事件的一个实例。事件类型和两个按钮的ID作为构造器的参数。通常,一个事件类可以有多个事件类型,尽管本例中不是这样。
#8 ProcessEvent()的调用将这个新事件引入到事件处理系统中。GetEventHandler()调用返回wx.EvtHandler的一个实例。大多数情况下,返回的实例是窗口部件对象本身,但是如果其它的wx.EvtHandler()方法已经被压入了事件处理器堆栈,那么返回的将是堆栈项的项目。
#9 该自定义的事件的绑定如同其它事件一样,在这里使用#3所创建的绑定器。
#10 这个例子的事件处理器函数改变窗口的标题以显示敲击数。
总结
1、wxPython应用程序使用基于事件的控制流。应用程序的大部分时间花费在一个主循环中,等待事件并分派它们到适当的处理器函数。
2、所有的wxPython事件是wx.Event类的子类。低级的事件,如鼠标敲击,被用来建立高级的事件,如按钮敲击或菜单项选择。这些由wxPython窗口部件引起的高级事件是类wx.CommandEvent的子类。大多的事件类通过一个事件类型字段被进一步分类,事件类型字段区分事件。
3、为了捕获事件和函数之间的关联,wxPython使用类wx.PyEventBinder的实例。类wx.PyEventBinder有许多预定义的实例,每个都对应于一个特定的事件类型。每个wxPython窗口部件都是类wx.EvtHandler的子类。类wx.EvtHandler有一个方法Bind(),它通常在初始化时被调用,所带参数是一个事件绑定器实例和一个处理器函数。根据事件的类型,别的wxPython对象的ID可能也需要被传递给Bind()调用。
4、事件通常被发送给产生它们的对象,以搜索一个绑定对象,这个绑定对象绑定事件到一个处理器函数。如果事件是命令事件,这个事件沿容器级向上传递直到一个窗口部件被发现有一个针对该事件类型的处理器。一旦一个事件处理器被发现,对于该事件的处理就停止,除非这个处理器调用了该事件的Skip()方法。你可以允许多个处理器去响应一个事件,或去核查该事件的所有默认行为。主循环的某些方面可以使用wx.App的方法来控制。
5、在wxPython中可以创建自定义事件,并作为定制(自定义)的窗口部件的行为的一部分。自定义的事件是类wx.PyEvent的子类,自定义的命令事件是类wx.PyCommandEvent的子类。为了创建一个自定义事件,新的类必须被定义,并且关于每个事件类型(这些事件类型被这个新类所管理)的绑定器必须被创建。最后,这个事件必须在系统的某处被生成,这通过经由ProcessEvent()方法传递一个新的实例给事件处理器系统来实现。
1、顶级窗口(Frame组件)
# 实例化一个主循环 app = wx.App() # 实例化一个窗口 frame = wx.Frame(None, title="Test",size=(600, 600)) # 设置窗口图标 wx_icon = wx.Icon(name="test.ico", type=wx.BITMAP_TYPE_ANY) frame.SetIcon(wx_icon) # 设置选择文件按钮 open_button = wx.Button(frame, label="文件", pos=(10, 5), size=(80, 40)) log_show = "" # 调用窗口展示功能 frame.Show() # 启动主循环 app.MainLoop()
2、附属窗口(Panel组件)
file_path = "D:\Develop\back-end\workkspace\dst-file" panel1 = wx.Panel(parent=frame, pos=(1, 469), size=(600, 30)) label = wx.StaticText(parent=panel1, label="文件路径:{}".format(file_path), pos=(1, 5), size=(300, 30))
3、标签组件(StaticText)
4、