Posted: Jan 17, 2009
by Datazygte, Inc - contribution by rscheckelhoff-at-fourcalorieservers-dot-com
Building a Simple Script GUI1 Application
Use of the toolkit from wxPython.org (which is not affiliated with this site) comprises a quick way to create a prototype for
an application for *nix platforms.
strStarterProject = '''#!/usr/bin/env python2.51
import wxversion
wxversion.select("2.8")
from wx import *
import os
import bdb
ID_ABOUT = 101
ID_EXIT = 102
TOOL_BUTTON_0 = wx.NewId()
TOOL_BUTTON_1 = wx.NewId()
TOOL_BUTTON_2 = wx.NewId()
TOOL_BUTTON_3 = wx.NewId()
TOOL_BUTTON_4 = wx.NewId()
TOOL_BUTTON_5 = wx.NewId()
STC_EDITOR_1 = wx.NewId()
# ******************* User Code Here ******************
print "Hello "
# This extremely rudimentary project shows how simply the toolkit can be
# used to create a GUI application.
#
# The project is just a little sample code under a bsd style of license
# (for any non-interpreted binary accessory package) and a wxWidgets
# license for the interpreted code. In either case, the code is
# provided "as is", it may have bugs, and it is skeletal and unfinished.
# The code is distributed only with the hope that it might be useful, but it
# is not implied that it is useful for any particular purpose. To the maximum
# extent of applicable law, it is distributed without any warranty or guarantee.
#
# For accessory packages:
# Copyright (c) 2009, Datazygte, Inc
# Contributing author(s): RScheckelhoff
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions ar# met:
# * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the distribution.
# * Neither Datazygte, Inc, nor the names of its contributors may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# For code on this page:
# Copyright (c) 2009, Datazygte, Inc.
# Contributing author(s): RScheckelhoff
# All rights reserved, distributed under wxWidgets license
# A copy of the wxWidgets license is available at: http://www.wxwidgets.org/about/licence3.txt
'''
class MyFrame(Frame):
def __init__(self, parent, ID, title):
Frame.__init__(self, parent, ID, title,
Point(76,76), Size(880, 600))
intResult = self.SetupDebugger()
if (intResult == 0):
intResult = self.SetupMembers()
if (intResult == 0):
intResult = self.SetupMenu()
if (intResult == 0):
self._mgr = wx.aui.AuiManager(self)
intResult = self.PopulateMainFunc()
if (intResult == 0):
intResult = self.PopulateClassTree()
if (intResult == 0):
intResult = self.PopulateBreakpointList()
if (intResult == 0):
intResult = self.BuildSourceEditorAndToolbar()
if (intResult == 0):
intResult = self.PopulateBitmaps()
if (intResult == 0):
intResult = self.AddToolsToToolbar()
if (intResult == 0):
intResult = self.AddPanesToGUI()
# show the bag
self._mgr.Update()
intResult = self.SetupEvents()
if (intResult != 0):
print "Could not fully initialize GUI\n"
self.PopMessage("GUI not fully initialized\n")
# end of init method
#
# event handler methods
#
def OnStyleEditor_Change(self, event):
#
# Try to use lingo in text editor
self.stc1.SetLexer(wx.stc.STC_LEX)
#
def PopMessage(self, strMessage):
#
dlg = MessageDialog(self, strMessage,
"Program Info", OK | ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
def OnToolButton0_Click(self, event):
#
dlgFile = wx.FileDialog(self, "Choose a file", "", defaultFile = "", wildcard = "*.*",
style = wx.FD_DEFAULT_STYLE, pos = Point(300,300))
if (dlgFile != None):
dlgFile.ShowModal()
lstFiles = dlgFile.GetFilenames()
#
if (lstFiles != None):
if (len(lstFiles) != 0):
try:
self.strFileName = lstFiles[0]
if (len(lstFiles[0]) != 0):
objFile = open(self.strFileName, 'r')
except IOError, e:
print "Problem opening source file\n"
self.PopMessage("Problem opening source file\n")
else:
if (len(lstFiles[0]) != 0):
self.strText=" "
for eachLine in objFile:
self.strText+=eachLine
objFile.close()
self.stc1.SetText(self.strText)
#
dlgFile.Destroy()
else:
#
# don't know why this would ever happen. Out of memory? - No way!
self.PopMessage("Could not open file dialog.\n")
#
# C coders who don't yet have a script1 brain may see benefit in the superfluous pound signs
#
def OnToolButton1_Click(self, event):
#
dlgFile = wx.FileDialog(self, "Save to file", "", defaultFile = self.strFileName, wildcard = "*.*",
style = wx.FD_SAVE, pos = Point(300,300))
if (dlgFile != None):
#
dlgFile.ShowModal()
lstFiles = dlgFile.GetFilenames()
#
# What? no elses? Can we assume that if the user didn't pick a file he wants to cancel? I did.
#
if (lstFiles != None):
if (len(lstFiles) != 0):
#
if (len(lstFiles[0]) != 0):
#
try:
objFile = open(lstFiles[0], 'w')
except IOError, e:
print "problem finding temp flie\n"
self.PopMessage("Problem finding file.\n")
else:
try:
objFile.write(self.stc1.GetTextRaw())
except IOError, e:
self.PopMessage("Could not save file.\n")
else:
objFile.close()
#
#
#
#
dlgFile.Destroy()
else:
# Don't know why this would ever happen
self.PopMessage("Could not display file dialog\n")
#
# Tool Button #2 has been clicked
#
def OnToolButton2_Click(self, event):
#
try:
strDebugSource = self.stc1.GetTextRaw()
if (len(strDebugSource) != 0):
# TBD code here for debugger setup
x=1
else:
self.PopMessage("Empty file. Cannot execute for debugging\n")
except Exception:
self.PopMessage("Error! \n")
def OnToolButton3_Click(self, event):
#
self.stc1.SetLexer(wx.stc.STC_LEX)
self.stc1.StartStyling(0, self.stc1.GetLength())
self.stc1.SetStyling(self.stc1.GetLength(), wx.stc.STC_LEX)
self.stc1.Colourise(0,self.stc1.GetLength())
def OnToolButton5_Click(self, event):
#
try:
strTemp = self.stc1.GetTextRaw()
if (len(strTemp) != 0):
objTempFile = open('pdbcovertempfilez.tvp', 'w')
objTempFile.write(strTemp)
objTempFile.close()
res = execfile('pdbcovertempfilez.tvp')
else:
self.PopMessage("No source supplied for execution!\n")
#
except Exception:
self.PopMessage("Error in Script \n")
def OnAbout(self, event):
#
self.PopMessage("Sample Program"\n\nCopyright Datazygte, Inc 2009\n\nContributor: RL Scheckelhoff\n")
def OnQuitGUI(self, event):
#
self.Close(True)
def OnClose(self, event):
self._mgr.UnInit()
self.Destroy()
#
# non-event methods ( populate menus, lists, trees, and editors ... )
#
def SetupDebugger(self):
# TBD code here for debugger setup
self.strDebugCodePad =""
self.bdb1 = bdb.Bdb()
# in real program, return intelligent result
return 0
def SetupMembers(self):
#
self.strFileName = ""
self.strText = ""
self.strTempClientCode = ""
self.strDebugCodePadFile = "datazygte.tmp"
self.CreateStatusBar()
self.SetStatusText("This is the statusbar")
# in real program, return intelligent result
return 0
def SetupMenu(self):
#
self.menu = Menu()
self.menu.Append(ID_ABOUT, "&About",
"More information about this program")
self.menu.AppendSeparator()
self.menu.Append(ID_EXIT, "E&xit", "Terminate the program")
self.menuBar = MenuBar()
self.menuBar.Append(self.menu, "&File");
self.SetMenuBar(self.menuBar)
# in real program, return intelligent result
return 0
def PopulateClassTree(self):
#
self.tree1 = wx.TreeCtrl(self, id=-1, pos=wx.Point(-1, -1), size=wx.Size(300, 300), style=5)
root = self.tree1.AddRoot("Root Class")
class1 = self.tree1.AppendItem(root, 'clsWaddayacallit')
class2 = self.tree1.AppendItem(root, 'clsThingamajig')
class3 = self.tree1.AppendItem(root, 'clsGoodfood')
class4 = self.tree1.AppendItem(class2, 'clsGoofiness1')
class5 = self.tree1.AppendItem(class2, 'clsFoolishness1')
class6 = self.tree1.AppendItem(class3, 'clsBananas')
class7 = self.tree1.AppendItem(class3, 'clsOranges')
class8 = self.tree1.AppendItem(class3, 'clsApples')
class9 = self.tree1.AppendItem(class1, 'clsYouknowwhat')
self.tree1.ExpandAll()
# in real program, return intelligent result
return 0
def PopulateBreakpointList(self):
#
self.listview1 = wx.ListView(self, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.LC_REPORT,
validator=wx.DefaultValidator, name="Breakpoints")
self.listitem1 = wx.ListItem()
self.listitem1.SetText("class: x")
# in real program, return intelligent result
return 0
def PopulateMainFunc(self):
#
# create a text control
self.text1 = wx.TextCtrl(self, -1, ' - main function name',
wx.DefaultPosition, wx.Size(300,100),
wx.NO_BORDER | wx.TE_MULTILINE)
# in real program, return intelligent result
return 0
def PopulateBitmaps(self):
#
self.bitmapOpen = wx.Bitmap('openfile.png')
self.bitmapSave = wx.Bitmap('save2.png')
self.bitmapStart = wx.Bitmap('debug-start.png')
self.bitmapStop = wx.Bitmap('debug-stop.png')
self.bitmapPause = wx.Bitmap('debug-pause.png')
self.bitmapRun = wx.Bitmap('run2.png')
# in real program, return intelligent result
return 0
def AddToolsToToolbar(self):
#
self.toolbar1.SetToolBitmapSize((32,32))
self.toolbar1.AddTool(TOOL_BUTTON_0, self.bitmapOpen, isToggle=True, shortHelpString='Open File')
self.toolbar1.AddTool(TOOL_BUTTON_1, self.bitmapSave, isToggle=True, shortHelpString='Save File')
self.toolbar1.AddTool(TOOL_BUTTON_2, self.bitmapStart, isToggle=True, shortHelpString='Start Debug')
self.toolbar1.AddTool(TOOL_BUTTON_3, self.bitmapStop, isToggle=True, shortHelpString='Stop Debug')
self.toolbar1.AddTool(TOOL_BUTTON_4, self.bitmapPause, isToggle=True, shortHelpString='Pause Debug')
self.toolbar1.AddTool(TOOL_BUTTON_5, self.bitmapRun, isToggle=True, shortHelpString='Execute Script')
# in real program, return intelligent result
return 0
def AddPanesToGUI(self):
#
# add the panes to the manager
self._mgr.AddPane(self.toolbar1, wx.TOP, 'Toolbar');
paneClasses = self._mgr.AddPane(self.tree1, wx.LEFT, 'Classes')
paneBreakpoints = self._mgr.AddPane(self.listview1, wx.BOTTOM, 'Breakpoints')
paneMainFunc = self._mgr.AddPane(self.text1, wx.BOTTOM, 'Main function:')
paneCode = self._mgr.AddPane(self.stc1, wx.CENTER)
return 0
def BuildSourceEditorAndToolbar(self):
#
self.stc1 = wx.stc.StyledTextCtrl(self, id=STC_EDITOR_1, pos=DefaultPosition, size=DefaultSize, style=0,
name="Source code editor")
self.stc1.SetText(strStarterProject)
self.toolbar1 = wx.ToolBar(self,-1, wx.DefaultPosition,
wx.DefaultSize, wx.NO_BORDER | wx.TB_HORIZONTAL,
'toolbar1')
# in real program, return intelligent result
return 0
def SetupEvents(self):
#
self.Bind(wx.EVT_CLOSE, self.OnClose)
self.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnStyleEditor_Change)
wx.EVT_TOOL(self, TOOL_BUTTON_0, self.OnToolButton0_Click)
wx.EVT_TOOL(self, TOOL_BUTTON_1, self.OnToolButton1_Click)
wx.EVT_TOOL(self, TOOL_BUTTON_2, self.OnToolButton2_Click)
wx.EVT_TOOL(self, TOOL_BUTTON_3, self.OnToolButton3_Click)
wx.EVT_TOOL(self, TOOL_BUTTON_4, self.OnToolButton3_Click)
wx.EVT_TOOL(self, TOOL_BUTTON_5, self.OnToolButton5_Click)
EVT_MENU(self, ID_ABOUT, self.OnAbout)
EVT_MENU(self, ID_EXIT, self.OnQuitGUI)
# in real program, return intelligent result
return 0
class MyApp(App):
def OnInit(self):
frame = MyFrame(None, -1, "Sample Program")
frame.Show(True)
self.SetTopWindow(frame)
return True
app = MyApp(0)
app.MainLoop()

Download sample files
Md5 checksum on sample tarball : e8e1d7a8b2b4966018b4689d5db06d26
Wow - that E A S Y ?
Wow! GUI apps made simpler than even say, "Visual Basic". "What's the catch?", you may ask. Well, the composition of
Python1 and the library module wxPython, which depends on wxWidgets, which itself depends on gtk2, which itself depends
upon Cairo, which depends (of course) upon X windows, (the Xorg.org libs, as well as the new xlib used
by Xorg (libxcb)) ... means
a "not-so-short" dependency list.
That said, an installation on my favorite operating system is relatively simple. Here it is:
1 ) I do a fresh install of FreeBSD 7.1 with X option (just out)
a ) - That automatically gives me my script interpreter packages
2 ) I do an installation of the Gnome Desktop
a ) - That gives me Cairo, gtk2, and a few other libs
3 ) Then (having prepared a little installation CD with the remaining
requisite packages), I install:
a ) echo "Installing prerequisites"
pkg_add xcb-proto.tbz
pkg_add libpthread-stubs.tbz
pkg_add libxcb.tbz
pkg_add libmspack-0.0.20040308_3.tbz
pkg_add xcb-util.tbz
pkg_add wxgtk2-2.8.9.tbz
pkg_add tcl-8.4.19,1.tbz
pkg_add tk84.tbz
pkg_add py25-tkinter-2.5.2_2.tbz
pkg_add py25-opengl-2.0.1.07_4.tbz
pkg_add py25-wxPython-2.8.7.1_1.tbz
Only a few wrinkles ...
Note that using the current binary packages from ports does create a slight version mismatch (usually 0.0.1 or 0.0.2 diff) due to the fact that the current ports tree has these particular binary packages. While it seems to work for me (at least on an experimental basis), I intend to (at my earliest convenience), recompile some packages to make the versions match, as that is the prudent thing to do. My emphatic suggestion would be for the reader to do likewise. Recompile all of the packages and get rid of the warning messages on startup.
1 Note: Python and wxPython are copyrights belonging to other parties
2 Note: this site has nothing to do (officially) with these entities. For information directly from the source, go
to http://www.wxPython.org or
http://www.Python.org