In this section we will show some small, complete scripts. These graphical scripts or "gripts" will demonstrate various areas in programming. Programming in Python, wxPython is easier than in most other toolkits. But it is still a laborious task. There is a long, long way from easy scripts to professional applications.
Each application should have a good name. Short and easily remembered. So, we have Tom. A simple gript that sends an email.
#!/usr/bin/python
# Tom
import wx
import smtplib
class Tom(wx.Dialog):
def __init__(self, parent, id, title):
wx.Dialog.__init__(self, parent, id, title, size=(400, 420))
panel = wx.Panel(self, -1)
vbox = wx.BoxSizer(wx.VERTICAL)
hbox1 = wx.BoxSizer(wx.HORIZONTAL)
hbox2 = wx.BoxSizer(wx.HORIZONTAL)
hbox3 = wx.BoxSizer(wx.HORIZONTAL)
st1 = wx.StaticText(panel, -1, 'From')
st2 = wx.StaticText(panel, -1, 'To ')
st3 = wx.StaticText(panel, -1, 'Subject')
self.tc1 = wx.TextCtrl(panel, -1, size=(180, -1))
self.tc2 = wx.TextCtrl(panel, -1, size=(180, -1))
self.tc3 = wx.TextCtrl(panel, -1, size=(180, -1))
self.write = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE)
button_send = wx.Button(panel, 1, 'Send')
hbox1.Add(st1, 0, wx.LEFT, 10)
hbox1.Add(self.tc1, 0, wx.LEFT, 35)
hbox2.Add(st2, 0, wx.LEFT, 10)
hbox2.Add(self.tc2, 0, wx.LEFT, 50)
hbox3.Add(st3, 0, wx.LEFT, 10)
hbox3.Add(self.tc3, 0, wx.LEFT, 20)
vbox.Add(hbox1, 0, wx.TOP, 10)
vbox.Add(hbox2, 0, wx.TOP, 10)
vbox.Add(hbox3, 0, wx.TOP, 10)
vbox.Add(self.write, 1, wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT, 15)
vbox.Add(button_send, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 20)
self.Bind(wx.EVT_BUTTON, self.OnSend, id=1)
panel.SetSizer(vbox)
self.Centre()
self.ShowModal()
self.Destroy()
def OnSend(self, event):
sender = self.tc1.GetValue()
recipient = self.tc2.GetValue()
subject = self.tc3.GetValue()
text = self.write.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()
app = wx.App()
Tom(None, -1, 'Tom')
app.MainLoop()
For working with emails we need to import smtp module. This module is part of the python language.
import smtplib
From, To and Subject options must be separated by carriedge return and newline as shown here. This weird thing is requested by RFC 821 norm. So we must follow it.
header = 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n' % (sender, recipient, subject)
Next we create an SMTP connection. Here you specify your settings. Each ISP gives you the name of the pop and smtp servers. In my case, 'mail.chello.sk' is a name for both. A mail is sent by calling the sendmail() method. Finally, we quit the connection with the quit() method.
server = smtplib.SMTP('mail.chello.sk')
server.sendmail(sender, recipient, message)
server.quit()
This editor example is the largest so far.
#!/usr/bin/python
# Editor
import wx
import os
class Editor(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(600, 500))
# variables
self.modify = False
self.last_name_saved = ''
self.replace = False
# setting up menubar
menubar = wx.MenuBar()
file = wx.Menu()
new = wx.MenuItem(file, 101, '&New\tCtrl+N', 'Creates a new document')
new.SetBitmap(wx.Bitmap('icons/stock_new-16.png'))
file.AppendItem(new)
open = wx.MenuItem(file, 102, '&Open\tCtrl+O', 'Open an existing file')
open.SetBitmap(wx.Bitmap('icons/stock_open-16.png'))
file.AppendItem(open)
file.AppendSeparator()
save = wx.MenuItem(file, 103, '&Save\tCtrl+S', 'Save the file')
save.SetBitmap(wx.Bitmap('icons/stock_save-16.png'))
file.AppendItem(save)
saveas = wx.MenuItem(file, 104, 'Save &As...\tShift+Ctrl+S',
'Save the file with a different name')
saveas.SetBitmap(wx.Bitmap('icons/stock_save_as-16.png'))
file.AppendItem(saveas)
file.AppendSeparator()
quit = wx.MenuItem(file, 105, '&Quit\tCtrl+Q', 'Quit the Application')
quit.SetBitmap(wx.Bitmap('icons/stock_exit-16.png'))
file.AppendItem(quit)
edit = wx.Menu()
cut = wx.MenuItem(edit, 106, '&Cut\tCtrl+X', 'Cut the Selection')
cut.SetBitmap(wx.Bitmap('icons/stock_cut-16.png'))
edit.AppendItem(cut)
copy = wx.MenuItem(edit, 107, '&Copy\tCtrl+C', 'Copy the Selection')
copy.SetBitmap(wx.Bitmap('icons/stock_copy-16.png'))
edit.AppendItem(copy)
paste = wx.MenuItem(edit, 108, '&Paste\tCtrl+V', 'Paste text from clipboard')
paste.SetBitmap(wx.Bitmap('icons/stock_paste-16.png'))
edit.AppendItem(paste)
delete = wx.MenuItem(edit, 109, '&Delete', 'Delete the selected text')
delete.SetBitmap(wx.Bitmap('icons/stock_delete-16.png',))
edit.AppendItem(delete)
edit.AppendSeparator()
edit.Append(110, 'Select &All\tCtrl+A', 'Select the entire text')
view = wx.Menu()
view.Append(111, '&Statusbar', 'Show StatusBar')
help = wx.Menu()
about = wx.MenuItem(help, 112, '&About\tF1', 'About Editor')
about.SetBitmap(wx.Bitmap('icons/stock_about-16.png'))
help.AppendItem(about)
menubar.Append(file, '&File')
menubar.Append(edit, '&Edit')
menubar.Append(view, '&View')
menubar.Append(help, '&Help')
self.SetMenuBar(menubar)
self.Bind(wx.EVT_MENU, self.NewApplication, id=101)
self.Bind(wx.EVT_MENU, self.OnOpenFile, id=102)
self.Bind(wx.EVT_MENU, self.OnSaveFile, id=103)
self.Bind(wx.EVT_MENU, self.OnSaveAsFile, id=104)
self.Bind(wx.EVT_MENU, self.QuitApplication, id=105)
self.Bind(wx.EVT_MENU, self.OnCut, id=106)
self.Bind(wx.EVT_MENU, self.OnCopy, id=107)
self.Bind(wx.EVT_MENU, self.OnPaste, id=108)
self.Bind(wx.EVT_MENU, self.OnDelete, id=109)
self.Bind(wx.EVT_MENU, self.OnSelectAll, id=110)
self.Bind(wx.EVT_MENU, self.ToggleStatusBar, id=111)
self.Bind(wx.EVT_MENU, self.OnAbout, id=112)
# setting up toolbar
self.toolbar = self.CreateToolBar( wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT
| wx.TB_TEXT )
self.toolbar.AddSimpleTool(801, wx.Bitmap('icons/stock_new.png'), 'New', '')
self.toolbar.AddSimpleTool(802, wx.Bitmap('icons/stock_open.png'), 'Open', '')
self.toolbar.AddSimpleTool(803, wx.Bitmap('icons/stock_save.png'), 'Save', '')
self.toolbar.AddSeparator()
self.toolbar.AddSimpleTool(804, wx.Bitmap('icons/stock_cut.png'), 'Cut', '')
self.toolbar.AddSimpleTool(805, wx.Bitmap('icons/stock_copy.png'), 'Copy', '')
self.toolbar.AddSimpleTool(806, wx.Bitmap('icons/stock_paste.png'), 'Paste', '')
self.toolbar.AddSeparator()
self.toolbar.AddSimpleTool(807, wx.Bitmap('icons/stock_exit.png'), 'Exit', '')
self.toolbar.Realize()
self.Bind(wx.EVT_TOOL, self.NewApplication, id=801)
self.Bind(wx.EVT_TOOL, self.OnOpenFile, id=802)
self.Bind(wx.EVT_TOOL, self.OnSaveFile, id=803)
self.Bind(wx.EVT_TOOL, self.OnCut, id=804)
self.Bind(wx.EVT_TOOL, self.OnCopy, id=805)
self.Bind(wx.EVT_TOOL, self.OnPaste, id=806)
self.Bind(wx.EVT_TOOL, self.QuitApplication, id=807)
self.text = wx.TextCtrl(self, 1000, '', size=(-1, -1), style=wx.TE_MULTILINE
| wx.TE_PROCESS_ENTER)
self.text.SetFocus()
self.text.Bind(wx.EVT_TEXT, self.OnTextChanged, id=1000)
self.text.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Bind(wx.EVT_CLOSE, self.QuitApplication)
self.StatusBar()
self.Centre()
self.Show(True)
def NewApplication(self, event):
editor = Editor(None, -1, 'Editor')
editor.Centre()
editor.Show()
def OnOpenFile(self, event):
file_name = os.path.basename(self.last_name_saved)
if self.modify:
dlg = wx.MessageDialog(self, 'Save changes?', '', wx.YES_NO | wx.YES_DEFAULT |
wx.CANCEL | wx.ICON_QUESTION)
val = dlg.ShowModal()
if val == wx.ID_YES:
self.OnSaveFile(event)
self.DoOpenFile()
elif val == wx.ID_CANCEL:
dlg.Destroy()
else:
self.DoOpenFile()
else:
self.DoOpenFile()
def DoOpenFile(self):
wcd = 'All files (*)|*|Editor files (*.ef)|*.ef|'
dir = os.getcwd()
open_dlg = wx.FileDialog(self, message='Choose a file', defaultDir=dir, defaultFile='',
wildcard=wcd, style=wx.OPEN|wx.CHANGE_DIR)
if open_dlg.ShowModal() == wx.ID_OK:
path = open_dlg.GetPath()
try:
file = open(path, 'r')
text = file.read()
file.close()
if self.text.GetLastPosition():
self.text.Clear()
self.text.WriteText(text)
self.last_name_saved = path
self.statusbar.SetStatusText('', 1)
self.modify = False
except IOError, error:
dlg = wx.MessageDialog(self, 'Error opening file\n' + str(error))
dlg.ShowModal()
except UnicodeDecodeError, error:
dlg = wx.MessageDialog(self, 'Error opening file\n' + str(error))
dlg.ShowModal()
open_dlg.Destroy()
def OnSaveFile(self, event):
if self.last_name_saved:
try:
file = open(self.last_name_saved, 'w')
text = self.text.GetValue()
file.write(text)
file.close()
self.statusbar.SetStatusText(os.path.basename(self.last_name_saved) + ' saved', 0)
self.modify = False
self.statusbar.SetStatusText('', 1)
except IOError, error:
dlg = wx.MessageDialog(self, 'Error saving file\n' + str(error))
dlg.ShowModal()
else:
self.OnSaveAsFile(event)
def OnSaveAsFile(self, event):
wcd='All files(*)|*|Editor files (*.ef)|*.ef|'
dir = os.getcwd()
save_dlg = wx.FileDialog(self, message='Save file as...', defaultDir=dir, defaultFile='',
wildcard=wcd, style=wx.SAVE | wx.OVERWRITE_PROMPT)
if save_dlg.ShowModal() == wx.ID_OK:
path = save_dlg.GetPath()
try:
file = open(path, 'w')
text = self.text.GetValue()
file.write(text)
file.close()
self.last_name_saved = os.path.basename(path)
self.statusbar.SetStatusText(self.last_name_saved + ' saved', 0)
self.modify = False
self.statusbar.SetStatusText('', 1)
except IOError, error:
dlg = wx.MessageDialog(self, 'Error saving file\n' + str(error))
dlg.ShowModal()
save_dlg.Destroy()
def OnCut(self, event):
self.text.Cut()
def OnCopy(self, event):
self.text.Copy()
def OnPaste(self, event):
self.text.Paste()
def QuitApplication(self, event):
if self.modify:
dlg = wx.MessageDialog(self, 'Save before Exit?', '', wx.YES_NO | wx.YES_DEFAULT |
wx.CANCEL | wx.ICON_QUESTION)
val = dlg.ShowModal()
if val == wx.ID_YES:
self.OnSaveFile(event)
if not self.modify:
wx.Exit()
elif val == wx.ID_CANCEL:
dlg.Destroy()
else:
self.Destroy()
else:
self.Destroy()
def OnDelete(self, event):
frm, to = self.text.GetSelection()
self.text.Remove(frm, to)
def OnSelectAll(self, event):
self.text.SelectAll()
def OnTextChanged(self, event):
self.modify = True
self.statusbar.SetStatusText(' modified', 1)
event.Skip()
def OnKeyDown(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_INSERT:
if not self.replace:
self.statusbar.SetStatusText('INS', 2)
self.replace = True
else:
self.statusbar.SetStatusText('', 2)
self.replace = False
event.Skip()
def ToggleStatusBar(self, event):
if self.statusbar.IsShown():
self.statusbar.Hide()
else:
self.statusbar.Show()
def StatusBar(self):
self.statusbar = self.CreateStatusBar()
self.statusbar.SetFieldsCount(3)
self.statusbar.SetStatusWidths([-5, -2, -1])
def OnAbout(self, event):
dlg = wx.MessageDialog(self, '\tEditor\t\n Another Tutorial\njan bodnar 2005-2006',
'About Editor', wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
app = wx.App()
Editor(None, -1, 'Editor')
app.MainLoop()
Kika is a gript that connects to an ftp site. If a login is successfull, Kika shows a connected icon on the statusbar. Otherwise, a disconnected icon is displayed. We use an ftplib module from the python standard library. If you do not have an ftp account, you can try to login to some anonymous ftp sites.
#!/usr/bin/python
# kika.py
from ftplib import FTP, all_errors
import wx
class MyStatusBar(wx.StatusBar):
def __init__(self, parent):
wx.StatusBar.__init__(self, parent)
self.SetFieldsCount(2)
self.SetStatusText('Welcome to Kika', 0)
self.SetStatusWidths([-5, -2])
self.icon = wx.StaticBitmap(self, -1, wx.Bitmap('icons/disconnected.png'))
self.Bind(wx.EVT_SIZE, self.OnSize)
self.PlaceIcon()
def PlaceIcon(self):
rect = self.GetFieldRect(1)
self.icon.SetPosition((rect.x+3, rect.y+3))
def OnSize(self, event):
self.PlaceIcon()
class Kika(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(250, 270))
wx.StaticText(self, -1, 'Ftp site', (10, 20))
wx.StaticText(self, -1, 'Login', (10, 60))
wx.StaticText(self, -1, 'Password', (10, 100))
self.ftpsite = wx.TextCtrl(self, -1, '', (110, 15), (120, -1))
self.login = wx.TextCtrl(self, -1, '', (110, 55), (120, -1))
self.password = wx.TextCtrl(self, -1, '', (110, 95), (120, -1), style=wx.TE_PASSWORD)
self.ftp = None
con = wx.Button(self, 1, 'Connect', (10, 160))
discon = wx.Button(self, 2, 'DisConnect', (120, 160))
self.Bind(wx.EVT_BUTTON, self.OnConnect, id=1)
self.Bind(wx.EVT_BUTTON, self.OnDisConnect, id=2)
self.statusbar = MyStatusBar(self)
self.SetStatusBar(self.statusbar)
self.Centre()
self.Show()
def OnConnect(self, event):
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.statusbar.SetStatusText('User connected')
self.statusbar.icon.SetBitmap(wx.Bitmap('icons/connected.png'))
except AttributeError:
self.statusbar.SetForegroundColour(wx.RED)
self.statusbar.SetStatusText('Incorrect params')
self.ftp = None
except all_errors, err:
self.statusbar.SetStatusText(str(err))
self.ftp = None
def OnDisConnect(self, event):
if self.ftp:
self.ftp.quit()
self.ftp = None
self.statusbar.SetStatusText('User disconnected')
self.statusbar.icon.SetBitmap(wx.Bitmap('icons/disconnected.png'))
app = wx.App()
Kika(None, -1, 'Kika')
app.MainLoop()
Notice that each time the window is resized, we must position our icon to a new place.
def PlaceIcon(self):
rect = self.GetFieldRect(1)
self.icon.SetPosition((rect.x+3, rect.y+3))
In this gript, we introduce a puzzle game. We have an image of a Sid character from the Ice Age movie. It is cut into 9 pieces and shuffled. The goal is to form the picture.
#!/usr/bin/python
# puzzle.py
import wx
import random
class Puzzle(wx.Dialog):
def __init__(self, parent, id, title):
wx.Dialog.__init__(self, parent, id, title)
images = ['images/one.jpg', 'images/two.jpg', 'images/three.jpg', 'images/four.jpg',
'images/five.jpg', 'images/six.jpg', 'images/seven.jpg', 'images/eight.jpg']
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:
button = wx.BitmapButton(self, i, wx.Bitmap(images[i]))
button.Bind(wx.EVT_BUTTON, self.OnPressButton, id=button.GetId())
self.sizer.Add(button)
self.panel = wx.Button(self, -1, size=(112, 82))
self.sizer.Add(self.panel)
self.SetSizerAndFit(self.sizer)
self.Centre()
self.ShowModal()
self.Destroy()
def OnPressButton(self, event):
button = event.GetEventObject()
sizeX = self.panel.GetSize().x
sizeY = self.panel.GetSize().y
buttonX = button.GetPosition().x
buttonY = button.GetPosition().y
panelX = self.panel.GetPosition().x
panelY = self.panel.GetPosition().y
buttonPosX = buttonX / sizeX
buttonPosY = buttonY / sizeY
buttonIndex = self.pos[buttonPosY][buttonPosX]
if (buttonX == panelX) and (panelY - buttonY) == sizeY:
self.sizer.Remove(self.panel)
self.sizer.Remove(button)
self.sizer.Insert(buttonIndex, self.panel)
self.sizer.Insert(buttonIndex+3, button)
self.sizer.Layout()
if (buttonX == panelX) and (panelY - buttonY) == -sizeY:
self.sizer.Remove(self.panel)
self.sizer.Remove(button)
self.sizer.Insert(buttonIndex-3, button)
self.sizer.Insert(buttonIndex, self.panel)
self.sizer.Layout()
if (buttonY == panelY) and (panelX - buttonX) == sizeX:
self.sizer.Remove(self.panel)
self.sizer.Remove(button)
self.sizer.Insert(buttonIndex, self.panel)
self.sizer.Insert(buttonIndex+1, button)
self.sizer.Layout()
if (buttonY == panelY) and (panelX - buttonX) == -sizeX:
self.sizer.Remove(self.panel)
self.sizer.Remove(button)
self.sizer.Insert(buttonIndex-1, button)
self.sizer.Insert(buttonIndex, self.panel)
self.sizer.Layout()
app = wx.App()
Puzzle(None, -1, 'Puzzle')
app.MainLoop()
images = ['images/one.jpg', 'images/two.jpg', 'images/three.jpg', 'images/four.jpg',
'images/five.jpg', 'images/six.jpg', 'images/seven.jpg', 'images/eight.jpg']
The picture was cut into 9 parts of 100x70 size. I did it with the Gimp program. Each part of the picture is placed on one button widget. Except one.
self.sizer = wx.GridSizer(3, 3, 0, 0)
For this gript, wx.GridSizer fits ideally.
numbers = [0, 1, 2, 3, 4, 5, 6, 7] random.shuffle(numbers)
We have eight numbers. Those numbers are shuffled so that we have a random number order. Each time we start the gript, we will have a different order of bitmaps.
self.panel = wx.Button(self, -1, size=(112, 82)) self.sizer.Add(self.panel)
This button has no bitmap. It is the 'travelling' button. It always exchanges it's position with the hitted button.