這本書講的如何應用wxPhython模塊。這個模塊是python中比較有名的GUI模塊。
要想用好這個模塊就要明白這個模塊核心是一個wx.App的類,一個是wx.Frame的類。
基礎的應用構架如下
# -*- coding: utf-8 -*-
#!/usr/bin/env python #1
"""Spare.py is a starting point for a wxPython program.""" #2
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.SetTopWindow(self.frame) #5
return True
if __name__ == "__main__": #6
app = App()
app.MainLoop()
#1 這行看似注釋,但是在如linux和unix等操作系統上,它告訴操作系統如何找到執行程序的解釋器。如果這個程序被給予的可執行權限(例如使用chmod命令),我們可以在命令行下僅僅鍵入該程序的名字來運行這個程序:這行在其它的操作系統上將被忽略。但是包含它可以實現代碼的跨平台。
#2 這是文檔字符串,當模塊中的第一句是字符串的時候,這個字符串就成了該模塊的文檔字符串並存儲在該模塊的__doc__屬性中。你能夠在你的代碼中、某些開發平台、甚至交互模式下運行的Python解釋器中訪問文檔字符串。
#3 我們改變了你們創建frame對象的方法。 bare版的程序簡單地創建了一個wx.Frame類的實例。在spare版中,我們定義了我們自己的Frame類作為wx.Frame的子類。此時,最終的結果沒有什麼不同,但是如果你想在你的框架中顯示諸如文本、按鈕、菜單的話,你可能就想要你自己的Frame類了。
#4 我們將對frame實例的引用作為應用程序實例的一個屬性.
#5 在OnInit()方法中,我們調用了這個App類自己的SetTopWindow()方法,並傳遞給它我們新創建的frame實例。我們不必定義SetTopWindow()方法,因為它繼承自wx.App父類。 SetTopWindow()方法是一個可選的方法,它讓wxPython方法知道哪個框架或對話框將被認為是主要的。一個wxPython程序可以有幾個框架,其中有一個是被設計為應用程序的頂級窗口的。
#6 這個是Python中通常用來測試該模塊是作為程序獨立運行還是被另一模塊所導入。我們通過檢查該模塊的__name__屬性來實現:
讓我們來仔細說明一下這兩個基礎對象(wx.App和wx.Frame)。這個應用程序(wx.App的對象或其子類的對象)對象管理主事件循環,主事件循環是你的wxPython程序的動力。啟動主事件循環是應用程序對象的工作。沒有應用程序對象,你的wxPython應用程序將不能運行。這個事件循環響應於窗口系統事件並分配它們給適當的事件處理器。頂級窗口(wx.Frame對象或者其子類的對象)通常管理最重要的數據,控制並呈現給用戶。下圖顯示了這兩個基礎對象和你的應用程序的其它部分這間的關系:
如何創建和使用一個應用程序對象
像第一個程序那樣:
你需要執行四個步驟:
1、定義這個子類
2、在定義的子類中寫一個OnInit()方法
3、在你的程序的主要部分創建這個類的一個實例
4、調用應用程序實例的MainLoop()方法。這個方法將程序的控制權轉交給wxPython
理解應用程序對象的生命周期
你的wxPython應用程序對象的生命周期開始於應用程序實例被創建時,在最後一個應用程序窗口被關閉時結束。這個沒有必要與你的wxPython應用程序所在的Python腳本的開始和結束相對應。 Python腳本可以在wxPython應用程序創建之前選擇做一動作,並可以在wxPython應用程序的MainLoop()退出後做一些清理工作。然而所有的wxPython動作必須在應用程序對象的生命周期中執行。正如我們曾提到過的,這意味你的主框架對象在wx.App對象被創建之前不能被創建。
下圖所示,創建應用程序對象觸發OnInit()方法並允許新的窗口對象被創建。在OnInit()之後,這個腳本調用MainLoop()方法,通知wxPython事件現在正在被處理。在窗口被關閉之前應用程序繼續它的事件處理。當所有頂級窗口被關閉後,MainLoop()函數返回同時應用程序對象被注銷。這之後,這個腳本能夠關閉其它的可能存豐的連接或線程。
如何定向wxPython程序的輸出?
所有的Python程序都能夠通過兩種標准流來輸出文本:分別是標准輸出流sys.stdout和標准錯誤流sys.stderr。通常, Python腳本定向標准輸出流到它所運行的控制台。然而,當你的應用程序對象被創建時,你可以決定使用wxPython
控制標准流並重定向輸出到一個窗口。在Windows下,這個重定向行為是wxPython的默認行為。而在Unix系統中,默認情況下, wxPython不控制這個標准流。在所有的系統中,當應用程序對象被創建的時候,重定向行為可以被明確地指定。我們推薦利用這個特性並總是指定重定向行為來避免不同平台上的不同行為產生的任何問題。
如果wxPython控制了標准流,那麼經由任何方法發送到流的文本被重定向到一個wxPython的框架。在wxPyton應用程序開始之前或結束之後發送到流的文本將按照Python通常的方法處理(輸出到控制台)。下例同時演示了應用程序的生命周期和stdout/stderr重定向:
# -*- coding: utf-8 -*-
#!/usr/bin/env python
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"
#1 這行創建了應用程序對象。這行之後,所有發送到stderr或stdout的文本都可被wxPython重定向到一個框架。參數redirect=True決定了是否重定向。
#2 運行的時候,應用程序創建了一個空的框架和也生成了一個用於重定向輸出的框架。圖示如下:
為了修改這個行為, wxPython允許你在創建應用程序時設置兩個參數。第一個參數是redirect,如果值為True,則重定向到框架,如果值為False,則輸出到控制台。如果參數redirect為True,那麼第二個參數filename也能夠被設置,這樣的話,輸出被重定向到filename所指定的文件中而不定向到wxPython框架。因此,如果我們將上例中的app = App(redirect=True)改為***app = App(False),***
則輸出將全部到控制台中:
我們再作一個改變:
app = App(True, ”output”)
這將導致所有的應用程序創建後的輸出重定向到名為output的文件中。而“App__init”和“after MainLoop”消息仍將發送到控制台,這是因為它們產生在wx.App對象控制流的時期之外。
5. 如何關閉wxPython應用程序?
在關閉的過程期間, wxPython關心的是刪除所有的它的窗口和釋放它們的資源。你可以在退出過程中定義一個鉤子來執行你自己的清理工作。由於你的wx.App子類的OnExit()方法在最後一個窗口被關閉後且在wxPython的內在的清理過程之前被調用,你可以使用OnExit()方法來清理你創建的任何非wxPython資源(例如一個數據庫連接)。即使使用了wx.Exit()來關閉wxPython程序, OnExit()方法仍將被觸發。
如果由於某種原因你想在最後的窗口被關閉後wxPython應用程序仍然可以繼續,你可以使用wx.App的SetExitOnFrameDelete(flag)方法來改變默認的行為。如果flag參數設置為False,那麼最後的窗口被關閉後wxPython應用程序仍然會繼續運行。這意味著wx.App實例將繼續存活,並且事件循環將繼續處理事件,比如這時你還可以創建所有新的這裡所謂的頂級窗口。 wxPython應用程序將保持存活直到全局函數wx.Exit()被明確地調用。
6. 如何創建和使用頂級窗口對象?
在你的應用程序中一個頂級窗口對象是一個窗口部件(通常是一個Frame),它不被別的窗口部件所包含。頂級窗口對象通常是你的應用程序的主窗口,它包含用戶與之交互的窗口部件和界面對象。當所有的頂級窗口被關閉時應用程序退出。
你的應用程序至少必須有一個頂級窗口對象。頂級窗口對象通常是類wx.Frame的子類,盡管它也可以是wx.Dialog的子類。大多數情況下,你將為了使用為你的應用程序定義定制的wx.Frame的子類。然而,這兒也存在一定數量的預定義的wx.Dialog的子類,它們提供了許多你可能會在一個應用程序中遇到的典型的對話框。
這兒可能有一個名稱上的混淆,那就是“頂級窗口”。一般意義上的頂級窗口是指在你的應用程序中任何沒有父容器的窗口部件。你的應用程序必須至少有一個,但是,只要你喜歡可以有多個。但是它們中只有一個可以通過使用SetTopWindow()被wxPython作為主頂級窗口。如果你沒有使用SetTopWindow()指定主頂級窗口,那麼在wx.App的頂級窗口列表中的第一個框架將被認為是這個主頂級窗口。因此,明確地定義一個主頂級窗口不總是必要的,例如,你只有一個頂級窗口的時候。反復調用SetTopWindow()將反復改變當前的主頂級窗口,因為一個應用程序一次只能有一主頂級窗口。
當你創建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給窗口部件的構造器
7. 如何為一個框架增加對象和子窗口?
下圖顯示了一個定制的wx.Frame的子類,名為InsertFrame。當點擊close按鈕時,這個窗口將關閉且應用程序將退出。
# -*- coding: utf-8 -*-
#!/usr/bin/env python
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()
類InsertFrame的方法__init__創建了兩子窗口。第一個是wx.Panel,它是其它窗口的容器,它自身也有一點功能。第二個是wx.Button,它是一個平常按鈕。接下來,按鈕的單擊事件和窗口的關閉事件被綁定到了相應的函數,當事件發生時這相應的函數將被調用執行。
大多數情況下,你將創建一個與你的wx.Frame大小一樣的wx.Panel實例以容納你的框架上的所有的內容。這樣做可以讓定制的窗口內容與其他如工具欄和狀態欄分開。 通過tab按鈕,可以遍歷wx.Panel中的元素, wx.Frame不能。
你不必像使用別的UI工具包那樣,你不需要顯式調用一個增加方法來向雙親中插入一個子窗口。在wxPython中,你只需在子窗口被創建時指定父窗口,這個子窗口就隱式地增加到父對象中了,例如上程序所示。
你可能想知道在上程序中,為什麼wx.Button被創建時使用了明確的位置和尺寸,而wx.Panel沒有。在wxPython中,如果只有一個子窗口的框架被創建,那麼那個子窗口(例2.3中是wx.Panel)被自動重新調整尺寸去填滿該框架的客戶區域。這個自動調整尺寸將覆蓋關於這個子窗口的任何位置和尺寸信息,盡管關於子窗口的信息已被指定,這些信息將被忽略。這個自動調整尺寸僅適用於框架內或對話框內的只有唯一元素的情況。這裡按鈕是panel的元素,而不是框架的,所以要使用指定的尺寸和位置。如果沒有為這個按鈕指定尺寸和位置,它將使用默認的位置(panel的左上角)和基於按鈕標簽的長度的尺寸。
顯式地指定所有子窗口的位置和尺寸是十分乏味的。更重要的是,當用戶調整窗口大小的時候,這使得子窗口的位置和大小不能作相應調整。為了解決這兩個問題, wxPython使用了稱為sizers的對象來管理子窗口的復雜布局。
# -*- coding: utf-8 -*-
#!/usr/bin/env python
import wx
import wx.py.images as images
from PIL import Image
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, "&File")
menu2 = wx.Menu()
#6 創建菜單的項目
menu2.Append(wx.NewId(), "&Copy", "Copy in status bar")
menu2.Append(wx.NewId(), "C&ut", "")
menu2.Append(wx.NewId(), "Paste", "")
menu2.AppendSeparator()
menu2.Append(wx.NewId(), " & Options...", "Display Options")
menuBar.Append(menu2, " & Edit") # 在菜單欄上附上菜單
self.SetMenuBar(menuBar) # 在框架上附上菜單欄
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = ToolbarFrame(parent=None, id=-1)
frame.Show()
app.MainLoop()
#1:這行創建了一個狀態欄,它是類wx.StatusBar的實例。它被放置在框架的底部,寬度與框架相同,高度由操作系統決定。狀態欄的目的是顯示在應用程序中被各種事件所設置的文本。
#2:創建了一個wx.ToolBar的實例,它是命令按鈕的容器。它被自動放置在框架的頂部
#3:有兩種方法來為你工具欄增加工具,這行使用了參數較少的一種:AddSimpleTool()。參數分別是ID,位圖,該工具的短的幫助提示文本,顯示在狀態欄中的該工具的長的幫助文本信息。(此刻不要考慮位圖從哪兒來)
#4: Realize()方法告訴工具欄這些工具按鈕應該被放置在哪兒。這是必須的。
#6:創建菜單的項目,其中參數分別代表ID,選項的文本,當鼠標位於其上時顯示在狀態欄的文本
1. 什麼是事件驅動編程?
事件就是發生在你的系統中的事,你的應用程序通過觸發相應的功能以響應它。事件可以是低級的用戶動作,如鼠標移動或按鍵按下,也可以是高級的用戶動作(定義在wxPython的窗口部件中的),如單擊按鈕或菜單選擇。事件可以產生自系統,如關機。你甚至可以創建你自己的對象去產生你自己的事件。 wxPython應用程序通過將特定類型的事件和特定的一塊代碼相關聯來工作,該代碼在響應事件時執行。事件被映射到代碼的過程稱為事件處理。
術語,很多都是以event開頭的。下表是我們將要用到的術語的一個快速參考:
事件(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類,它允許它的實例在一個特定類型,一個事件源,和一個事件處理器之間創建綁定。注意,這個類與先前定義的事件處理函數或方法不是同一個東西。
事件驅動程序主要是一個控制結構,它接受事件並響應它們。 wxPython程序(或任何事件驅動程序)的結構與平常的Python腳本不同。標准的Python腳本有一個特定的開始點和結束點,程序員使用條件、循環、和函數來控制執行
順序。從用戶的角度上來看, wxPython程序大部分時間什麼也不做,一直閒著直到用戶或系統做了些什麼來觸發這個wxPython程序動作。 wxPython程序的結構就是一個事件驅動程序體系的例子。下圖是事件處理循環的示意,它展示了主
程序的生命、用戶事件、和分派到的處理器函數。
事件驅動系統的主循環類似於客戶服務呼叫中心的操作者。當沒有呼叫的進入的時候,這個操作者處於等待狀態。當一個事件發生的時候,如電話鈴響了,這個操作者開始一個響應過程,他與客戶交談直到他獲得足夠的信息以分派該客戶給一個合適的回答者。然後操作者等待下一個事件。盡管每個事件驅動系統之間有一些不同,但它們有很多相似的地方。下面
列出了事件驅動程序結構的主要特點:
1、在初始化設置之後,程序的大部分時間花在了一個空閉的循環之中。進入這個循環就標志著程序與用戶交互的部分的開始,退出這個循環就標志結束。在wxPython中,這個循環的方法是: wx.App.MainLoop(),並且在你的腳本中顯式地被調用。當所有的頂級窗口關閉時,主循環退出。
2、程序包含了對應於發生在程序環境中的事情的事件。事件通常由用戶的行為觸發,但是也可以由系統的行為或程序中其他任意的代碼。在wxPython中,所有的事件都是類wx.Event或其子類的一個實例。每個事件都有一個事件類型屬性,它使得不同的事件能夠被辨別。例如,鼠標釋放和鼠示按下事件都被認為是同一個類的實例,但有不同的事件類型。
3、作為這個空閉的循環部分,程序定期檢查是否有任何請求響應事情發生。有兩種機制使得事件驅動系統可以得到有關事件的通知。最常被wxPython使用的方法是,把事件傳送到一個中心隊列,由該隊列觸發相應事件的處理。另一種方法是使用輪詢的方法,所有可能引發事件的事件主被主過程定期查詢並詢問是否有沒有處理的事件。
4、當事件發生時,基於事件的系統試著確定相關代碼來處理該事件,如果有,相關代碼被執行。在wxPython中,原系統事件被轉換為wx.Event實例,然後使用wx.EvtHandler.ProcessEvent()方法將事件分派給適當的處理器代碼。圖3.3呈現了這個過程
事件機制的組成部分是事件綁定器對象和事件處理器。事件綁定器是一個預定義的wxPython對象。每個事件都有各自的事件綁定器。事件處理器是一個函數或方法,它要求一個wxPython事件實例作為參數。當用戶觸發了適當的事件時,一個事件處理器被調用。
在你的wxPython代碼中,事件和事件處理器是基於相關的窗口部件的。例如,一個按鈕被單擊被分派給一個基於該按鈕的專用的事件處理器。為了要把一個來自特定窗口部件的事件綁定到一個特定的處理器方法,你要使用一個綁定器對象來管理這個連接。例如:
self.Bind(wx.EVT_BUTTON, self.OnClick, aButton)
在wxPython中,代表事件的是事件對象。事件對象是類wx.Event或其子類的一個實例。父類wx.Event相對小且抽象,它只是包含了對所有事件的一些通常的信息。 wx.Event的各個子類都添加了更多的信息。
在wxPython中,有一些wx.Event的子類。表3.2包含了你將最常遇到的一些事件類。記住,一個事件類可以有多個事件類型,每個都對應於一個不同的用戶行為。下表是wx.Event的重要的子類。
wx.CloseEvent: 當一個框架關閉時觸發。這個事件的類型分為一個通常的框架關閉和一個系統關閉事件。
wx.CommandEvent: 與窗口部件的簡單的各種交互都將觸發這個事件,如按鈕單擊、菜單項選擇、單選按鈕選擇。這些交互有它各自的事件類型。許多更復雜的窗口部件,如列表等則定義wx.CommandEvent的子類。事件處理系統對待命令事件與其它事件不同。
wx.KeyEvent: 按按鍵事件。這個事件的類型分按下按鍵、釋放按鍵、整個按鍵動作。
wx.MouseEvent: 鼠標事件。這個事件的類型分鼠標移動和鼠標敲擊。對於哪個鼠標按鈕被敲擊和是單擊還是雙擊都有各自的事件類型。
wx.PaintEvent: 當窗口的內容需要被重畫時觸發。
wx.SizeEvent: 當窗口的大小或其布局改變時觸發。
wx.TimerEvent: 可以由類wx.Timer類創建,它是定期的事件。
2. 如何將事件綁定到處理器?
在wxPython中,事件綁定器實例的名字是全局性的。為了清楚地將事件類型與處理器聯系起來,它們的名字都是以wx.EVT_開頭並且對應於使用在C++ wxWidgets代碼中宏的名字。值得強調的是, wx.EVT綁定器名字的值不是你通過調用一個wx.Event實例的GetEventType()方法得到的事件類型的實際的整數碼。事件類型整數碼有一套完全不同的全局名,並且在實際中不常被使用。
作為wx.EVT名字的例子,讓我們看看wx.MouseEvent的事件類型。正如我們所提到的,它們有十四個,其中的九個涉及到了基於在按鈕上的敲擊,如鼠標按下、鼠標釋放、或雙擊事件。這九個事件類型使用了下面的名字:
wx.EVT_LEFT_DOWN
wx.EVT_LEFT_UP
wx.EVT_LEFT_DCLICK
wx.EVT_MIDDLE_DOWN
wx.EVT_MIDDLE_UP
wx.EVT_MIDDLE_DCLICK
wx.EVT_RIGHT_DOWN
wx.EVT_RIGHT_UP
wx.EVT_RIGHT_DCLICK
類wx.CommandEvent有28個不同的事件類型與之關聯;盡管有幾個僅針對老的Windows操作系統。它們中的大多數是專門針對單一窗口部件的,如wx.EVT_BUTTON用於按鈕敲擊, wx.EVT_MENU用於菜單項選擇。
wx.EvtHandler類定義的一些方法在一般情況下用不到。你會經常使用的wx.EvtHandler的方法是Bind(),它創建事件綁定。該方法的用法如下:
Bind(event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY)
Bind()函數將一個事件和一個對象與一個事件處理器函數關聯起來。參數event是必選的,它是我們在3.3節中所說的wx.PyEventBinder的一個實例。參數handler也是必選的,它是一個可調用的Python對象,通常是一個被綁定的方法
或函數。處理器必須是可使用一個參數(事件對象本身)來調用的。參數handler可以是None,這種情況下,事件沒有關聯的處理器。參數source是產生該事件的源窗口部件,這個參數在觸發事件的窗口部件與用作事件處理器的窗口部件不相同時使用。通常情況下這個參數使用默認值None,這是因為你一般使用一個定制的wx.Frame類作為處理器,並且綁定來自於包含在該框架內的窗口部件的事件。父窗口的__init__是一個用於聲明事件綁定的方便的位置。但是如果父窗口包含了多個按鈕敲擊事件源(比如OK按鈕和Cancel按鈕),那麼就要指定source參數以便wxPython區分它們。下面是該方法的一個例子:
self.Bind(wx.EVT_BUTTON, self.OnClick, button)
3. wxPython是如何處理事件的?
事件處理過程開始於觸發事件的對象。通常, wxPython首先在觸發對象中查找匹配事件類型的被綁定的處理器函數。如果找到,則相應的方法被執行。否則, wxPython將檢查該事件是否傳送到了上一級的容器。如果是的話,父窗口部件將被檢查,這樣一級一級向上尋找,直到wxPython找到了一個處理器函數或到達了頂級窗口。如果事件沒有傳播,在處理過程完成之前, wxPython仍將為了處理器函數而檢查應用程序對象。
第一步,創建事件
第二步,確定事件對象是否被允許處理事件
第三步 定位綁定器對象
第四步 決定是否繼續處理
第五步 決定是否展開
使用Skip()方法
# -*- coding: utf-8 -*-
#!/usr/bin/env python
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()
#1 這行綁定按鈕敲擊事件到OnButtonClick()處理器,這個處理器改變框架的背景色。
#2 這行綁定鼠標左鍵按下事件到OnMouseDown()處理器,這個處理器改變按鈕的標簽文本。由於鼠標左鍵按下事件不是命令事件,所以它必須被綁定到按鈕(self.button.Bind)而非框架(self.Bind)
3. 如何創建自己的事件?
創建自定義事件的步驟:
1、定義一個新的事件類,它是wxPython的wx.PyEvent類的子類。如果你想這個事件被作為命令事件,你可以創建wx.PyCommandEvent的子類。像許多wxPython中的覆蓋一樣,一個類的py版本使得wxWidget系統明白用Python寫的
覆蓋C++方法的方法。
2、創建一個事件類型和一個綁定器對象去綁定該事件到特定的對象。
3、添加能夠建造這個新事件實例的代碼,並且使用ProcessEvent()方法將這個實例引入事件處理系統。一旦該事件被創建,你就可以像使用其它的wxPython事件一樣創建綁定和處理器方法。
# -*- coding: utf-8 -*-
#!/usr/bin/env python
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()的調用將這個新事件引入到事件處理系統中, ProcessEvent()的說明見3.4.1節。 GetEventHandler()調用返回wx.EvtHandler的一個實例。大多數情況下,返回的實例是窗口部件對象本身,但是如果其它的wx.EvtHandler()方法已經被壓入了事件處理器堆棧,那麼返回的將是堆棧項的項目。
#9 該自定義的事件的綁定如同其它事件一樣,在這裡使用#3所創建的綁定器。
#10 這個例子的事件處理器函數改變窗口的標題以顯示敲擊數