在上一篇:
中,我们给程序增加了一个简单的界面,界面上显示了5天的天气信息,还有三个按钮。不过到目前为止,按钮还只是个摆设,没有具体功能。这一次,我要实现按钮的作用,也就是给按钮增加点击事件处理程序。本文转自三思之旅博客http://think3t.iteye.com,转载请注明出处。
事件处理是wxPython程序工作的基本机制。执行事件处理的工作称为事件驱动。事件驱动程序主要是一个控制结构,它接受事件并响应它们。wxPython程序(或任何事件驱动程序)的结构与平常的Python脚本不同。标准的Python脚本有一个特定的开始点和结束点,程序员使用条件、循环、和函数来控制执行顺序。从用户的角度上来看,wxPython程序大部分时间什么也不做,一直闲着直到用户或系统做了些什么来触发这个wxPython程序动作。wxPython程序的结构就是一个事件驱动程序体系的例子。本文转自三思之旅博客http://think3t.iteye.com,转载请注明出处。
事件驱动系统的主循环类似于客户服务呼叫中心的操作者。当没有呼叫进入的时候,这个操作者处于等待状态。当一个事件发生的时候,如电话铃响了,这个操作者开始一个响应过程,他与客户交谈直到他获得足够的信息以分派该客户给一个合适的回答者。然后操作者等待下一个事件。尽管每个事件驱动系统之间有一些不同,但它们有很多相似的地方。下面列出了事件驱动程序结构的主要特点:
1、在初始化设置之后,程序的大部分时间花在了一个空闭的循环之中。进入这个循环就标志着程序与用户交互的部分的开始,退出这个循环就标志结束。在wxPython中,这个循环的方法是:wx.App.MainLoop(),并且在你的脚本中显式地被调用。当所有的顶级窗口关闭时,主循环退出。
2、程序包含了对应于发生在程序环境中的事情的事件。事件通常由用户的行为触发,但是也可以由系统的行为或程序中其他任意的代码。在wxPython中,所有的事件都是类wx.Event或其子类的一个实例。每个事件都有一个事件类型属性,它使得不同的事件能够被辨别。例如,鼠标释放和鼠示按下事件都被认为是同一个类的实例,但有不同的事件类型。
3、作为这个空闭的循环部分,程序定期检查是否有任何请求响应事情发生。有两种机制使得事件驱动系统可以得到有关事件的通知。最常被wxPython使用的方法是,把事件传送到一个中心队列,由该队列触发相应事件的处理。另一种方法是使用轮询的方法,所有可能引发事件的事件主被主过程定期查询并询问是否有没有处理的事件。
4、当事件发生时,基于事件的系统试着确定相关代码来处理该事件,如果有,相关代码被执行。在wxPython中,原系统事件被转换为wx.Event实例,然后使用wx.EvtHandler.ProcessEvent()方法将事件分派给适当的处理器代码。
事件机制的组成部分是事件绑定器对象和事件处理器。事件绑定器是一个预定义的wxPython对象。每个事件都有各自的事件绑定器。事件处理器是一个函数或方法,它要求一个wxPython事件实例作为参数。当用户触发了适当的事件时,一个事件处理器被调用。本文转自三思之旅博客http://think3t.iteye.com,转载请注明出处。
事件和事件处理器是基于相关的窗口部件的。为了要把一个来自特定窗口部件的事件绑定到一个特定的处理器方法,你要使用一个绑定器对象来管理这个连接。例如,给我们程序面板中的三个按钮绑定处理器的代码如下:
self.Bind(wx.EVT_BUTTON,self.OnRefresh,self.updateBtn) #更新按钮绑定事件处理器 self.Bind(wx.EVT_BUTTON,self.OnConfig,self.setupBtn) #设置按钮绑定事件处理器 self.Bind(wx.EVT_BUTTON,self.OnSend,self.sendBtn) #发送按钮绑定事件处理器
上面使用了预定义的事件绑定器对象wx.EVT_BUTTON来将self.updateBtn、self.setupBtn和self.sendBtn对象上的按钮单击事件与方法self.OnRefresh、self.OnConfig和self.OnSend相关联起来。这个Bind()方法是wx.EvtHandler的一个方法,wx.EvtHandler是所有可显示对象的父类。因此上面代码行可以被放置在任何显示类,此处我们放在Frame的子类里。本文转自三思之旅博客http://think3t.iteye.com,转载请注明出处。
即使你的wxPython程序表面上看起来在被动地等待事件,但它仍在做事。它在运行方法wx.App.MainLoop(),该方法是一个无限的循环。MainLoop()方法可以使用Python伪代码表示如下:
while True: while not self.Pending(): self.ProcessIdle() self.DoMessage()
上面的伪代码意思是如果没有未处理的消息,则做一些空闲时做的事;如果有消息进入,那么将这个消息分派给适当的事件处理方法。本文转自三思之旅博客http://think3t.iteye.com,转载请注明出处。
绑定了事件处理器,接下来就需要一一实现self.OnRefresh、self.OnConfig和self.OnSend方法了。本文转自三思之旅博客http://think3t.iteye.com,转载请注明出处。
def OnRefresh(self,event): self.updateWeather() #更新主面板天气信息 def OnConfig(self,event): cfgFrame=CfgFrame(self) #打开配置窗口 cfgFrame.Show(True) #显示配置窗口 def OnSend(self, event): self.sendToUsers() #给预定义的用户发送各自的天气信息 def updateWeather(self): self.cityCode = self.searcher.getMainCityCode() wInfo = WeatherInfo('http://wap.weather.com.cn/wap/weather/%s.shtml' % self.cityCode).getWeather() wTitle = wInfo[0] wTime = wInfo[1] todayInfo = wInfo[2] daysInfo = wInfo[3:7] self.SetTitle(wTitle) self.stBar.SetStatusText(wTime,0) ###显示今日天气### self.weatherLabels[0].SetLabel(todayInfo[1]) #从文件载入图像 todayImg1 = wx.Image("img/a_" + todayInfo[2], wx.BITMAP_TYPE_GIF) todayImg2 = wx.Image("img/a_" + todayInfo[3], wx.BITMAP_TYPE_GIF) #转换为静态图像控件 self.weatherIcons[0].SetBitmap(wx.BitmapFromImage(todayImg1)) self.weatherIcons[1].SetBitmap(wx.BitmapFromImage(todayImg2)) ###显示未来4日天气### i = 0 for dayInfo in daysInfo: self.weatherLabels[2*i+1].SetLabel(dayInfo[0]) self.weatherLabels[2*i+2].SetLabel(dayInfo[1]) img1 = wx.Image("img/" + dayInfo[2], wx.BITMAP_TYPE_GIF) img2 = wx.Image("img/" + dayInfo[3], wx.BITMAP_TYPE_GIF) self.weatherIcons[2*i+2].SetBitmap(wx.BitmapFromImage(img1)) self.weatherIcons[2*i+3].SetBitmap(wx.BitmapFromImage(img2)) i += 1 self.hdLeft.Fit(self) self.hdRight.Fit(self) self.head.Fit(self) self.days.Fit(self) self.sizer.Fit(self) return True def sendToUsers(self): #to be continued
CfgFrame类以及sendToUsers方法将在下一篇中给出,本篇先实现更新按钮的功能,同时对wxPython事件处理机制进行了简单介绍 。其实wxPython事件处理机制简单的说就两句话,先绑定,然后实现事件处理方法,搞定!
打造自己的天气系列文章写到这里已经第四篇了,然后也发现了最初程序里的一些问题。最开始写主窗口MyFrame类的时候,将数据和显示部分的代码写在了一起,然后写updateWeather方法的时候,发现无法实现。于是就修改了一下MyFrame类的代码,将显示部分和数据分开来,通过一个接口方法实现数据在控件上的显示。本文转自三思之旅博客http://think3t.iteye.com,转载请注明出处。
好了,本篇文章到此为止了。最近两个月因为工作中封闭开发加上过年,整天忙的晕头转向的,所以第四篇文章直到今天才更新。今后工作不忙的时候,我会及时更新博客,请大家继续关注