wxPython 教程 (十五): wxPython Gripts

总目录: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() 退出连接。

tutorial wxpython-jiaocheng

图: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 连接我们对其进行退出,状态栏则显示未连接图标。

tutorial wxpython-jiaocheng

图: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 指向新的位置。

tutorial wxpython-jiaocheng

图:Puzzle

在本节,我们展示了一些有趣的 gripts。


上一节:wxPython 教程 (十四): 提示和技巧          下一节:wxPython 教程 (十六): wxPython 俄罗斯方块

发表评论

电子邮件地址不会被公开。 必填项已用*标注