WxPython FAQ Gripts

Материал из Wiki.crossplatform.ru

(Различия между версиями)
Перейти к: навигация, поиск
ViGOur (Обсуждение | вклад)
(Новая: 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 th...)
Следующая правка →

Версия 11:02, 19 февраля 2009

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.

center

#!/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.

center

#!/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.

center

#!/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.

center

#!/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.