1. 程式人生 > >wxPython 把視窗部件放入框架中

wxPython 把視窗部件放入框架中

把視窗部件放入框架中

在你的wxPython中,所有的使用者互動行為都發生在一個視窗部件容器中,它通常被稱作視窗,在wxPython 中被稱為框架。在這一章中,我們將討論wxPython中的幾個不同樣式的框架。這個主要的wx.Frame有幾個不同的框架樣式,這些樣式可以改變wx.Frame的外觀。另外,wxPython提供了小型框架和實現多文件介面的框架。框架可以使用分隔條來劃分為不同的部分,並且可以通過滾動條的使用來包含比框架本身大的面板(panel)。

框架的壽命

我們將通過討論框架最基本的元素:建立和除去它們,來作為我們的開始。建立框架包括瞭解可以應用的所有樣式元素;框架的去除可能比你原本想像的要複雜。

如何建立一個框架?

在本書中我們已經見過了許多的框架建立的例子,但是我們仍將再回顧一下框架建立的初步原則。

建立一個簡單的框架

框架是類wx.Frame的例項。例8.1顯示了一個非常簡單的框架建立的例子。

例8.1 建立基本的wx.Frame

切換行號顯示
   1 import wx
   2 
   3 if __name__ == '__main__':
   4     app = wx.PySimpleApp()
   5     frame = wx.Frame(None, -1, "A Frame", style=wx.DEFAULT_FRAME_STYLE
,
6 size=(200, 100)) 7 frame.Show() 8 app.MainLoop()

上面的程式碼建立一個帶有標題的框架,其大小是(200,100)。表8.1中的預設樣式提供了標準框架的裝飾如關閉框、最小化和最大化框。結果如圖8.1所示。

圖8.1

w8.1.gif

wx.Frame的建構函式類似於我們在第7章見到的其它視窗部件的建構函式:

切換行號顯示
   1 wx.Frame(parent, id=-1, title="", pos=wx.DefaultPosition,
   2         size
=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,
3 name="frame")

這裡有超過十餘種之多的專用於wx.Frame的樣式標記,我們將在下一部分涵蓋它們。預設樣式為你提供了最小化和最大化框、系統下拉選單、可調整尺寸的粗邊框和一個標題。

這裡沒有與一個wx.Frame掛鉤的事件型別。但是,由於一個wx.Frame是你的螢幕上使用者最可能去關閉的元素,所以你通常想去為關閉事件定義一個處理器,以便子視窗和資料被妥善的處理。

建立框架的子類

你將很少直接建立wx.Frame的例項。正如我們在本書中所見過的其它例子一樣,一個典型的wxPython應用程式建立wx.Frame的子類並建立那些子類的例項。這是因為wx.Frame的獨特的情形——雖然它自身定義了很少的行為,但是帶有獨自的初始化程式的子類是放置有關你的框架的佈局和行為的最合理的地方。不建立子類而構造你應用程式的特定的佈局是有可能,但除了最簡單的應用程式以外,那是不容易的。例8.2展示了wx.Frame子類的例子。

例8.2 一個簡單的框架子類

切換行號顯示
   1 import wx
   2 
   3 class SubclassFrame(wx.Frame):
   4     def __init__(self):
   5         wx.Frame.__init__(self, None, -1, 'Frame Subclass', 
   6                 size=(300, 100))
   7         panel = wx.Panel(self, -1)
   8         button = wx.Button(panel, -1, "Close Me", pos=(15, 15))
   9         self.Bind(wx.EVT_BUTTON, self.OnCloseMe, button)
  10         self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
  11 
  12     def OnCloseMe(self, event):
  13         self.Close(True)
  14 
  15     def OnCloseWindow(self, event):
  16         self.Destroy()
  17 
  18 if __name__ == '__main__':
  19     app = wx.PySimpleApp()
  20     SubclassFrame().Show()
  21     app.MainLoop() 

執行結果如圖8.2所示

圖8.2

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

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

w8.4.gif

標記值必須使用SetExtraStyle()方法來設定。有時,額外樣式資訊必須在框架被例項化前被設定,這就導致了一個問題:你如何對於一個不存在的例項呼叫一個方法?在接下來的部分,我們將展示實現這種操作的兩個機制。

新增額外樣式資訊

在wxPython中,額外樣式資訊在建立之前通過使用專門的類wx.PreFrame來被新增,它是框架的一種區域性例項。你可以在預框架(preframe)上設定額外樣式位,然後使用這個預框架(preframe)來建立實際的框架。例8.3顯示了在一個子類的構造器中如何完成這兩步(two-step)的構建。注意,在wxPython中它實際上是三步(在C++ wxWidgets工具包中,它是兩步(two-step),我們只是沿用這個叫法而已)。

例8.3

切換行號顯示
   1 import wx
   2 
   3 class HelpFrame(wx.Frame):
   4 
   5     def __init__(self):
   6         pre = wx.PreFrame() #1 預構建物件
   7         pre.SetExtraStyle(wx.FRAME_EX_CONTEXTHELP)
   8         pre.Create(None, -1, "Help Context", size=(300, 100),
   9                 style=wx.DEFAULT_FRAME_STYLE ^
  10                 (wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX)) #2 建立框架
  11         self.PostCreate(pre) #3 底層C++指標的傳遞
  12 
  13 if __name__ == '__main__':
  14     app = wx.PySimpleApp()
  15     HelpFrame().Show()
  16     app.MainLoop()

#1 建立wx.PreFrame()的一個例項(關於對話方塊,這有一個類似的wx.PreDialog()——其它的wxWidgets視窗部件有它們自己的預類)。在這個呼叫之後,你可以做你需要的其它初始化工作。

#2 呼叫Create()方法建立框架。

#3 這是特定於wxPython的,並且不由C++完成。PostCreate方法做一些內部的內務處理,它例項化一個你在第一步中建立的封裝了C++的物件。

新增額外樣式資訊的通用方法

先前的演算法有點笨拙,但是它可以被重構得容易一點,以便於管理維護。第一步是建立一個公用函式,它可以管理任何分兩步的建立。例8.4提供了一個例子,它使用Python的內省效能來呼叫以變數形式被傳遞的函式。這個例子用於在Python的一個新的框架例項化期間的init方法中被呼叫。

例8.4 一個公用的兩步式建立函式

切換行號顯示
   1 def twoStepCreate(instance, preClass, preInitFunc, *args,**kwargs):
   2         pre = preClass()
   3         preInitFunc(pre)
   4         pre.Create(*args, **kwargs)
   5         instance.PostCreate(pre)

在例8.4中,函式要求三個必須的引數。instance引數是實際被建立的例項。preClass引數是臨時的預類的類物件——對框架預類是wx.PreFrame。preInitFunc是一個函式物件,它通常作為回撥函式用於該例項的初始化。這三個引數之後,我們可以再增加任意數量的其它可選引數。

這個函式的第一行,pre = preClass(),內省地例項化這個預建立物件,使用作為引數傳遞過來的類物件。下面一行根據引數preInitFunc內省地呼叫回撥函式,它通常設定擴充套件樣式標記。然後pre.Create()方法被呼叫,它使用了可選的引數。最後,PostCreate方法被呼叫來將內在的值從pre移給例項。至此,instance引數已經完全被建立了。假設twoStepCreate已被匯入,那麼上面的公用函式可以如例8.5被使用。

例8.5 另一個兩步式的建立,使用了公用函式

切換行號顯示
   1 import wx
   2 
   3 class HelpFrame(wx.Frame):
   4 
   5 def __init__(self, parent, ID, title,pos=wx.DefaultPosition, size=(100,100),style=wx.DEFAULT_DIALOG_STYLE):
   6         twoStepCreate(self, wx.PreFrame, self.preInit, parent,
   7         id, title, pos, size, style)
   8 
   9 def preInit(self, pre):
  10         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的最基本的屬性。這些屬性和方