WxPython FAQ Gripts
Материал из Wiki.crossplatform.ru
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.
Содержание |
Tom
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()
Editor
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
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))
Puzzle
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.