转自:http://thisis.yorven.site/blog/index.php/2017/10/12/wxpython-gripts/

本节中,我们将展示一些简单但完整的脚本,这些图形脚本也叫做 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。