总目录:wxPython 教程目录 本节内容:wxPython Gripts 本节译自:zetcode 上一篇:wxPython 教程 (十四): 提示和技巧 下一篇:wxPython 教程 (十六): wxPython 俄罗斯方块
本节中,我们将展示一些简单但完整的脚本,这些图形脚本也叫做 gripts 将展示不同的编程领域应用。
我们一共展示 3 个 wxPython gripts 。第一个发送 email 消息,第二个连接至匿名 FTP 账户并展示一个已连接或未连接图像,最后一个创建一个拼图游戏。
Tom
Tom 是一个简单脚本,用来发送 email。
#!/usr/bin/python # -*- coding: utf-8 -*- import wx import smtplib class Example(wx.Dialog): def __init__(self, *args, **kw): super(Example, self).__init__(*args, **kw) self.InitUI() def InitUI(self): pnl = wx.Panel(self) vbox = wx.BoxSizer(wx.VERTICAL) hbox1 = wx.BoxSizer(wx.HORIZONTAL) hbox2 = wx.BoxSizer(wx.HORIZONTAL) hbox3 = wx.BoxSizer(wx.HORIZONTAL) st1 = wx.StaticText(pnl, label='From') st2 = wx.StaticText(pnl, label='To ') st3 = wx.StaticText(pnl, label='Subject') self.tc1 = wx.TextCtrl(pnl, size=(180, -1)) self.tc2 = wx.TextCtrl(pnl, size=(180, -1)) self.tc3 = wx.TextCtrl(pnl, size=(180, -1)) self.tc = wx.TextCtrl(pnl, style=wx.TE_MULTILINE) button_send = wx.Button(pnl, label='Send') hbox1.Add(st1, flag=wx.LEFT, border=10) hbox1.Add(self.tc1, flag=wx.LEFT, border=35) hbox2.Add(st2, flag=wx.LEFT, border=10) hbox2.Add(self.tc2, flag=wx.LEFT, border=50) hbox3.Add(st3, flag=wx.LEFT, border=10) hbox3.Add(self.tc3, flag=wx.LEFT, border=20) vbox.Add(hbox1, flag=wx.TOP, border=10) vbox.Add(hbox2, flag=wx.TOP, border=10) vbox.Add(hbox3, flag=wx.TOP, border=10) vbox.Add(self.tc, proportion=1, flag=wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT, border=15) vbox.Add(button_send, flag=wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, border=20) self.Bind(wx.EVT_BUTTON, self.OnSend, button_send) pnl.SetSizer(vbox) self.SetSize((400, 420)) self.SetTitle('Tom') self.Centre() self.ShowModal() self.Destroy() def OnSend(self, e): sender = self.tc1.GetValue() recipient = self.tc2.GetValue() subject = self.tc3.GetValue() text = self.tc.GetValue() header = 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n' % (sender, recipient, subject) message = header + text try: server = smtplib.SMTP('mail.chello.sk') server.sendmail(sender, recipient, message) server.quit() dlg = wx.MessageDialog(self, 'Email was successfully sent', 'Success', wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() except smtplib.SMTPException, error: dlg = wx.MessageDialog(self, 'Failed to send email', 'Error', wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() def main(): ex = wx.App() Example(None) ex.MainLoop() if __name__ == '__main__': main()
我们有一个对话框,包含 from(发件人)、to(收件人)和主题的文本控件,以及一个消息文本控件。再点击发送按钮后,email 将被发送至接受者。
import smtplib
我们需要导入 SMTP 模块来发送 email,这个模块是 Python 的模块。
header = 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n' % (sender, recipient, subject)
From、To 和 Subject 即 发件、收件、主题必须用回车换行分隔开,这是 RFC 821 标准规定的,我们必须遵守。
server = smtplib.SMTP('mail.chello.sk') server.sendmail(sender, recipient, message) server.quit()
接着我们可以创建 SMTP 连接。这里,你可以自定义设置。 每个 ISP 服务商会提供 pop 和 SMTP 的服务器,在我们的例子里,均为 ‘mail.chello.sk’ 。使用 sendmail() 方法可以发送邮件,最终,我们使用 quit() 退出连接。
图:Tom
Kika
Kika 是一个连接 FTP 的 gript。如果成功登陆,Kika 会在状态栏显示一个已连接图标,否则,显示未连接图标。我们使用 Python 的标准模块 ftplib 来进行操作。如果没有 FTP 账户,可以试着登录一些允许匿名登录的 FTP 站点。
#!/usr/bin/python from ftplib import FTP, all_errors import wx class MyStatusBar(wx.StatusBar): def __init__(self, parent): super(MyStatusBar, self).__init__(parent) self.SetFieldsCount(2) self.SetStatusText('Welcome to Kika', 0) self.SetStatusWidths([-1, 50]) self.icon = wx.StaticBitmap(self, bitmap=wx.Bitmap('disconnected.png')) self.Bind(wx.EVT_SIZE, self.OnSize) self.PlaceIcon() def PlaceIcon(self): rect = self.GetFieldRect(1) self.icon.SetPosition((rect.x+5, rect.y+1)) def OnSize(self, e): e.Skip() self.PlaceIcon() class Example(wx.Frame): def __init__(self, *args, **kw): super(Example, self).__init__(*args, **kw) self.InitUI() def InitUI(self): wx.StaticText(self, label='Ftp site', pos=(10, 20)) wx.StaticText(self, label='Login', pos=(10, 60)) wx.StaticText(self, label='Password', pos=(10, 100)) self.ftpsite = wx.TextCtrl(self, pos=(110, 15), size=(120, -1)) self.login = wx.TextCtrl(self, pos=(110, 55), size=(120, -1)) self.password = wx.TextCtrl(self, pos=(110, 95), size=(120, -1), style=wx.TE_PASSWORD) self.ftp = None con = wx.Button(self, label='Connect', pos=(10, 160)) discon = wx.Button(self, label='DisConnect', pos=(120, 160)) self.Bind(wx.EVT_BUTTON, self.OnConnect, con) self.Bind(wx.EVT_BUTTON, self.OnDisConnect, discon) self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize) self.Bind(wx.EVT_SHOW, self.OnShown) self.sb = MyStatusBar(self) self.SetStatusBar(self.sb) self.SetSize((250, 270)) self.SetTitle('Kika') self.Centre() self.Show() def OnShown(self, e): if self.sb: self.sb.PlaceIcon() def OnMaximize(self, e): self.sb.PlaceIcon() def OnConnect(self, e): if not self.ftp: ftpsite = self.ftpsite.GetValue() login = self.login.GetValue() password = self.password.GetValue() try: self.ftp = FTP(ftpsite) var = self.ftp.login(login, password) self.sb.SetStatusText('User connected') self.sb.icon.SetBitmap(wx.Bitmap('connected.png')) except AttributeError: self.sb.SetStatusText('Incorrect params') self.ftp = None except all_errors, err: self.sb.SetStatusText(str(err)) self.ftp = None def OnDisConnect(self, e): if self.ftp: self.ftp.quit() self.ftp = None self.sb.SetStatusText('User disconnected') self.sb.icon.SetBitmap(wx.Bitmap('disconnected.png')) def main(): ex = wx.App() Example(None) ex.MainLoop() if __name__ == '__main__': main()
在代码例子中,我们将连接到 FTP 并显示连接或未连接的图标。
from ftplib import FTP, all_errors
我们使用了 Python 的标准 ftplib 模块。
self.SetFieldsCount(2) self.SetStatusText('Welcome to Kika', 0) self.SetStatusWidths([-1, 50])
我们的状态栏有两个 fields。第一个 field 将显示欢迎信息, SetStatusWidth() 方法将设置 field 的宽度。第二个 field 有一个固定宽度,第一个则占用状态栏剩余的宽度。
def PlaceIcon(self): rect = self.GetFieldRect(1) self.icon.SetPosition((rect.x+5, rect.y+1))
PlaceIcon() 方法将图标放置在状态栏。GetFieldRect() 可以得到状态栏第二个 field 的宽度,之后我们使用 SetPosition() 方法将图标放置。
def OnSize(self, e): e.Skip() self.PlaceIcon()
注意到,再次窗口调整大小时,我们要把图标放到一个新的位置。
self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize) self.Bind(wx.EVT_SHOW, self.OnShown)
我们也需要处理 wx.EVT_MAXIMIZE 和 wx.EVT_SHOW 事件。在这些事件处理器中,我们将图标放置在状态栏上。
try: self.ftp = FTP(ftpsite) var = self.ftp.login(login, password) self.sb.SetStatusText('User connected') self.sb.icon.SetBitmap(wx.Bitmap('connected.png'))
我们登录到提供的 FTP 站点,如果连接成功则显示已连接图标。
def OnDisConnect(self, e): if self.ftp: self.ftp.quit() self.ftp = None self.sb.SetStatusText('User disconnected') self.sb.icon.SetBitmap(wx.Bitmap('disconnected.png'))
在 OnDisConnect() 方法中,如果有 FTP 连接我们对其进行退出,状态栏则显示未连接图标。
图:Kika
Puzzle
在这个 gript 中,我们将设计一个拼图游戏。我们有来自 Ice Age 电影的 Sid 的图片,拼图游戏的目标是形成完整的图片。
$ convert sid.png -crop 120x90 sid%d.png $ ls sid0.png sid2.png sid4.png sid6.png sid8.png sid1.png sid3.png sid5.png sid7.png sid.png
ImageMagick 程序可以用来将图片切割成多个更小的图片。如果图片大小是 360×270,上面的命令将把图片分割成 9 部分。
#!/usr/bin/python # -*- coding: utf-8 -*- import wx import random class Example(wx.Dialog): def __init__(self, *args, **kw): super(Example, self).__init__(*args, **kw) self.InitUI() def InitUI(self): images = ['sid1.png', 'sid2.png', 'sid3.png', 'sid4.png', 'sid5.png', 'sid6.png', 'sid7.png', 'sid8.png'] self.pos = [ [0, 1, 2], [3, 4, 5], [6, 7, 8] ] self.sizer = wx.GridSizer(3, 3, 0, 0) numbers = [0, 1, 2, 3, 4, 5, 6, 7] random.shuffle(numbers) for i in numbers: btn = wx.BitmapButton(self, i, wx.Bitmap(images[i])) btn.Bind(wx.EVT_BUTTON, self.OnPressButton, btn) self.sizer.Add(btn) self.empty = wx.BitmapButton(self, bitmap=wx.Bitmap('empty.png')) self.empty.Bind(wx.EVT_BUTTON, self.OnPressButton, self.empty) self.sizer.Add(self.empty) self.SetSizerAndFit(self.sizer) self.SetTitle('Puzzle') self.Centre() self.ShowModal() self.Destroy() def OnPressButton(self, e): btn = e.GetEventObject() width = self.empty.GetSize().x height = self.empty.GetSize().y btnX = btn.GetPosition().x btnY = btn.GetPosition().y emptyX = self.empty.GetPosition().x emptyY = self.empty.GetPosition().y if (((btnX == emptyX) and (emptyY - btnY) == height) or ((btnX == emptyX) and (emptyY - btnY) == -height) or ((btnY == emptyY) and (emptyX - btnX) == width) or ((btnY == emptyY) and (emptyX - btnX) == -width)): self.ExchangeImages(btn) def ExchangeImages(self, btn): bmp1 = self.empty.GetBitmapLabel() bmp2 = btn.GetBitmapLabel() self.empty.SetBitmapLabel(bmp2) btn.SetBitmapLabel(bmp1) self.empty = btn def main(): ex = wx.App() Example(None) ex.MainLoop() if __name__ == '__main__': main()
图片被分成 9 个子图。在程序中将使用其中 8 个,剩余一个为空白图片。
images = ['sid1.png', 'sid2.png', 'sid3.png', 'sid4.png', 'sid5.png', 'sid6.png', 'sid7.png', 'sid8.png']
这些图片将被显示在 button 控件中。
self.sizer = wx.GridSizer(3, 3, 0, 0)
对这个 gript,wx.GridSizer 非常实用。
numbers = [0, 1, 2, 3, 4, 5, 6, 7] random.shuffle(numbers)
我们有 8 个数字,这些数字被搅乱来使得我们有随机的顺序。每次打开程序,都会有不一样的顺序。
for i in numbers: btn = wx.BitmapButton(self, i, wx.Bitmap(images[i])) btn.Bind(wx.EVT_BUTTON, self.OnPressButton, btn) self.sizer.Add(btn)
在循环中,我们创建 8 个 bitmap 按钮,每个按钮都绑定了事件处理器。
self.empty = wx.BitmapButton(self, bitmap=wx.Bitmap('empty.png')) self.empty.Bind(wx.EVT_BUTTON, self.OnPressButton, self.empty) self.sizer.Add(self.empty)
第九个图片是空白图片, self.empty 被用来指代它。按钮之间会交换图片,所以 self.empty 会经常变动位置。
def OnPressButton(self, e): btn = e.GetEventObject() width = self.empty.GetSize().x height = self.empty.GetSize().y ...
当我们按下按钮时,OnPressButton() 函数会被调用。GetEventObject() 会得到时间来源,即触发事件的按钮。我们得到空白按钮的高和宽,其他按钮的大小和这是一样的。这些值将被用来决定空白按钮周围的按钮。
btnX = btn.GetPosition().x btnY = btn.GetPosition().y emptyX = self.empty.GetPosition().x emptyY = self.empty.GetPosition().y
我们得到目前点击的按钮的坐标和空白按钮的坐标。
if (((btnX == emptyX) and (emptyY - btnY) == height) or ((btnX == emptyX) and (emptyY - btnY) == -height) or ((btnY == emptyY) and (emptyX - btnX) == width) or ((btnY == emptyY) and (emptyX - btnX) == -width)): self.ExchangeImages(btn)
这里,我们判断点击的按钮是否与空白按钮相邻,如果是真的,我们会调用 ExchangeImages() 方法来交换两个按钮的图片。
def ExchangeImages(self, btn): bmp1 = self.empty.GetBitmapLabel() bmp2 = btn.GetBitmapLabel() self.empty.SetBitmapLabel(bmp2) btn.SetBitmapLabel(bmp1) self.empty = btn
在 ExchangeImages() 方法中,我们得到两个按钮的图片,交换他们,将 self.empty 指向新的位置。
图:Puzzle
在本节,我们展示了一些有趣的 gripts。
上一节:wxPython 教程 (十四): 提示和技巧 下一节:wxPython 教程 (十六): wxPython 俄罗斯方块