坚实的基础
在学习任何一门技术或者应用的时候,基础都是必不可少的,往后的这几个章节基本都是一些偏向理论的内容,比如如何梳理你的应用程序结构和逻辑,wxpython的生命周期等等,但是这些基础都是必不可少的,了解了这些内容之后,对于之后的界面的编写都是有很大好处的。
我们wxpython的基础其实就是两个主要的对象,一个是顶级界面对象,也就是Frame对象;还有一个对象是应用程序对象,也就是App对象。这两个对象就是我们整个的wxpython的基础,搞清楚这两个对象,之后在编写更复杂的页面的时候也会得心应手。
关于所要求的对象我们需要知道什么?
首先让我们来描述一下这两个对象,应用程序对象(App)的主要作用是启动并管理主事件循环,主事件循环是wxpython的核心所在,没有这个主事件循环,你的wxpython将无法启动,无法显示页面,更无法响应事件。
顶级窗口对象的作用就是显示控件和展示数据使用,如果不创建并设置顶级窗口的话,会导致程序运行就直接结束了,因为没有窗口被显示出来。
他的逻辑其实很简单,当应用程序启动的时候,应用程序对象将会被首先创建,然后启动主事件循环,并寻找顶级窗口,如果找到了那么就展示出来,如果没有设置顶级窗口,那么就直接退出程序;然后在顶级窗口中,展示控件和数据,控件绑定着事件处理函数,当控件被触发的时候,比如当按钮被按下,这个时候他会将事件发送到主事件循环,然后寻找于这个事件绑定的事件处理函数,然后运行这个函数,之后的结果继续返回到顶级窗口中,然后重复触发-展示这个循环,直到顶级窗口被关闭。
如何创建一个应用程序对象?
任何wxPython应用程序都需要一个应用程序对象。这个应用程序对象必须是类wx.App或其子类的一个实例。应用程序对象的主要目的是管理幕后的主事件循环。这个事件循环响应于窗口系统事件并分配它们给适当的事件处理函数,也叫事件处理器,这两个名词是一个意思。这个应用程序对象对wxPython进程的管理如此的重要以至于在你的程序没有实例化一个应用程序对象之前你不能创建任何的wxPython图形对象,就是说应用程序对象(App)要比顶级窗口对象(Frame)先创建出来。
父类wx.App也定义了一些属性,它们对整个应用程序是全局性的。在wx.App这个父类里面,其实已经定义了一些默认的配置,而大多数情况下,这些默认的配置就是够用的了,比如链接数据库的配置,处理错误的配置,读取配置文件的配置等等,我们要做的其实就是用它来设置一下顶级窗口,其他的配置我们大多数情况下只需要了解一下就可以了,不用去修改它。
创建一个应用程序对象(App)
创建一个应用程序对象其实很简单,而且我们也建议无论在你编写什么界面的时候,都先创建一个应用程序对象,即使你不修改父类App的任何配置,也先创建一个App对象出来。创建一个应用程序对象需要以下四个步骤:
- 1、定义一个类,继承自wx.App
- 2、在定义的子类中写一个OnInit()方法
- 3、在你的程序的main方法中创建这个类的一个实例
- 4、调用应用程序实例的MainLoop()方法。这个方法将程序的控制权转交给wxPython
我们在第一章中看到过OnInit()方法。它在应用程序开始时并在主事件循环开始前被wxPython系统调用。这个方法不要求填写任何的参数,但是要返回一个布尔值True,如果所返回的值是False,则应用程序将立即退出。大多数情况下,你将想要该方法返回的结果为True。只有在处理某些错误的时候,我们需要关闭这个软件,比如内存泄漏,资源缺失等等。
关于OnInit方法和__init__方法的冲突问题。
当我们在应用程序对象中使用了OnInit方法的时候,其实OnInit是属于wxpython中的一部分,我们所有的初始化的操作其实都在OnInit方法中完成就可以了,如果遇到了必须使用__init__方法的时候,则必须在方法中再次调用父类的__init__方法,就像是这样:wx.App.__init__(self)
一般情况下,你的应用程序对象里面需要实例化一个Frame对象,或者是通过SetTopWindow方法来指定一个顶级窗口,这两种方法都是可以的,然后你的顶级窗口会默认当作那些没有被设置为顶级窗口的父窗口,而区分是不是顶级窗口的方法除了看有没有在应用程序对象中被设置为顶级窗口之外,还有一个重要的点就是顶级窗口对象是没有父窗口的。
何时可以省略App对象
虽说我们之前说了,在你创建一个界面之前都建议先创建一个应用程序对象,但是在某些时候,其实他也是可以被省略的。其实到目前位置,我们创建应用程序对象就是为了设置顶级窗口这一个目的,而当我们的界面中只有一个界面的时候,它会被默认设置为顶级对象,那么这个时候,其实我们是可以省略掉这个应用程序对象的。但是这个的实用性其实不高,我们前期学习还是要遵循一个规范开发的要求,这个了解一下就可以了。
理解应用程序的生命周期
你的wxPython应用程序对象的生命周期开始于应用程序实例被创建时,在最后一个应用程序窗口被关闭时结束。这个没有必要与你的wxPython应用程序所在的Python脚本的开始和结束相对应。Python脚本可以在wxPython应用程序创建之前选择做动作,并可以在wxPython应用程序的MainLoop()退出后做一些清理工作。然而所有的wxPython动作必须在应用程序对象的生命周期中执行。正如我们曾提到过的,这意味你的主框架对象在wx.App对象被创建之前不能被创建。(这就是为什么我们建议在OnInit()方法中创建顶级框架——因为这样一来,就确保了这个应用程序已经存在。)
创建应用程序对象触发OnInit()方法并允许新的窗口对象被创建。在OnInit()之后,这个脚本调用MainLoop()方法,通知wxPython事件现在正在被处理。在窗口被关闭之前应用程序继续它的事件处理。当所有顶级窗口被关闭后,MainLoop()函数返回同时应用程序对象被注销。这之后,这个脚本能够关闭其它的可能存丰的连接或线程。
理解生命周期可以让我们知道我们的某些动作应该在什么位置执行,比如初始化对象和回收资源的位置,防止因此而导致的逻辑错误。
如何关闭WxPython应用程序?
当你的应用程序的最后的顶级窗口被用户关闭时,wxPython应用程序就退出了。我们这里所说的顶层窗口是指任何没有父窗口的窗口,并不只是使用SetTopWindow()方法设计的窗口。这包括任何由wxPython自身创建的窗口。要使用编程触发一个关闭,你可以在所有的这里所谓顶级窗口上调用Close()方法。
管理正常的关闭
在关闭的过程期间,wxPython关心的是删除所有的它的窗口和释放它们的资源。你可以在退出过程中定义一个钩子函数来执行你自己的清理工作。由于你的wx.App子类的OnExit()方法在最后一个窗口被关闭后且在wxPython的内在的清理过程之前被调用,你可以使用OnExit()方法来清理你创建的任何非wxPython资源(例如一个数据库连接)。即使使用了wx.Exit()来关闭wxPython程序,OnExit()方法仍将被触发。
如果由于某种原因你想在最后的窗口被关闭后wxPython应用程序仍然可以继续,你可以使用wx.App的SetExitOnFrameDelete(flag)方法来改变默认的行为。如果flag参数设置为False,那么最后的窗口被关闭后wxPython应用程序仍然会继续运行。这意味着wx.App实例将继续存活,并且事件循环将继续处理事件,比如这时你还可以创建所有新的这里所谓的顶级窗口。wxPython应用程序将保持存活直到全局函数wx.Exit()被明确地调用。
管理紧急关闭
你不能总是以一个可控的方法关闭你的程序。有时候,你需要立即结束应用程序并不考虑清理工作。例如一个必不可少的资源可能已被关闭或被损坏。如果系统正在关闭,你可能不能做所有的清理工作。比如当应用程序崩溃,或者是闪退的时候,这个时候是因为整个的程序紧急退出,而导致本来应该进行资源回收的代码没有被执行。
这里有两种在紧急情况下退出你的wxPython应用程序的方法。你可以调用wx.App的ExitMainLoop()方法。这个方法显式地使用主消息循环终止,使用控制离开MainLoop()函数。这通常将终止应用程序,这个方法实际上等同于关闭所有这里所谓顶级窗口。
你也可以调用全局方法wx.Exit()。正常使用情况下,两种方法我们都不推荐,因为它将导致一些资源回收函数被跳过。
有时候,你的应用程序由于一个控制之外的事件而需要关闭。例如操作系统的关闭或注销。在这种情况下,你的应用程序将试图做一些保存文档或关闭连接等等。如果你的应用程序为wx.EVT_QUERY_END_SESSION事件绑定了一个事件处理器,那么当wxPython得到关闭通知时这个事件处理器将被调用。这个event参数是wx.CloseEvent。我们可以通过关闭事件来否决关闭。这可以使用关闭事件的CanVeto()方法,CanVeto()方法决定是否可以否决,Veto()执行否决。如果你不能成功地保存或关闭所有的资源,你可能想使用该方法。wx.EVT_QUERY_END_SESSION事件的默认处理器调用顶级窗口的Close()方法,这将依次向顶层窗口发送wx.EVT_CLOSE事件,这给了你控制关闭过程的另一选择。如果任何一个Close()方法返回False,那么应用程序将试图否决关闭。
如何创建和使用顶级窗口
在你的应用程序中一个顶级窗口对象是一个窗口部件(通常是一个框架,按照wxPython中的说法,框架就是用户通常称的窗口。),它不被别的窗口部件所包含。顶级窗口对象通常是你的应用程序的主窗口,它包含用户与之交互的窗口部件和界面对象。当所有的顶级窗口被关闭时应用程序退出。
你的应用程序至少必须有一个顶级窗口对象。顶级窗口对象通常是类wx.Frame的子类,尽管它也可以是wx.Dialog的子类。大多数情况下,你会为你的应用程序使用自定义的wx.Frame的子类。然而,这儿也存在一定数量的预定义的wx.Dialog的子类,它们提供了许多你可能会在一个应用程序中遇到的典型的对话框。
wx.Dialog 是 wxPython 库中的一个类,用于创建对话框。对话框通常用于显示临时窗口,用于与用户进行交互,例如显示信息、获取输入或警告用户。
这儿可能有一个名称上的混淆,那就是“顶级窗口”。一般意义上的顶级窗口是指在你的应用程序中任何没有父窗口的窗口部件。你的应用程序必须至少有一个,但是,只要你喜欢可以有多个。但是它们中只有一个可以通过使用SetTopWindow()被wxPython作为主顶级窗口。如果你没有使用SetTopWindow()指定主顶级窗口,那么在wx.App的顶级窗口列表中的第一个框架将被认为是这个主顶级窗口。因此,明确地定义一个主顶级窗口不总是必要的,例如,你只有一个顶级窗口的时候。反复调用SetTopWindow()将反复改变当前的主顶级窗口,因为一个应用程序一次只能有一主顶级窗口。
使用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): # 定义一个名为 myFrame 的类,继承自 wx.Frame
def __init__(self): # 定义类的构造函数
# 调用父类 wx.Frame 的构造函数,初始化窗口
# 参数说明:
# None: 父窗口,这里没有父窗口
# -1: 窗口标识符,-1 表示自动生成
# "My Frame Window": 窗口的标题
# (100, 100): 窗口的位置,左上角坐标为 (100, 100)
# (800, 600): 窗口的大小,宽度为 800 像素,高度为 600 像素
wx.Frame.__init__(self, None, -1, "My Frame Window", (100, 100), (800, 600))
注意这里面有两个
__init__
初始化函数,外层的初始化函数是自己定义的这个子类,也就是myFrame函数的自定义函数,内层的初始化函数是调用的父类的创建窗口的初始化函数,这两个地方要注意分清一下
使用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给窗口部件的构造器
明确地选择ID号
第一个或最直接的方法是明确地给构造器传递一个正的整数作为该窗口部件的ID。如果你这样做了,你必须确保在一个框架内没有重复的ID或重用了一个预定义的常量。你可以通过调用wx.RegisterId()来确保在应用程序中wxPython不在别处使用你的ID。要防止你的程序使用相同的wxPython ID,你应该避免使用全局常量wx.ID_LOWEST和wx.ID_HIGHEST之间的ID号。
使用全局性的NewID()函数
自己确保ID号的唯一性十分麻烦,你可以使用wx.NewId()函数让wxPython来为你创建ID:
id = wx.NewId()
frame = wx.Frame.__init__(None, id)
你也可以给窗口部件的构造器传递全局常量wx.ID_ANY或-1,然后wxPython将为你生成新的ID。然后你可以在需要这个ID时使用GetId()方法来得到它:
frame = wx.Frame.__init__(None, -1)
id = frame.GetId()
使用wx.Size和wx.Point
wx.Frame构造器的参数也引用了类wx.Size和wx.Point。这两个类在你的wxPython编程中将频繁被使用。
wx.Point类表示一个点或位置。构造器要求点的x和y值。如果不设置x,y值,则值默认为0。我们可以使用Set(x,y)和Get()函数来设置和得到x和y值。Get()函数返回一个元组。x和y值可以像下面这样作为属性被访问:
point = wx.Point(10, 12)
x = point.x
y = point.y
在wx.Point的实参中,坐标值一般为整数。如果你需要浮点数坐标,你可以使用类wx.RealPoint,它的用法如同wx.Point。
wx.Size类几乎和wx.Point完全相同,除了实参的名字是width和height。对wx.Size的操作与wx.Point一样。
在你的应用程序中当一个wx.Point或wx.Size实例被要求的时候(例如在另一个对象的构造器中),你不必显式地创建这个实例。你可以传递一个元组给构造器,wxPython将隐含地创建这个wx.Point或wx.Size实例:
frame = wx.Frame(None, -1, pos=(10, 10), size=(100, 100))
其实大多数情况下,我们会直接写具体的坐标,等我们学会了使用网格的时候,会使用网格来进行排版。这个地方一般是在设置控件的大小的时候会用的比较多。