PyEdit: A Text Editor Program/Object

PyEdit A Text Editor Program Object

In the last few decades, I've typed text into a lot of programs. Most were closed systems (I had to live with whatever decisions their designers made), and many ran on only one platform. The PyEdit program presented in this section does better on both counts: it implements a full-featured, graphical text editor program in roughly 470 lines of portable Python code (including whitespace and comments). Despite its size, PyEdit was sufficiently powerful and robust to serve as the primary tool used to code most examples in this book.

PyEdit supports all the usual mouse and keyboard text-editing operations: cut and paste, search and replace, open and save, and so on. But really, PyEdit is a bit more than just another text editor -- it is designed to be used as both a program and a library component, and can be run in a variety of roles:

Standalone mode

As a standalone text editor program, with or without the name of a file to be edited passed in on the command line. In this mode, PyEdit is roughly like other text-editing utility programs (e.g., Notepad on Windows), but also provides advanced functions such as running Python program code being edited, changing fonts and colors, and so on. More importantly, because it is coded in Python, PyEdit is easy to customize, and runs portably on Windows, X Windows, and Macintosh.

Pop-up mode

Within a new pop-up window, allowing an arbitrary number of copies to appear as pop-ups at once in a program. Because state information is stored in class instance attributes, each PyEdit object created operates independently. In this mode and the next, PyEdit serves as a library object for use in other scripts, not a canned application.

Embedded mode

As an attached component, to provide a text- editing widget for other GUIs. When attached, PyEdit uses a frame-based menu, and can optionally disable some of its menu options for an embedded role. For instance, PyView (later in this chapter) uses PyEdit in embedded mode this way to serve as a note editor for photos, and PyMail (in Chapter 11) attaches it to get an email text editor for free.

While such mixed-mode behavior may sound complicated to implement, most of PyEdit's modes are a natural by-product of coding GUIs with the class-based techniques we've seen in the last three chapters.

9.4.1 Running PyEdit

PyEdit sports lots of features, and the best way to learn how it works is to test drive it for yourself -- it can be run by starting the file textEditor.pyw, or from the PyDemo and PyGadget launcher bars described in the previous chapter (the launchers themselves live in the top level of the book examples directory tree). To give you a sampling of its interfaces, Figure 9-10 shows the main window's default appearance, after opening PyEdit's source code file.

Figure 9-10. PyEdit main window, editing itself

figs/ppy2_0910.gif

The main part of this window is a Text widget object, and if you read the last chapter's coverage of this widget, PyEdit text-editing operations will be familiar. It uses text marks, tags, and indexes, and implements cut-and-paste operations with the system clipboard so that PyEdit can paste data to and from other applications. Both vertical and horizontal scrollbars are cross-linked to the Text widget, to support movement through arbitrary files.

If PyEdit's menu and toolbars look familiar, they should -- it builds the main window with minimal code and appropriate clipping and expansion policies, by mixing in the GuiMaker class we met earlier in this chapter. The toolbar at the bottom contains shortcut buttons for operations I tend to use most often; if my preferences don't match yours, simply change the toolbar list in the source code to show the buttons you want (this is Python, after all). As usual for Tkinter menus, shortcut key combinations can be used to invoke menu options quickly, too -- press Alt plus all the underlined keys of entries along the path to the desired action.

PyEdit pops up a variety of modal and nonmodal dialogs, both standard and custom. Figure 9-11 shows the custom and nonmodal change dialog, along with a standard dialog used to display file statistics.

Figure 9-11. PyEdit with colors, font, and a few pop-ups

figs/ppy2_0911.gif

The main window here has been given new foreground and background colors (with the standard color selection dialog), and a new text font has been selected from a canned list in the script that users can change to suit their preferences (this is Python, after all). The standard file open and save selection dialogs in PyEdit use object-based interfaces to remember the last directory visited, so you don't have to renavigate there every time.

One of the more unique features of PyEdit is that it can actually run Python program code that you are editing. This isn't as hard as it may sound either -- because Python provides built-ins for both compiling and running code strings and launching programs, PyEdit simply has to make the right calls for this to work. For example, it's easy to code a simple-minded Python interpreter in Python (though you need a bit more to handle multiple-line statements), as shown in Example 9-13.

Example 9-13. PP2EGuiTextEditorsimpleshell.py

namespace= {}
while 1:
 try:
 line = raw_input('>>> ') # single line statements only
 except EOFError:
 break
 else:
 exec line in namespace # or eval( ) and print result

Depending on the user's preference, PyEdit either does something similar to this to run code fetched from the text widget, or uses the launchmodes module we wrote at the end of Chapter 3 to run the code's file as an independent program. There are a variety of options in both schemes that you can customize as you like (this is Python, after all). See the onRunCode method for details, or simply edit and run some Python code on your own.

Figure 9-12 shows four independently started instances of PyEdit running with a variety of color schemes, sizes, and fonts. This figure also captures two PyEdit torn-off menus (lower right) and the PyEdit help pop-up (upper right). The edit windows' backgrounds are shades of yellow, blue, purple, and orange; use the Tools menu's Pick options to set colors as you like.

Figure 9-12. Multiple PyEdit sessions at work

figs/ppy2_0912.gif

Since these four PyEdit sessions are all editing Python source-coded text, you can run their contents with the Run Code option in the Tools pull-down menu. Code run from files is spawned independently; the standard streams of code run not from a file (i.e., fetched from the text widget itself) are mapped to the PyEdit session's console window. This isn't an IDE by any means; it's just something I added because I found it to be useful. It's nice to run code you're editing without fishing through directories.

One caveat before I turn you loose on the source code: PyEdit does not yet have an Undo button in this release. I don't use such a mode myself, and it's easy to undo cuts and pastes right after you've done them (simply paste back from the clipboard, or cut the pasted and selected text). Adding a general undo option would make for a fun exercise if you are so motivated. An interesting approach may be to subclass either the TextEditor class here or the Tkinter Text class itself. Such a subclass would record text operations on a limited-length list and run calls to back out of each logged operation on demand and in reverse. It could also be used to make PyEdit smarter about knowing when to ask about saving the file before it exits. By adding undo as an external subclass, exiting PyEdit code would not have to be instrumented to keep track of everything it does to the text. This is Python, after all.

9.4.2 PyEdit Source Code

The program in Example 9-14 consists of just two source files -- a .pyw that can be run on Windows to avoid the DOS console streams window pop-up, and a main .py that can be either run or imported. We need both because PyEdit is both script and library, and .pyw files can only be run, not imported (see Chapter 2 if you've forgotten what that implies).

Example 9-14. PP2EGuiTextEditor extEditor.pyw

#################################################################
# run PyEdit without DOS console popup for os.system on Windows;
# at present, ".pyw" files cannot be imported as modules;
# if you want a file to be both a program that launches without
# a dos console box on windows, and be imported from elsewhere,
# use ".py" for the main file and import .py code from a ".pyw";
# execfile('textEditor.py') fails when run from another dir,
# because the current working dir is the dir I'm run from;
#################################################################

import textEditor # grab .py (or .pyc) file
textEditor.main( ) # run top-level entry point

The module in Example 9-15 is PyEdit's implementation; the main classes used to start and embed a PyEdit object appear at the end of this file. Study this listing while you experiment with PyEdit, to learn about its features and techniques.

Example 9-15. PP2EGuiTextEditor extEditor.py

################################################################################
# PyEdit 1.1: a Python/Tkinter text file editor and component.
# Uses the Tk text widget, plus GuiMaker menus and toolbar buttons
# to implement a full-featured text editor that can be run as a 
# stand-alone program, and attached as a component to other GUIs.
# Also used by PyMail and PyView to edit mail and image file notes.
################################################################################

Version = '1.1'
from Tkinter import * # base widgets, constants
from tkFileDialog import * # standard dialogs
from tkMessageBox import *
from tkSimpleDialog import *
from tkColorChooser import askcolor
from string import split, atoi
from PP2E.Gui.Tools.guimaker import * # Frame + menu/toolbar builders

START = '1.0' # index of first char: row=1,col=0
SEL_FIRST = SEL + '.first' # map sel tag to index
SEL_LAST = SEL + '.last' # same as 'sel.last'

import sys, os, string
FontScale = 0 # use bigger font on linux
if sys.platform[:3] != 'win': # and other non-windows boxes
 FontScale = 3

class TextEditor: # mix with menu/toolbar Frame class
 startfiledir = '.'
 ftypes = [('All files', '*'), # for file open dialog
 ('Text files', '.txt'), # customize in subclass
 ('Python files', '.py')] # or set in each instance

 colors = [{'fg':'black', 'bg':'white'}, # color pick list
 {'fg':'yellow', 'bg':'black'}, # first item is default
 {'fg':'white', 'bg':'blue'}, # tailor me as desired
 {'fg':'black', 'bg':'beige'}, # or do PickBg/Fg chooser
 {'fg':'yellow', 'bg':'purple'},
 {'fg':'black', 'bg':'brown'},
 {'fg':'lightgreen', 'bg':'darkgreen'},
 {'fg':'darkblue', 'bg':'orange'},
 {'fg':'orange', 'bg':'darkblue'}]

 fonts = [('courier', 9+FontScale, 'normal'), # platform-neutral fonts
 ('courier', 12+FontScale, 'normal'), # (family, size, style)
 ('courier', 10+FontScale, 'bold'), # or popup a listbox
 ('courier', 10+FontScale, 'italic'), # make bigger on linux
 ('times', 10+FontScale, 'normal'),
 ('helvetica', 10+FontScale, 'normal'),
 ('ariel', 10+FontScale, 'normal'),
 ('system', 10+FontScale, 'normal'),
 ('courier', 20+FontScale, 'normal')]

 def __init__(self, loadFirst=''):
 if not isinstance(self, GuiMaker):
 raise TypeError, 'TextEditor needs a GuiMaker mixin'
 self.setFileName(None)
 self.lastfind = None
 self.openDialog = None
 self.saveDialog = None
 self.text.focus( ) # else must click in text
 if loadFirst: 
 self.onOpen(loadFirst)
 
 def start(self): # run by GuiMaker.__init__
 self.menuBar = [ # configure menu/toolbar
 ('File', 0, 
 [('Open...', 0, self.onOpen),
 ('Save', 0, self.onSave),
 ('Save As...', 5, self.onSaveAs),
 ('New', 0, self.onNew),
 'separator',
 ('Quit...', 0, self.onQuit)]
 ),
 ('Edit', 0,
 [('Cut', 0, self.onCut),
 ('Copy', 1, self.onCopy),
 ('Paste', 0, self.onPaste),
 'separator',
 ('Delete', 0, self.onDelete),
 ('Select All', 0, self.onSelectAll)]
 ),
 ('Search', 0,
 [('Goto...', 0, self.onGoto),
 ('Find...', 0, self.onFind),
 ('Refind', 0, self.onRefind),
 ('Change...', 0, self.onChange)]
 ),
 ('Tools', 0,
 [('Font List', 0, self.onFontList),
 ('Pick Bg...', 4, self.onPickBg),
 ('Pick Fg...', 0, self.onPickFg),
 ('Color List', 0, self.onColorList),
 'separator',
 ('Info...', 0, self.onInfo),
 ('Clone', 1, self.onClone),
 ('Run Code', 0, self.onRunCode)]
 )]
 self.toolBar = [
 ('Save', self.onSave, {'side': LEFT}),
 ('Cut', self.onCut, {'side': LEFT}),
 ('Copy', self.onCopy, {'side': LEFT}),
 ('Paste', self.onPaste, {'side': LEFT}),
 ('Find', self.onRefind, {'side': LEFT}),
 ('Help', self.help, {'side': RIGHT}),
 ('Quit', self.onQuit, {'side': RIGHT})]

 def makeWidgets(self): # run by GuiMaker.__init__
 name = Label(self, bg='black', fg='white') # add below menu, above tool
 name.pack(side=TOP, fill=X) # menu/toolbars are packed

 vbar = Scrollbar(self) 
 hbar = Scrollbar(self, orient='horizontal')
 text = Text(self, padx=5, wrap='none') 

 vbar.pack(side=RIGHT, fill=Y)
 hbar.pack(side=BOTTOM, fill=X) # pack text last
 text.pack(side=TOP, fill=BOTH, expand=YES) # else sbars clipped

 text.config(yscrollcommand=vbar.set) # call vbar.set on text move
 text.config(xscrollcommand=hbar.set)
 vbar.config(command=text.yview) # call text.yview on scroll move
 hbar.config(command=text.xview) # or hbar['command']=text.xview

 text.config(font=self.fonts[0], 
 bg=self.colors[0]['bg'], fg=self.colors[0]['fg'])
 self.text = text
 self.filelabel = name

 #####################
 # Edit menu commands
 #####################

 def onCopy(self): # get text selected by mouse,etc
 if not self.text.tag_ranges(SEL): # save in cross-app clipboard
 showerror('PyEdit', 'No text selected')
 else:
 text = self.text.get(SEL_FIRST, SEL_LAST) 
 self.clipboard_clear( ) 
 self.clipboard_append(text)

 def onDelete(self): # delete selected text, no save
 if not self.text.tag_ranges(SEL):
 showerror('PyEdit', 'No text selected')
 else:
 self.text.delete(SEL_FIRST, SEL_LAST)

 def onCut(self):
 if not self.text.tag_ranges(SEL):
 showerror('PyEdit', 'No text selected')
 else: 
 self.onCopy( ) # save and delete selected text
 self.onDelete( )

 def onPaste(self):
 try:
 text = self.selection_get(selection='CLIPBOARD')
 except TclError:
 showerror('PyEdit', 'Nothing to paste')
 return
 self.text.insert(INSERT, text) # add at current insert cursor
 self.text.tag_remove(SEL, '1.0', END) 
 self.text.tag_add(SEL, INSERT+'-%dc' % len(text), INSERT)
 self.text.see(INSERT) # select it, so it can be cut

 def onSelectAll(self):
 self.text.tag_add(SEL, '1.0', END+'-1c') # select entire text 
 self.text.mark_set(INSERT, '1.0') # move insert point to top
 self.text.see(INSERT) # scroll to top

 ######################
 # Tools menu commands 
 ######################

 def onFontList(self):
 self.fonts.append(self.fonts[0]) # pick next font in list
 del self.fonts[0] # resizes the text area
 self.text.config(font=self.fonts[0]) 

 def onColorList(self):
 self.colors.append(self.colors[0]) # pick next color in list
 del self.colors[0] # move current to end
 self.text.config(fg=self.colors[0]['fg'], bg=self.colors[0]['bg']) 

 def onPickFg(self): 
 self.pickColor('fg') # added on 10/02/00
 def onPickBg(self): # select arbitrary color
 self.pickColor('bg') # in standard color dialog
 def pickColor(self, part): # this is way too easy
 (triple, hexstr) = askcolor( )
 if hexstr:
 apply(self.text.config, ( ), {part: hexstr})

 def onInfo(self):
 text = self.getAllText( ) # added on 5/3/00 in 15 mins
 bytes = len(text) # words uses a simple guess: 
 lines = len(string.split(text, '
')) # any separated by whitespace
 words = len(string.split(text)) 
 index = self.text.index(INSERT)
 where = tuple(string.split(index, '.'))
 showinfo('PyEdit Information',
 'Current location:

' +
 'line:	%s
column:	%s

' % where +
 'File text statistics:

' +
 'bytes:	%d
lines:	%d
words:	%d
' % (bytes, lines, words))

 def onClone(self):
 new = Toplevel( ) # a new edit window in same process
 myclass = self.__class__ # instance's (lowest) class object
 myclass(new) # attach/run instance of my class

 def onRunCode(self, parallelmode=1):
 """
 run Python code being edited--not an ide, but handy;
 tries to run in file's dir, not cwd (may be pp2e root);
 inputs and adds command-line arguments for script files;
 code's stdin/out/err = editor's start window, if any;
 but parallelmode uses start to open a dos box for i/o;
 """
 from PP2E.launchmodes import System, Start, Fork
 filemode = 0
 thefile = str(self.getFileName( ))
 cmdargs = askstring('PyEdit', 'Commandline arguments?') or ''
 if os.path.exists(thefile):
 filemode = askyesno('PyEdit', 'Run from file?')
 if not filemode: # run text string
 namespace = {'__name__': '__main__'} # run as top-level
 sys.argv = [thefile] + string.split(cmdargs) # could use threads
 exec self.getAllText( ) + '
' in namespace # exceptions ignored
 elif askyesno('PyEdit', 'Text saved in file?'):
 mycwd = os.getcwd( ) # cwd may be root
 os.chdir(os.path.dirname(thefile) or mycwd) # cd for filenames
 thecmd = thefile + ' ' + cmdargs
 if not parallelmode: # run as file
 System(thecmd, thecmd)( ) # block editor
 else:
 if sys.platform[:3] == 'win': # spawn in parallel
 Start(thecmd, thecmd)( ) # or use os.spawnv
 else:
 Fork(thecmd, thecmd)( ) # spawn in parallel
 os.chdir(mycwd)

 #######################
 # Search menu commands
 #######################
 
 def onGoto(self):
 line = askinteger('PyEdit', 'Enter line number')
 self.text.update( ) 
 self.text.focus( )
 if line is not None:
 maxindex = self.text.index(END+'-1c')
 maxline = atoi(split(maxindex, '.')[0])
 if line > 0 and line <= maxline:
 self.text.mark_set(INSERT, '%d.0' % line) # goto line
 self.text.tag_remove(SEL, '1.0', END) # delete selects
 self.text.tag_add(SEL, INSERT, 'insert + 1l') # select line
 self.text.see(INSERT) # scroll to line
 else:
 showerror('PyEdit', 'Bad line number')

 def onFind(self, lastkey=None):
 key = lastkey or askstring('PyEdit', 'Enter search string')
 self.text.update( )
 self.text.focus( )
 self.lastfind = key
 if key:
 where = self.text.search(key, INSERT, END) # don't wrap
 if not where:
 showerror('PyEdit', 'String not found')
 else:
 pastkey = where + '+%dc' % len(key) # index past key
 self.text.tag_remove(SEL, '1.0', END) # remove any sel
 self.text.tag_add(SEL, where, pastkey) # select key 
 self.text.mark_set(INSERT, pastkey) # for next find
 self.text.see(where) # scroll display

 def onRefind(self):
 self.onFind(self.lastfind)

 def onChange(self):
 new = Toplevel(self)
 Label(new, text='Find text:').grid(row=0, column=0)
 Label(new, text='Change to:').grid(row=1, column=0)
 self.change1 = Entry(new)
 self.change2 = Entry(new)
 self.change1.grid(row=0, column=1, sticky=EW)
 self.change2.grid(row=1, column=1, sticky=EW)
 Button(new, text='Find', 
 command=self.onDoFind).grid(row=0, column=2, sticky=EW)
 Button(new, text='Apply', 
 command=self.onDoChange).grid(row=1, column=2, sticky=EW)
 new.columnconfigure(1, weight=1) # expandable entrys

 def onDoFind(self):
 self.onFind(self.change1.get( )) # Find in change box

 def onDoChange(self):
 if self.text.tag_ranges(SEL): # must find first
 self.text.delete(SEL_FIRST, SEL_LAST) # Apply in change
 self.text.insert(INSERT, self.change2.get( )) # deletes if empty
 self.text.see(INSERT)
 self.onFind(self.change1.get( )) # goto next appear
 self.text.update( ) # force refresh

 #####################
 # File menu commands
 #####################

 def my_askopenfilename(self): # objects remember last result dir/file
 if not self.openDialog:
 self.openDialog = Open(initialdir=self.startfiledir, 
 filetypes=self.ftypes)
 return self.openDialog.show( )

 def my_asksaveasfilename(self): # objects remember last result dir/file
 if not self.saveDialog:
 self.saveDialog = SaveAs(initialdir=self.startfiledir, 
 filetypes=self.ftypes)
 return self.saveDialog.show( )
 
 def onOpen(self, loadFirst=''):
 doit = self.isEmpty( ) or askyesno('PyEdit', 'Disgard text?')
 if doit:
 file = loadFirst or self.my_askopenfilename( )
 if file:
 try:
 text = open(file, 'r').read( )
 except:
 showerror('PyEdit', 'Could not open file ' + file)
 else:
 self.setAllText(text)
 self.setFileName(file)

 def onSave(self):
 self.onSaveAs(self.currfile) # may be None

 def onSaveAs(self, forcefile=None):
 file = forcefile or self.my_asksaveasfilename( )
 if file:
 text = self.getAllText( )
 try:
 open(file, 'w').write(text)
 except:
 showerror('PyEdit', 'Could not write file ' + file)
 else:
 self.setFileName(file) # may be newly created

 def onNew(self):
 doit = self.isEmpty( ) or askyesno('PyEdit', 'Disgard text?')
 if doit:
 self.setFileName(None)
 self.clearAllText( )

 def onQuit(self):
 if askyesno('PyEdit', 'Really quit PyEdit?'):
 self.quit( ) # Frame.quit via GuiMaker

 ####################################
 # Others, useful outside this class
 ####################################

 def isEmpty(self):
 return not self.getAllText( ) 

 def getAllText(self):
 return self.text.get('1.0', END+'-1c') # extract text as a string

 def setAllText(self, text):
 self.text.delete('1.0', END) # store text string in widget
 self.text.insert(END, text) # or '1.0'
 self.text.mark_set(INSERT, '1.0') # move insert point to top 
 self.text.see(INSERT) # scroll to top, insert set

 def clearAllText(self):
 self.text.delete('1.0', END) # clear text in widget 

 def getFileName(self):
 return self.currfile

 def setFileName(self, name):
 self.currfile = name # for save
 self.filelabel.config(text=str(name))

 def help(self):
 showinfo('About PyEdit', 
 'PyEdit version %s
October, 2000

'
 'A text editor program
and object component
'
 'written in Python/Tk.
Programming Python 2E
'
 "O'Reilly & Associates" % Version)


##################################################################
# ready-to-use editor classes 
# mix in a Frame subclass that builds menu/toolbars
##################################################################


# when editor owns the window 

class TextEditorMain(TextEditor, GuiMakerWindowMenu): # add menu/toolbar maker 
 def __init__(self, parent=None, loadFirst=''): # when fills whole window
 GuiMaker.__init__(self, parent) # use main window menus
 TextEditor.__init__(self, loadFirst) # self has GuiMaker frame
 self.master.title('PyEdit ' + Version) # title if stand-alone
 self.master.iconname('PyEdit') # catch wm delete button
 self.master.protocol('WM_DELETE_WINDOW', self.onQuit)

class TextEditorMainPopup(TextEditor, GuiMakerWindowMenu):
 def __init__(self, parent=None, loadFirst=''): 
 self.popup = Toplevel(parent) # create own window
 GuiMaker.__init__(self, self.popup) # use main window menus
 TextEditor.__init__(self, loadFirst) 
 assert self.master == self.popup
 self.popup.title('PyEdit ' + Version) 
 self.popup.iconname('PyEdit') 
 def quit(self):
 self.popup.destroy( ) # kill this window only


# when embedded in another window

class TextEditorComponent(TextEditor, GuiMakerFrameMenu): 
 def __init__(self, parent=None, loadFirst=''): # use Frame-based menus
 GuiMaker.__init__(self, parent) # all menus, buttons on
 TextEditor.__init__(self, loadFirst) # GuiMaker must init 1st

class TextEditorComponentMinimal(TextEditor, GuiMakerFrameMenu): 
 def __init__(self, parent=None, loadFirst='', deleteFile=1): 
 self.deleteFile = deleteFile
 GuiMaker.__init__(self, parent) 
 TextEditor.__init__(self, loadFirst) 
 def start(self): 
 TextEditor.start(self) # GuiMaker start call
 for i in range(len(self.toolBar)): # delete quit in toolbar
 if self.toolBar[i][0] == 'Quit': # delete file menu items
 del self.toolBar[i]; break # or just disable file
 if self.deleteFile:
 for i in range(len(self.menuBar)):
 if self.menuBar[i][0] == 'File':
 del self.menuBar[i]; break
 else:
 for (name, key, items) in self.menuBar:
 if name == 'File':
 items.append([1,2,3,4,6]) 


# stand-alone program run
 
def testPopup( ): 
 # see PyView and PyMail for component tests
 root = Tk( )
 TextEditorMainPopup(root)
 TextEditorMainPopup(root)
 Button(root, text='More', command=TextEditorMainPopup).pack(fill=X)
 Button(root, text='Quit', command=root.quit).pack(fill=X)
 root.mainloop( )

def main( ): # may be typed or clicked
 try: # or associated on Windows
 fname = sys.argv[1] # arg = optional filename
 except IndexError:
 fname = None
 TextEditorMain(loadFirst=fname).pack(expand=YES, fill=BOTH)
 mainloop( )

if __name__ == '__main__': # when run as a script
 #testPopup( )
 main( ) # run .pyw for no dos box

Introducing Python

Part I: System Interfaces

System Tools

Parallel System Tools

Larger System Examples I

Larger System Examples II

Part II: GUI Programming

Graphical User Interfaces

A Tkinter Tour, Part 1

A Tkinter Tour, Part 2

Larger GUI Examples

Part III: Internet Scripting

Network Scripting

Client-Side Scripting

Server-Side Scripting

Larger Web Site Examples I

Larger Web Site Examples II

Advanced Internet Topics

Part IV: Assorted Topics

Databases and Persistence

Data Structures

Text and Language

Part V: Integration

Extending Python

Embedding Python

VI: The End

Conclusion Python and the Development Cycle

show all menu



Programming Python
Python Programming for the Absolute Beginner, 3rd Edition
ISBN: 1435455002
EAN: 2147483647
Year: 2000
Pages: 245
Similar book on Amazon

Flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net