本文翻译自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 相关的内容只是列出了标题,却没有介绍。若是要编写小游戏,这部份内容是很关键的,太遗憾了。
先发出来,附加的内容再慢慢补充。