本文翻譯自http://wiki.wxpython.org/Getting%20Started
首先聲明:本人仍是個菜鳥,翻譯只是爲了學習,就看成記筆記了。水平有限,錯誤和疏漏在所不免,但願各路高手可以給予指導。並且簡單查了一下,好像中文世界目前尚未完整的翻譯 Getting Started with wxPython 的。html
按慣例,咱們先來寫一個 「Hello, World!」 小程序。這是代碼:python
# -*- coding: utf-8 -*-
""" http://blog.csdn.net/chenghit """
import wx
app = wx.App(False) #建立1個APP,禁用stdout/stderr重定向
frame = wx.Frame(None, wx.ID_ANY, "Hello, World!") #這是一個頂層的window
frame.Show(True) #顯示這個frame
app.MainLoop()
解釋:web
代碼 | 說明 |
---|---|
app = wx.App(False) | 每個 wxPython 應用程序都是一個 wx.App 實例。對於大多數的簡單程序,直接實例化 wx.App 便可。但若是你但願建立一個複雜的應用程序,那麼能夠對 wx.App class 作一些擴展。」False」 參數意味着「不要把 stdout 和 stderr 信息重定向到窗口」,固然也能夠不加 「False」 參數。 |
frame = wx.Frame(None, wx.ID_ANY, 「Hello, World!」) | 完整的語法是 x.Frame(Parent, Id, Title) 。在本例中,咱們使用 「None」 來表示這個frame是頂層的框架,沒有父框架;使用 「wx.ID_ANY」 讓 wxWidgets 來給咱們挑選一個ID。 |
frame.Show(True) | 顯示這個Frame |
app.MainLoop() | 運行這個應用程序 |
Note1: 你還能夠用 -1
來替代wx.ID_ANY
,-1
就是默認值的意思。另外 wxWidgets 還提供了其它的標準 ID(v2.8)。 你也能夠自定義一個ID,但 Getting Started with wxPython 認爲,沒有理由那樣作,用標準ID更好。
Note2: 實際上,wx.Frame的完整語法是(詳細的參數介紹):小程序
wx.Frame(Parent, ID, Title, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, name="frame")
最後運行程序,咱們能夠看到相似這樣的窗口:
windows
當人們談論GUI的時候,他們一般指的是windows,menus和icons。那麼天然地,你可能會認爲應該用wx.Window
來表明屏幕上的一個window。但實際上不是這樣的。wx.Window
是一個基礎的class,全部的可視化元素,例如buttons, menus等等,都起源於wx.Window
類。而程序窗口則是一個wx.Frame
。新手常常把這2個概念搞混,須要特別留心。api
如今咱們來寫一個簡單的記事本。在這個例子中,咱們會用到幾個組件,來理解一些特性或功能,例如事件(events)和回調(callbacks)。app
首先,咱們須要建立1個frame,而且這個frame包含1個可編輯的文本框(text box)。文本框須要用wx.TextCtrl
來建立。默認狀況下,文本框只能編輯1行文字——不管文字有多長,都不會換行。因此,咱們須要用wx.TE_MULTILINE
參數來容許多行編輯。框架
# -*- coding: utf-8 -*-
""" http://blog.csdn.net/chenghit """
import wx
class MainWindow(wx.Frame):
"""We simply derive a new class of Frame."""
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title = title, size = (200, 100))
self.control = wx.TextCtrl(self, style = wx.TE_MULTILINE)
self.Show(True)
app = wx.App(False)
frame = MainWindow(None, 'Small editor')
app.MainLoop()
在這個例子中,咱們生成一個wx.Frame
的子類,並重寫它的__init__
方法。咱們用wx.TextCtrl
來聲明一個簡單的文本編輯器。注意,由於在MyFrame.__init__
中已經運行了self.Show()
,因此在建立MyFrame的實例以後,就不用再調用frame.Show()
了。編輯器
全部的應用程序都會有一個菜單欄,和一個狀態欄。讓咱們來給這個記事本程序添加一個:svg
# -*- coding: utf-8 -*-
""" http://blog.csdn.net/chenghit """
import wx
class MainWindow(wx.Frame):
"""We simply derive a new class of Frame."""
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title = title, size = (200, 100))
self.control = wx.TextCtrl(self, style = wx.TE_MULTILINE)
self.CreateStatusBar() #建立位於窗口的底部的狀態欄
#設置菜單
filemenu = wx.Menu()
#wx.ID_ABOUT和wx.ID_EXIT是wxWidgets提供的標準ID
filemenu.Append(wx.ID_ABOUT, u"關於", u"關於程序的信息")
filemenu.AppendSeparator()
filemenu.Append(wx.ID_EXIT, u"退出", u"終止應用程序")
#建立菜單欄
menuBar = wx.MenuBar()
menuBar.Append(filemenu, u"文件")
self.SetMenuBar(menuBar)
self.Show(True)
app = wx.App(False)
frame = MainWindow(None, title = u"記事本")
app.MainLoop()
TIP: wx.ID_ABOUT
和wx.ID_EXIT
是wxWidgets提供的標準ID(查看所有標準ID)。若是有一個現成的標準ID,最好仍是使用它,而不要自定義。由於這樣可讓wxWidgets知道,在不一樣的平臺怎樣去顯示這個組件,使它看起來更美觀。
咱們已經建立了1個記事本,雖然它有菜單,可是什麼都作不了。咱們但願點擊菜單以後,程序可以作出反應,例如退出,或者保存文件。在Python中,點擊菜單,點擊按鈕,輸入文本,鼠標移動等等,都被稱爲事件event,而對event作出反應,則被稱爲event handling。對不一樣的event作出不一樣的響應,這是GUI程序的根本。咱們可使用Bind()
方法,將1個對象Object和1個時間event創建綁定關係。
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self,parent, title=title, size=(200,100))
...
menuItem = filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
self.Bind(wx.EVT_MENU, self.OnAbout, menuItem)
這段代碼意味着:從如今開始,一旦用戶點擊了菜單中的 「About」 項目,self.OnAbout
就會被執行。
Note: Bind()
以後,運行個人程序就提示編碼錯誤,不能再使用中文了,因此下面的代碼示例都是全英文的。不知道這是否是python(x,y)獨有的問題。誰能幫我解答一下?
wx.EVT_MENU
指代「選擇菜單中的項目」這個事件。wxWidgets 提供了不少的事件,能夠點這裏查看不完整的列表,也可使用下面的代碼打印完整的列表。全部的事件都是wx.Event
的子類。
import wx
for x in dir(wx):
if x.startswith('EVT_'):
print x
若是直接運行上面的Bind程序,會提示不存在OnAbout這個attribute。還須要在Class中聲明self.OnAbout
方法:
def OnAbout(self, event):
...
這裏的event參數是wx.Event
的子類的一個實例。
當event發生的時候,method就會被執行。默認狀況下,這個method會處理event,而且當callback完成以後,event也會中止。可是在一些結構化的事件處理器event handlers中,咱們可使用event.Skip()
來跳過一個event。例如
def OnButtonClick(self, event):
if (某種條件):
作某事()
else:
event.Skip()
def OnEvent(self, event):
...
當一個點擊按鈕的事件發生時,OnButtonClick會被調用。若是「某種條件」爲真,咱們就會「作某事()」。不然咱們就會讓其它的event handler來處理這個事件。
如今來看看咱們的程序:
# -*- coding: utf-8 -*-
""" http://blog.csdn.net/chenghit """
import wx
class MainWindow(wx.Frame):
"""We simply derive a new class of Frame."""
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title = title, size = (600, 400))
self.control = wx.TextCtrl(self, style = wx.TE_MULTILINE)
self.CreateStatusBar() # 建立位於窗口的底部的狀態欄
# 設置菜單
filemenu = wx.Menu()
# wx.ID_ABOUT和wx.ID_EXIT是wxWidgets提供的標準ID
menuAbout = filemenu.Append(wx.ID_ABOUT, "&About", \
" Information about this program") # (ID, 項目名稱, 狀態欄信息)
filemenu.AppendSeparator()
menuExit = filemenu.Append(wx.ID_EXIT, "E&xit", \
" Terminate the program") # (ID, 項目名稱, 狀態欄信息)
# 建立菜單欄
menuBar = wx.MenuBar()
menuBar.Append(filemenu, "&File") # 在菜單欄中添加filemenu菜單
self.SetMenuBar(menuBar) # 在frame中添加菜單欄
# 設置events
self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)
self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
self.Show(True)
def OnAbout(self, e):
# 建立一個帶"OK"按鈕的對話框。wx.OK是wxWidgets提供的標準ID
dlg = wx.MessageDialog(self, "A small text editor.", \
"About Sample Editor", wx.OK) # 語法是(self, 內容, 標題, ID)
dlg.ShowModal() # 顯示對話框
dlg.Destroy() # 當結束以後關閉對話框
def OnExit(self, e):
self.Close(True) # 關閉整個frame
app = wx.App(False)
frame = MainWindow(None, title = "Small editor")
app.MainLoop()
Note1: 上述代碼的菜單項目名稱」&About」, 「E&xit」, 「&File」 中的 「&」是作什麼用的? 「&」 的位置也不同,分別意味着什麼?若是直接print "&About"
,會把 「&」 打印出來。可是在上面的應用程序菜單中看不到 「&」。並且我試過把 「&」去掉,沒有任何變化。誰能幫我解答一下?
Note2: 下面代碼中的wx.OK
能夠省略,此時等於wx.ID_ANY
dlg = wx.MessageDialog(self, "A small text editor.", \
"About Sample Editor", wx.OK) # 語法是(self, 內容, 標題, ID)
這是帶wx.OK的對話框:
這是省略wx.OK的對話框:
固然,一個文本編輯器不可以沒有打開或保存文檔的功能——這些功能是由對話來實現的。通常對話由底層平臺提供,這樣你的應用程序看上去就像是一個原生程序。在本例中,對話由 MainWindow
的 OnOpen
方法來實施:
def OnOpen(self,e):
""" Open a file"""
self.dirname = ''
dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*", wx.OPEN)
if dlg.ShowModal() == wx.ID_OK:
self.filename = dlg.GetFilename()
self.dirname = dlg.GetDirectory()
f = open(os.path.join(self.dirname, self.filename), 'r')
self.control.SetValue(f.read())
f.close()
dlg.Destroy()
解釋:
ShowModal
打開對話框 - 「Modal」 的意思是,在用戶點擊 OK 或 Cancel 以前,不能作任何的操做。ShowModal
的返回值是一個被點擊按鈕的 ID, 若是用戶點擊了 OK 按鈕,程序就讀取文件如今,你能夠向菜單中添加相應的條目,並把它連接到OnOpen
方法。若是你遇到了問題,請向下滾動頁面,查閱下文的完整代碼。
固然,目前這個程序還遠不是一個合格的文本編輯器。可是,添加其它的功能並不比咱們剛纔所完成的內容更難,你能夠從 wxPython 提供的 Demo 獲取靈感(點此下載Demo,選擇版本後,下載 wxPython-demo-x.x.x 文件):
主題:
在這個章節,咱們將會講解 wxPython 處理窗口和窗口內容的方法,包括建立輸入組件,使用各類工具和控件 widgets/controls。 咱們將會建立一個計算股票價格的小程序。若是你已是個有經驗的 GUI 開發者,這部分的內容對你來講太簡單了,你能夠直接閱讀下文的 Boa-Constructor 章節。
在 frame 裏面,你可使用若干個 wxWindow 子類來充實 frame 的內容,經常使用的元素有如下幾種:
wx.MenuBar
, 在 frame 的頂部填加菜單欄wx.StatusBar
, 在 frame 的底部填加狀態欄,顯示狀態信息wx.ToolBar
, 在 frame 中添加工具欄wx.Control
的子類,它們表明用戶接口的widgets (例如顯示數據 and/or 處理用戶輸入的可見元素). 常見的wx.Control
對象包括 wx.Button
, wx.StaticText
, wx.TextCtrl
和 wx.ComboBox
. wx.Panel
, 它是容納各類wx.Control
對象的容器。把wx.Control
對象放入wx.Panel
, 用戶就能夠操做它們。全部的可見元素 (wxWindow 對象和它們的子類) 都可以容納子元素。例如,一個wx.Frame
能夠容納若干個wx.Panel
對象,而這些wx.Panel
又能夠容納若干wx.Button
, wx.StaticText
和 wx.TextCtrl
對象,就像這樣:
注意,這僅僅是描述可見元素的相關性,而不是描述應該怎樣佈局它們。若是要處理元素的佈局,有如下幾種選擇:
wx.LayoutConstraints
, 可是很複雜LayoutAnchors
, 比wx.LayoutConstraints
簡單些做爲wx.Sizer
的子類,Sizer 可以被用來在 frame 或 window 中佈置可見元素。它的做用包括:
一些常見的 Sizer 包括:
wx.BoxSizer
, 基於水平線或垂直線佈置可見元素wx.GridSizer
, 按照網格結構來佈置元素wx.FlexGridSizer
, 與wx.GridSizer
相似,但更加靈活經過調用sizer.Add(window, options...)
或者 sizer.AddMany(...)
來給出一個wx.Window
對象的列表,sizer 就可以佈置它們. Sizer 還可以嵌套,你能夠把 1 個 sizer 放進另 1 個 sizer 裏面,例如把 2 個按水平線佈置按鈕的wx.BoxSizer
放進另 1 個按垂直線佈置元素的wx.BoxSizer
裏面,就像這樣:
NOTE: 在上面的例子中,6 個按鈕並非按照 2 行 3 列來作陣列式佈局的,若是要那樣作,你必須使用wx.GridSizer
接下來,咱們給咱們的文本編輯器增長 2 個嵌套的 sizer,把 1 個水平佈局的 sizer 嵌入到 1 個垂直佈局的 sizer 裏面:
# -*- coding: utf-8 -*-
""" http://blog.csdn.net/chenghit """
import wx
import os
class MainWindow(wx.Frame):
"""We simply derive a new class of Frame."""
def __init__(self, parent, title):
self.dirname = ''
# "-1"這個尺寸參數意味着通知wxWidget使用默認的尺寸
# 在這個例子中,咱們使用200像素的寬度,和默認的高度
wx.Frame.__init__(self, parent, title = title, size = (200, -1))
self.control = wx.TextCtrl(self, style = wx.TE_MULTILINE)
self.CreateStatusBar() # 建立位於窗口的底部的狀態欄
# 設置菜單
filemenu = wx.Menu()
# wx.ID_ABOUT和wx.ID_EXIT是wxWidgets提供的標準ID
menuOpen = filemenu.Append(wx.ID_OPEN, "&Open", " Open a file")
menuAbout = filemenu.Append(wx.ID_ABOUT, "&About", \
" Information about this program") # (ID, 項目名稱, 狀態欄信息)
filemenu.AppendSeparator()
menuExit = filemenu.Append(wx.ID_EXIT, "E&xit", \
" Terminate the program") # (ID, 項目名稱, 狀態欄信息)
# 建立菜單欄
menuBar = wx.MenuBar()
menuBar.Append(filemenu, "&File") # 在菜單欄中添加filemenu菜單
self.SetMenuBar(menuBar) # 在frame中添加菜單欄
# 設置events
self.Bind(wx.EVT_MENU, self.OnOpen, menuOpen)
self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)
self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
# 設置sizers
self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
self.buttons = []
for i in range(0, 6):
self.buttons.append(wx.Button(self, -1, "Button &" + str(i)))
self.sizer2.Add(self.buttons[i], 1, wx.SHAPED)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.control, 1, wx.EXPAND)
self.sizer.Add(self.sizer2, 0, wx.GROW)
# 激活sizer
self.SetSizer(self.sizer)
self.SetAutoLayout(True)
self.sizer.Fit(self)
self.Show(True)
def OnAbout(self, e):
# 建立一個帶"OK"按鈕的對話框。wx.OK是wxWidgets提供的標準ID
dlg = wx.MessageDialog(self, "A small text editor.", \
"About Sample Editor", wx.OK) # 語法是(self, 內容, 標題, ID)
dlg.ShowModal() # 顯示對話框
dlg.Destroy() # 當結束以後關閉對話框
def OnExit(self, e):
self.Close(True) # 關閉整個frame
def OnOpen(self, e):
""" open a file. """
# wx.FileDialog語法:(self, parent, message, defaultDir, defaultFile,
# wildcard, style, pos)
dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*",
wx.OPEN)
if dlg.ShowModal() == wx.ID_OK:
self.filename = dlg.GetFilename()
self.dirname = dlg.GetDirectory()
f = open(os.path.join(self.dirname, self.filename), 'r') # 暫時只讀
self.control.SetValue(f.read())
f.close()
dlg.Destroy()
app = wx.App(False)
frame = MainWindow(None, title = "Small editor")
app.MainLoop()
sizer.Add
方法有 3 個參數(語法):
wx.GROW
或者wx.EXPAND
, 它們的做用是同樣的,這意味着控件能夠調整本身的尺寸以適應 frame 尺寸的變化。若是使用wx.SHAPED
來充當第 3 個參數,那麼控件的尺寸雖然能夠變化,可是形狀會保持不變。在上面的例子中,self.sizer.Add(self.sizer2, 0, wx.GROW)
權重因子是 0,因此咱們能夠看到不管 frame 的形狀怎麼變,self.sizer2
的高度是一直不變的,由於它的父 sizer self.sizer
是按照垂直線來佈置元素的。而self.sizer2
的寬度能夠變,由於第 3 個參數是wx.GROW
。
另外,self.sizer2.Add(self.buttons[i], 1, wx.SHAPED)
第 3 個參數是wx.SHAPED
,因此不管 frame 的形狀和尺寸怎樣變化,按鈕的形狀都不會變,長度和寬度一直保持着相同的比率。
flag 參數也可使用wx.ALIGN_CENTER_HORIZONTAL
, wx.ALIGN_CENTER_VERTICAL
, 或wx.ALIGN_CENTER
(for both) 來設置元素的居中方式,還可使用wx.ALIGN_LEFT
, wx.ALIGN_TOP
, wx.ALIGN_RIGHT
, wx.ALIGN_BOTTOM
中的 1 個或 2 個組合,來設置元素的對齊方式。默認的對齊方式是wx.ALIGN_LEFT | wx.ALIGN_TOP
.
wx.Sizer
和它的子類有一個可能會讓人感到困惑的地方,就是 sizer 和父窗口之間的區別。當你把一個對象添加到 sizer 裏面時,不須要指定這個對象的父窗口。sizer 只是對窗口布局的方式,它自己並非窗口。可是在建立對象的時候就須要指定父窗口。在上面的例子中,使用wx.Button
(語法)建立按鈕的時候就須要指定 frame 或 window 做爲按鈕的父窗口,而不是指定 sizer 來當父窗口。
一旦你完成可見元素的設置,並把它們加入到 sizer(或者嵌套的 sizer),下一步就是告訴 frame 或 window 來使用 sizer。用如下 3 個必要的步驟來完成這項工做:
window.SetSizer(sizer)
window.SetAutoLayout(True)
sizer.Fit(window)
SetSizer()
告訴你的 window (or frame) 應該使用哪一個sizer。SetAutoLayout()
告訴你的 window 使用 sizer 來佈局組件sizer.Fit()
告訴 sizer 計算它所容納的元素的初始化尺寸和位置咱們已經在上文講解過菜單的設置和使用方法,再也不累述。
當你建立一個對話框或者輸入控件的時候,可使用wx.Validator
來簡化控件加載數據的進程,對輸入的數據進行驗證,或從中摘錄數據。wx.Validator
還能夠被用來截取控件域內發生的一些事件,例如敲擊鍵盤的動做。要使用驗證器,你必須先定義一個wx.Validator
的子類 (既不是wx.TextValidator
也不是wx.GenericValidator
),而後再調用myInputField.SetValidator
(myValidator
) 把它關聯到你的控件域。
NOTE1: 你定義的wx.Validator
子類必須覆蓋wxValidator.Clone()
方法。
NOTE2: 原文並無進一步的講解 Validators 的設置和使用方法,不過你能夠參考這個 bing.com 的網頁快照
如今咱們來寫一個小程序,這個程序很簡單,frame 中只有一個包含有一個標籤label[7] 的面板panel[8]:
# -*- coding: utf-8 -*-
""" Created on Sun Dec 20 20:46:32 2015 @author: chenghit """
import wx
class ExampleFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
panel = wx.Panel(self)
self.quote = wx.StaticText(panel, label="Your quote:", pos=(20, 30))
self.Show()
app = wx.App(False)
ExampleFrame(None)
app.MainLoop()
若是你讀完前面怎樣編寫文本編輯器的部分,你必定會以爲這個程序很是簡單。但須要注意的是,在這裏應該用一個 sizer 來佈置組件,而不該該手工的一一指定它們的位置。注意這行代碼:
self.quote = wx.StaticText(panel, label="Your quote:", pos=(20, 30))
wxStaticText
的 parent 參數是一個 panel。咱們的靜態文本將陳列在咱們剛剛建立的 panel 上面,並使用了wxPoint
參數來定義位置。根據wx.StaticText
的語法,還能夠定義一個wxSize
參數,可是在這個例子中並無採用。
[7] 根據 wxPython 的文檔:
Panel 就是放置組件的窗口,它一般被放置在 frame 裏面。在繼承它的父類 wxWindow 的基礎上,Panel 還含有一些額外的,細微的功能性。Panel 的主要目的是在功能性和外觀上和對話框類似,可是又有做爲父窗口的靈活性。
事實上, 對於那些處理文字錄入的對象(一般被稱做控件或組件)來講,Panel 就是個灰色的背景。
[8] label 的做用僅僅是顯示文本,並不和用戶進行交互。
你能夠在 wxPython 的 demo 和 docs 中種類繁多的控件,可是本文將只會講解其中最經常使用的幾種:
wxButton
是最基本的控件: 它是一個你能夠點擊的按鈕,並帶有文字。下面是一個 「Clear」 按鈕的例子(比方說,你點擊以後會清空文字):clearButton = wx.Button(self, wx.ID_CLEAR, "Clear")
self.Bind(wx.EVT_BUTTON, self.OnClear, clearButton)
wxTextCtrl
這個控件可讓用戶輸入文字,它產生 2 種主要的事件:若是文字被改變了,它會調用 EVT_TEXT ;若是鍵盤被按下,它會調用 EVT_CHAR。根據下面的例子,若是你按下了 「Clear」 按鈕,將只會產生一個 EVT_TEXT 事件,而不會產生 EVT_CHAR 事件。textField = wx.TextCtrl(self) self.Bind(wx.EVT_TEXT, self.OnChange, textField) self.Bind(wx.EVT_CHAR, self.OnKeyPress, textField)
wxComboBox
下拉菜單,和 wxTextCtrl
很像,可是除了 EVT_TEXT 和 EVT_CHAR 以外,wxComboBox
還可以生成 EVT_COMBOBOX 事件. ComboBox 能夠是 「下拉菜單+複選框」 , 能夠是 「下拉菜單+表格」…能夠點擊這裏查看 ComboBox 的示例,雖然是 C# 寫的,但 ComboBox 的概念是相同的。wxCheckBox
複選框,可讓用戶作出 true/false 的選擇wxRadioBox
單選框,可讓用戶從一個列表中作出選擇如今讓咱們來豐富咱們的程序:
# -*- coding: utf-8 -*-
""" http://blog.csdn.net/chenghit """
import wx
class ExamplePanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.quote = wx.StaticText(self, label='Your quote:', pos=(20, 30))
# 這個多行的文本框只是用來記錄並顯示events,不要糾結之
self.logger = wx.TextCtrl(self, pos=(300,20), size=(200,300),
style=wx.TE_MULTILINE | wx.TE_READONLY)
# 一個按鈕
self.button = wx.Button(self, label='Save', pos=(200, 325))
self.Bind(wx.EVT_BUTTON, self.OnClick, self.button)
# 僅有1行的編輯控件
self.lblname = wx.StaticText(self, label='Your name:', pos=(20, 60))
self.editname = wx.TextCtrl(self, value='Enter here your name:',
pos=(150, 60), size=(140, -1))
self.Bind(wx.EVT_TEXT, self.EvtText, self.editname)
self.Bind(wx.EVT_CHAR, self.EvtChar, self.editname)
# 一個ComboBox控件(下拉菜單)
self.sampleList = ['friends', 'advertising', 'web search', \
'Yellow Pages']
self.lblhear = wx.StaticText(self, label="How did you hear from us ?",
pos=(20, 90))
self.edithear = wx.ComboBox(self, pos=(150, 90), size=(95, -1),
choices=self.sampleList,
style=wx.CB_DROPDOWN)
self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox, self.edithear)
# 注意ComboBox也綁定了EVT_TEXT事件
self.Bind(wx.EVT_TEXT, self.EvtText, self.edithear)
# 複選框
self.insure = wx.CheckBox(self, label="Do you want Insured Shipment ?",
pos=(20,180))
self.Bind(wx.EVT_CHECKBOX, self.EvtCheckBox, self.insure)
# 單選框
radioList = ['blue', 'red', 'yellow', 'orange', 'green', 'purple', \
'navy blue', 'black', 'gray']
self.rb = wx.RadioBox(label="What color would you like ?",
pos=(20, 210), choices=radioList, \
majorDimension=3, style=wx.RA_SPECIFY_COLS)
self.Bind(wx.EVT_RADIOBOX, self.EvtRadioBox, self.rb)
def OnClick(self, event):
self.logger.AppendText('Click on object with Id %d\n' % \
event.GetId())
def EvtText(self, event):
self.logger.AppendText(self, 'EvtText: %s\n' % event.GetString())
def EvtChar(self, event):
self.logger.AppendText('EvtChar: %d\n' % event.GetKeyCode())
event.Skip()
def EvtComboBox(self, event):
self.logger.AppendText('EvtComboBox: %s\n' % event.GetString())
def EvtCheckBox(self, event):
self.logger.AppendText('EvtCheckBox: %d\n' % event.Checked())
def EvtRadioBox(self, event):
self.logger.AppendText('EvtRadioBox: %d\n' % event.GetInt())
app = wx.App(False)
frame = wx.Frame(None)
panel = ExamplePanel(frame)
frame.Show()
app.MainLoop()
如今咱們的 class 變得很複雜,咱們添加了不少的控件,而且它們是能夠交互的。咱們還添加了一個 wxTextCtrl
控件來顯示其它控件產生的事件:
有時候,一個表單(form)太大了,沒法在一頁內完整的顯示。這時候就要用到wxNoteBook
,它容許用戶經過點擊標籤在幾個頁面之間快速的瀏覽。咱們先把wxNoteBook
放進 frame,而後再用AddPage
把上面的 Panel 放進wxNoteBook
:
app = wx.App(False)
frame = wx.Frame(None, title="Demo with Notebook")
nb = wx.Notebook(frame)
# 這裏把ExamplePanel重複3次放進Notebook
nb.AddPage(ExamplePanel(nb), "Absolute Positioning")
nb.AddPage(ExamplePanel(nb), "Page Two")
nb.AddPage(ExamplePanel(nb), "Page Three")
frame.Show()
app.MainLoop()
NOTE: 如今 ExamplePanel 的父窗口是 Notebook 了,這很關鍵。
嚴格的定義每一個元素的位置並不會帶來理想的顯示效果,由於老是有不少緣由致使 frame 的尺寸並非咱們但願的那樣的大小。上文咱們已經講解過wx.BoxSizer
, wx.GridSizer
, 和wx.FlexGridSizer
, 如今咱們再介紹一種:wx.GridBagSizer
. 你必定用過Excel,必定作過「合併單元格」的操做吧?對了,wxGridBagSizer
就是合併單元格以後的wxGridSizer
。 GridBagSizer介紹,GridBadSizer教程
在下面採用了 GridBagSizer 的例子中,」pos」 參數控制組件放置的座標位置,(0, 0) 意味着組件緊貼在左上角,而 (3, 5) 則意味着組件要再向下 3 行,再向右 5 列。」span」 就是合併單元格的參數:
# -*- coding: utf-8 -*-
""" http://blog.csdn.net/chenghit """
import wx
class ExamplePanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# 建立一些Sizer
mainSizer = wx.BoxSizer(wx.VERTICAL)
grid = wx.GridBagSizer(hgap=5, vgap=5) # 行和列的間距是5像素
hSizer = wx.BoxSizer(wx.HORIZONTAL)
self.quote = wx.StaticText(self, label='Your quote:', pos=(20, 30))
grid.Add(self.quote, pos=(0,0)) # 加入GridBagSizer
self.logger = wx.TextCtrl(self, pos=(300,20), size=(200,300), style=wx.TE_MULTILINE | wx.TE_READONLY)
self.button = wx.Button(self, label='Save', pos=(200, 325))
self.Bind(wx.EVT_BUTTON, self.OnClick, self.button)
self.lblname = wx.StaticText(self, label='Your name:', pos=(20, 60))
grid.Add(self.lblname, pos=(1,0))
self.editname = wx.TextCtrl(self, value='Enter here your name:', pos=(150, 60), size=(140, -1))
grid.Add(self.editname, pos=(1,1))
self.Bind(wx.EVT_TEXT, self.EvtText, self.editname)
self.Bind(wx.EVT_CHAR, self.EvtChar, self.editname)
# 向GridBagSizer中填充空白的空間
grid.Add((10, 40), pos=(2,0))
self.sampleList = ['friends', 'advertising', 'web search', 'Yellow Pages']
self.lblhear = wx.StaticText(self, label="How did you hear from us ?", pos=(20, 90))
grid.Add(self.lblhear, pos=(3,0))
self.edithear = wx.ComboBox(self, pos=(150, 90), size=(95, -1), choices=self.sampleList, style=wx.CB_DROPDOWN)
grid.Add(self.edithear, pos=(3,1))
self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox, self.edithear)
self.Bind(wx.EVT_TEXT, self.EvtText, self.edithear)
self.insure = wx.CheckBox(self, label="Do you want Insured Shipment ?", pos=(20,180))
# 加入Sizer的同時,設置對齊方式和邊距
grid.Add(self.insure, pos=(4,0), span=(1,2), flag=wx.BOTTOM, border=5)
self.Bind(wx.EVT_CHECKBOX, self.EvtCheckBox, self.insure)
radioList = ['blue', 'red', 'yellow', 'orange', 'green', 'purple', 'navy blue', 'black', 'gray']
self.rb = wx.RadioBox(self, label="What color would you like ?", pos=(20, 210), choices=radioList,
majorDimension=3, style=wx.RA_SPECIFY_COLS)
grid.Add(self.rb, pos=(5,0), span=(1,2)) # 合併了1行2列的單元格
self.Bind(wx.EVT_RADIOBOX, self.EvtRadioBox, self.rb)
hSizer.Add(grid, 0, wx.ALL, 5)
hSizer.Add(self.logger)
mainSizer.Add(hSizer, 0, wx.ALL, 5)
mainSizer.Add(self.button, 0, wx.CENTER)
# 能夠把SetSizer()和sizer.Fit()合併成一條SetSizerAndFit()語句
self.SetSizerAndFit(mainSizer)
def OnClick(self, event):
self.logger.AppendText('Click on object with Id %d\n' % event.GetId())
def EvtText(self, event):
self.logger.AppendText('EvtText: %s\n' % event.GetString())
def EvtChar(self, event):
self.logger.AppendText('EvtChar: %d\n' % event.GetKeyCode())
event.Skip()
def EvtComboBox(self, event):
self.logger.AppendText('EvtComboBox: %s\n' % event.GetString())
def EvtCheckBox(self, event):
self.logger.AppendText('EvtCheckBox: %d\n' % event.Checked())
def EvtRadioBox(self, event):
self.logger.AppendText('EvtRadioBox: %d\n' % event.GetInt())
app = wx.App(False)
frame = wx.Frame(None, title="Demo with Notebook")
nb = wx.Notebook(frame)
nb.AddPage(ExamplePanel(nb), "Absolute Positioning")
nb.AddPage(ExamplePanel(nb), "Page Two")
nb.AddPage(ExamplePanel(nb), "Page Three")
frame.Show()
app.MainLoop()
原文基本上到這裏就結束了,後面的 Drawing 相關的內容只是列出了標題,卻沒有介紹。若是要編寫小遊戲,這部份內容是很關鍵的,太遺憾了。
先發出來,附加的內容再慢慢補充。