diff options
Diffstat (limited to 'Demo/tkinter')
-rwxr-xr-x | Demo/tkinter/guido/MimeViewer.py | 143 | ||||
-rwxr-xr-x | Demo/tkinter/guido/dialog.py | 119 | ||||
-rwxr-xr-x | Demo/tkinter/guido/mbox.py | 288 | ||||
-rwxr-xr-x | Demo/tkinter/guido/rmt.py | 152 | ||||
-rwxr-xr-x | Demo/tkinter/guido/tst.py | 81 | ||||
-rwxr-xr-x | Demo/tkinter/guido/wish.py | 26 |
6 files changed, 809 insertions, 0 deletions
diff --git a/Demo/tkinter/guido/MimeViewer.py b/Demo/tkinter/guido/MimeViewer.py new file mode 100755 index 0000000..5bf194a --- /dev/null +++ b/Demo/tkinter/guido/MimeViewer.py @@ -0,0 +1,143 @@ +#! /ufs/guido/bin/sgi/tkpython + +# View a single MIME multipart message. +# Display each part as a box. + +import string +from types import * +from Tkinter import * +from ScrolledText import ScrolledText + +class MimeViewer: + def __init__(self, parent, title, msg): + self.title = title + self.msg = msg + self.frame = Frame(parent, {'relief': 'raised', 'bd': 2}) + self.frame.packing = {'expand': 0, 'fill': 'both'} + self.button = Checkbutton(self.frame, + {'text': title, + 'command': self.toggle}) + self.button.pack({'anchor': 'w'}) + headertext = msg.getheadertext( + lambda x: x != 'received' and x[:5] != 'x400-') + height = countlines(headertext, 4) + if height: + self.htext = ScrolledText(self.frame, + {'height': height, + 'width': 80, + 'wrap': 'none', + 'relief': 'raised', + 'bd': 2}) + self.htext.packing = {'expand': 1, 'fill': 'both', + 'after': self.button} + self.htext.insert('end', headertext) + else: + self.htext = Frame(self.frame, + {'relief': 'raised', 'bd': 2}) + self.htext.packing = {'side': 'top', + 'ipady': 2, + 'fill': 'x', + 'after': self.button} + body = msg.getbody() + if type(body) == StringType: + self.pad = None + height = countlines(body, 10) + if height: + self.btext = ScrolledText(self.frame, + {'height': height, + 'width': 80, + 'wrap': 'none', + 'relief': 'raised', + 'bd': 2}) + self.btext.packing = {'expand': 1, + 'fill': 'both'} + self.btext.insert('end', body) + else: + self.btext = None + self.parts = None + else: + self.pad = Frame(self.frame, + {'relief': 'flat', 'bd': 2}) + self.pad.packing = {'side': 'left', 'ipadx': 10, + 'fill': 'y', 'after': self.htext} + self.parts = [] + for i in range(len(body)): + p = MimeViewer(self.frame, + '%s.%d' % (title, i+1), + body[i]) + self.parts.append(p) + self.btext = None + self.collapsed = 1 + def pack(self): + self.frame.pack(self.frame.packing) + def destroy(self): + self.frame.destroy() + def show(self): + if self.collapsed: + self.button.invoke() + def toggle(self): + if self.collapsed: + self.explode() + else: + self.collapse() + def collapse(self): + self.collapsed = 1 + for comp in self.htext, self.btext, self.pad: + if comp: + comp.forget() + if self.parts: + for part in self.parts: + part.frame.forget() + self.frame.pack({'expand': 0}) + def explode(self): + self.collapsed = 0 + for comp in self.htext, self.btext, self.pad: + if comp: comp.pack(comp.packing) + if self.parts: + for part in self.parts: + part.pack() + self.frame.pack({'expand': 1}) + +def countlines(str, limit): + i = 0 + n = 0 + while n < limit: + i = string.find(str, '\n', i) + if i < 0: break + n = n+1 + i = i+1 + return n + +def main(): + import sys + import getopt + import mhlib + opts, args = getopt.getopt(sys.argv[1:], '') + for o, a in opts: + pass + message = None + folder = 'inbox' + for arg in args: + if arg[:1] == '+': + folder = arg[1:] + else: + message = string.atoi(arg) + + mh = mhlib.MH() + f = mh.openfolder(folder) + if not message: + message = f.getcurrent() + m = f.openmessage(message) + + root = Tk() + tk = root.tk + + top = MimeViewer(root, '+%s/%d' % (folder, message), m) + top.pack() + top.show() + + root.minsize(1, 1) + + tk.mainloop() + +if __name__ == '__main__': main() diff --git a/Demo/tkinter/guido/dialog.py b/Demo/tkinter/guido/dialog.py new file mode 100755 index 0000000..31f5340 --- /dev/null +++ b/Demo/tkinter/guido/dialog.py @@ -0,0 +1,119 @@ +#! /ufs/guido/bin/sgi/tkpython + +# A Python function that generates dialog boxes with a text message, +# optional bitmap, and any number of buttons. +# Cf. Ousterhout, Tcl and the Tk Toolkit, Figs. 27.2-3, pp. 269-270. + +from Tkinter import * + +def dialog(master, title, text, bitmap, default, *args): + + # 1. Create the top-level window and divide it into top + # and bottom parts. + + w = Toplevel(master, {'class': 'Dialog'}) + w.tk.call('global', 'button') + w.title(title) + w.iconname('Dialog') + + top = Frame(w, {'relief': 'raised', 'bd': 1, + Pack: {'side': 'top', 'fill': 'both'}}) + bot = Frame(w, {'relief': 'raised', 'bd': 1, + Pack: {'side': 'bottom', 'fill': 'both'}}) + + # 2. Fill the top part with the bitmap and message. + + msg = Message(top, + {'width': '3i', + 'text': text, + 'font': '-Adobe-Times-Medium-R-Normal-*-180-*', + Pack: {'side': 'right', 'expand': 1, + 'fill': 'both', + 'padx': '3m', 'pady': '3m'}}) + if bitmap: + bm = Label(top, {'bitmap': bitmap, + Pack: {'side': 'left', + 'padx': '3m', 'pady': '3m'}}) + + # 3. Create a row of buttons at the bottom of the dialog. + + buttons = [] + i = 0 + for but in args: + b = Button(bot, {'text': but, + 'command': ('set', 'button', i)}) + buttons.append(b) + if i == default: + bd = Frame(bot, {'relief': 'sunken', 'bd': 1, + Pack: {'side': 'left', 'expand': 1, + 'padx': '3m', 'pady': '2m'}}) + w.tk.call('raise', b) + b.pack ({'in': bd, 'side': 'left', + 'padx': '2m', 'pady': '2m', + 'ipadx': '2m', 'ipady': '1m'}) + else: + b.pack ({'side': 'left', 'expand': 1, + 'padx': '3m', 'pady': '3m', + 'ipady': '2m', 'ipady': '1m'}) + i = i+1 + + # 4. Set up a binding for <Return>, if there's a default, + # set a grab, and claim the focus too. + + if default >= 0: + w.bind('<Return>', + lambda b=buttons[default], i=default: + (b.cmd('flash'), + b.tk.call('set', 'button', i))) + + oldFocus = w.tk.call('focus') + w.tk.call('grab', 'set', w) + w.tk.call('focus', w) + + # 5. Wait for the user to respond, then restore the focus + # and return the index of the selected button. + + w.tk.call('tkwait', 'variable', 'button') + w.tk.call('destroy', w) + w.tk.call('focus', oldFocus) + return w.tk.call('set', 'button') + +# The rest is the test program. + +def go(): + i = dialog(mainWidget, + 'Not Responding', + "The file server isn't responding right now; " + "I'll keep trying.", + '', + -1, + 'OK') + print 'pressed button', i + i = dialog(mainWidget, + 'File Modified', + 'File "tcl.h" has been modified since ' + 'the last time it was saved. ' + 'Do you want to save it before exiting the application?', + 'warning', + 0, + 'Save File', + 'Discard Changes', + 'Return To Editor') + print 'pressed button', i + +def test(): + import sys + global mainWidget + mainWidget = Frame() + Pack.config(mainWidget) + start = Button(mainWidget, + {'text': 'Press Here To Start', 'command': go}) + start.pack() + endit = Button(mainWidget, + {'text': 'Exit', + 'command': 'exit', + Pack: {'fill' : 'both'}}) + mainWidget.tk.mainloop() + +if __name__ == '__main__': + test() diff --git a/Demo/tkinter/guido/mbox.py b/Demo/tkinter/guido/mbox.py new file mode 100755 index 0000000..a4e86da --- /dev/null +++ b/Demo/tkinter/guido/mbox.py @@ -0,0 +1,288 @@ +#! /ufs/guido/bin/sgi/tkpython + +# Scan MH folder, display results in window + +import os +import sys +import regex +import getopt +import string +import mhlib + +from Tkinter import * + +from dialog import dialog + +mailbox = os.environ['HOME'] + '/Mail' + +def main(): + global root, tk, top, mid, bot + global folderbox, foldermenu, scanbox, scanmenu, viewer + global folder, seq + global mh, mhf + + # Parse command line options + + folder = 'inbox' + seq = 'all' + try: + opts, args = getopt.getopt(sys.argv[1:], '') + except getopt.error, msg: + print msg + sys.exit(2) + for arg in args: + if arg[:1] == '+': + folder = arg[1:] + else: + seq = arg + + # Initialize MH + + mh = mhlib.MH() + mhf = mh.openfolder(folder) + + # Build widget hierarchy + + root = Tk() + tk = root.tk + + top = Frame(root) + top.pack({'expand': 1, 'fill': 'both'}) + + # Build right part: folder list + + right = Frame(top) + right.pack({'fill': 'y', 'side': 'right'}) + + folderbar = Scrollbar(right, {'relief': 'sunken', 'bd': 2}) + folderbar.pack({'fill': 'y', 'side': 'right'}) + + folderbox = Listbox(right) + folderbox.pack({'expand': 1, 'fill': 'both', 'side': 'left'}) + + foldermenu = Menu(root) + foldermenu.add('command', + {'label': 'Open Folder', + 'command': open_folder}) + foldermenu.add('separator') + foldermenu.add('command', + {'label': 'Quit', + 'command': 'exit'}) + foldermenu.bind('<ButtonRelease-3>', folder_unpost) + + folderbox['yscrollcommand'] = (folderbar, 'set') + folderbar['command'] = (folderbox, 'yview') + folderbox.bind('<Double-1>', open_folder, 1) + folderbox.bind('<3>', folder_post) + + # Build left part: scan list + + left = Frame(top) + left.pack({'expand': 1, 'fill': 'both', 'side': 'left'}) + + scanbar = Scrollbar(left, {'relief': 'sunken', 'bd': 2}) + scanbar.pack({'fill': 'y', 'side': 'right'}) + + scanbox = Listbox(left, {'font': 'fixed'}) + scanbox.pack({'expand': 1, 'fill': 'both', 'side': 'left'}) + + scanmenu = Menu(root) + scanmenu.add('command', + {'label': 'Open Message', + 'command': open_message}) + scanmenu.add('command', + {'label': 'Remove Message', + 'command': remove_message}) + scanmenu.add('command', + {'label': 'Refile Message', + 'command': refile_message}) + scanmenu.add('separator') + scanmenu.add('command', + {'label': 'Quit', + 'command': 'exit'}) + scanmenu.bind('<ButtonRelease-3>', scan_unpost) + + scanbox['yscrollcommand'] = (scanbar, 'set') + scanbar['command'] = (scanbox, 'yview') + scanbox.bind('<Double-1>', open_message) + scanbox.bind('<3>', scan_post) + + # Separator between middle and bottom part + + rule2 = Frame(root, {'bg': 'black'}) + rule2.pack({'fill': 'x'}) + + # Build bottom part: current message + + bot = Frame(root) + bot.pack({'expand': 1, 'fill': 'both'}) + # + viewer = None + + # Window manager commands + + root.minsize(800, 1) # Make window resizable + + # Fill folderbox with text + + setfolders() + + # Fill scanbox with text + + rescan() + + # Enter mainloop + + root.mainloop() + +def folder_post(e): + x, y = e.x_root, e.y_root + foldermenu.post(x - 10, y - 10) + foldermenu.grab_set() + +def folder_unpost(e): + tk.call('update', 'idletasks') + foldermenu.grab_release() + foldermenu.unpost() + foldermenu.invoke('active') + +def scan_post(e): + x, y = e.x_root, e.y_root + scanmenu.post(x - 10, y - 10) + scanmenu.grab_set() + +def scan_unpost(e): + tk.call('update', 'idletasks') + scanmenu.grab_release() + scanmenu.unpost() + scanmenu.invoke('active') + +scanparser = regex.compile('^ *\([0-9]+\)') + +def open_folder(*e): + global folder, mhf + sel = folderbox.curselection() + if len(sel) != 1: + if len(sel) > 1: + msg = "Please open one folder at a time" + else: + msg = "Please select a folder to open" + dialog(root, "Can't Open Folder", msg, "", 0, "OK") + return + i = sel[0] + folder = folderbox.get(i) + mhf = mh.openfolder(folder) + rescan() + +def open_message(*e): + global viewer + sel = scanbox.curselection() + if len(sel) != 1: + if len(sel) > 1: + msg = "Please open one message at a time" + else: + msg = "Please select a message to open" + dialog(root, "Can't Open Message", msg, "", 0, "OK") + return + cursor = scanbox['cursor'] + scanbox['cursor'] = 'watch' + tk.call('update', 'idletasks') + i = sel[0] + line = scanbox.get(i) + if scanparser.match(line) >= 0: + num = string.atoi(scanparser.group(1)) + m = mhf.openmessage(num) + if viewer: viewer.destroy() + from MimeViewer import MimeViewer + viewer = MimeViewer(bot, '+%s/%d' % (folder, num), m) + viewer.pack() + viewer.show() + scanbox['cursor'] = cursor + +def interestingheader(header): + return header != 'received' + +def remove_message(): + itop = scanbox.nearest(0) + sel = scanbox.curselection() + if not sel: + dialog(root, "No Message To Remove", + "Please select a message to remove", "", 0, "OK") + return + todo = [] + for i in sel: + line = scanbox.get(i) + if scanparser.match(line) >= 0: + todo.append(string.atoi(scanparser.group(1))) + mhf.removemessages(todo) + rescan() + fixfocus(min(todo), itop) + +lastrefile = '' +tofolder = None +def refile_message(): + global lastrefile, tofolder + itop = scanbox.nearest(0) + sel = scanbox.curselection() + if not sel: + dialog(root, "No Message To Refile", + "Please select a message to refile", "", 0, "OK") + return + foldersel = folderbox.curselection() + if len(foldersel) != 1: + if not foldersel: + msg = "Please select a folder to refile to" + else: + msg = "Please select exactly one folder to refile to" + dialog(root, "No Folder To Refile", msg, "", 0, "OK") + return + refileto = folderbox.get(foldersel[0]) + todo = [] + for i in sel: + line = scanbox.get(i) + if scanparser.match(line) >= 0: + todo.append(string.atoi(scanparser.group(1))) + print 'refile', todo, tofolder + if lastrefile != refileto or not tofolder: + print 'new folder' + lastrefile = refileto + tofolder = None + tofolder = mh.openfolder(lastrefile) + mhf.refilemessages(todo, tofolder) + rescan() + fixfocus(min(todo), itop) + +def fixfocus(near, itop): + n = scanbox.size() + for i in range(n): + line = scanbox.get(`i`) + if scanparser.match(line) >= 0: + num = string.atoi(scanparser.group(1)) + if num >= near: + break + else: + i = 'end' + scanbox.select_from(i) + print 'yview', `itop` + scanbox.yview(itop) + +def setfolders(): + folderbox.delete(0, 'end') + for fn in mh.listallfolders(): + folderbox.insert('end', fn) + +def rescan(): + global viewer + if viewer: + viewer.destroy() + viewer = None + scanbox.delete(0, 'end') + for line in scanfolder(folder, seq): + scanbox.insert('end', line) + +def scanfolder(folder = 'inbox', sequence = 'all'): + return map( + lambda line: line[:-1], + os.popen('scan +%s %s' % (folder, sequence), 'r').readlines()) + +main() diff --git a/Demo/tkinter/guido/rmt.py b/Demo/tkinter/guido/rmt.py new file mode 100755 index 0000000..2ac2408 --- /dev/null +++ b/Demo/tkinter/guido/rmt.py @@ -0,0 +1,152 @@ +#! /ufs/guido/bin/sgi/tkpython + +# A Python program implementing rmt, an application for remotely +# controlling other Tk applications. +# Cf. Ousterhout, Tcl and the Tk Toolkit, Figs. 27.5-8, pp. 273-276. + +# Note that because of forward references in the original, we +# sometimes delay bindings until after the corresponding procedure is +# defined. We also introduce names for some unnamed code blocks in +# the original because of restrictions on lambda forms in Python. + +from Tkinter import * + +# 1. Create basic application structure: menu bar on top of +# text widget, scrollbar on right. + +root = Tk() +tk = root.tk +mBar = Frame(root, {'relief': 'raised', 'bd': 2, + Pack: {'side': 'top', 'fill': 'x'}}) +f = Frame(root) +f.pack({'expand': 1, 'fill': 'both'}) +s = Scrollbar(f, {'relief': 'flat', + Pack: {'side': 'right', 'fill': 'y'}}) +t = Text(f, {'relief': 'raised', 'bd': 2, 'yscrollcommand': (s, 'set'), + 'setgrid': 1, + Pack: {'side': 'left', 'fill': 'both', 'expand': 1}}) + +t.tag_config('bold', {'font': '-Adobe-Courier-Bold-R-Normal-*-120-*'}) +s['command'] = (t, 'yview') +root.title('Tk Remote Controller') +root.iconname('Tk Remote') + +# 2. Create menu button and menus. + +file = Menubutton(mBar, {'text': 'File', 'underline': 0, + Pack: {'side': 'left'}}) +file_m = Menu(file) +file['menu'] = file_m +file_m_apps = Menu(file_m) +file_m.add('cascade', {'label': 'Select Application', 'underline': 0, + 'menu': file_m_apps}) +file_m.add('command', {'label': 'Quit', 'underline': 0, 'command': 'exit'}) + +# 3. Create bindings for text widget to allow commands to be +# entered and information to be selected. New characters +# can only be added at the end of the text (can't ever move +# insertion point). + +def single1(e): + x = e.x + y = e.y + tk.setvar('tk_priv(selectMode)', 'char') + t.mark_set('anchor', At(x, y)) + # Should focus W +t.bind('<1>', single1) + +def double1(e): + x = e.x + y = e.y + tk.setvar('tk_priv(selectMode)', 'word') + tk.call('tk_textSelectTo', t, At(x, y)) +t.bind('<Double-1>', double1) + +def triple1(e): + x = e.x + y = e.y + tk.setvar('tk_priv(selectMode)', 'line') + tk.call('tk_textSelectTo', t, At(x, y)) +t.bind('<Triple-1>', triple1) + +def returnkey(e): + t.insert('insert', '\n') + invoke() +t.bind('<Return>', returnkey) + +def controlv(e): + t.insert('insert', tk.call('selection', 'get')) + t.yview_pickplace('insert') + if t.index('insert')[-2:] == '.0': + invoke() +t.bind('<Control-v>', controlv) + +# 4. Procedure to backspace over one character, as long as +# the character isn't part of the prompt. + +def backspace(e): + if t.index('promptEnd') != t.index('insert - 1 char'): + t.delete('insert - 1 char', 'insert') + t.yview_pickplace('insert') +t.bind('<BackSpace>', backspace) +t.bind('<Control-h>', backspace) +t.bind('<Delete>', backspace) + + +# 5. Procedure that's invoked when return is typed: if +# there's not yet a complete command (e.g. braces are open) +# then do nothing. Otherwise, execute command (locally or +# remotely), output the result or error message, and issue +# a new prompt. + +def invoke(): + cmd = t.get('promptEnd + 1 char', 'insert') + if tk.getboolean(tk.call('info', 'complete', cmd)): + if app == tk.call('winfo', 'name', '.'): + msg = tk.call('eval', cmd) + else: + msg = tk.call('send', app, cmd) + if msg: + t.insert('insert', msg + '\n') + prompt() + t.yview_pickplace('insert') + +def prompt(): + t.insert('insert', app + ': ') + t.mark_set('promptEnd', 'insert - 1 char') + t.tag_add('bold', 'insert linestart', 'promptEnd') + +# 6. Procedure to select a new application. Also changes +# the prompt on the current command line to reflect the new +# name. + +def newApp(appName): + global app + app = appName + t.delete('promptEnd linestart', 'promptEnd') + t.insert('promptEnd', appName + ':') + t.tag_add('bold', 'promptEnd linestart', 'promptEnd') + +newApp_tcl = `id(newApp)` +tk.createcommand(newApp_tcl, newApp) + +def fillAppsMenu(): + file_m_apps.add('command') + file_m_apps.delete(0, 'last') + names = tk.splitlist(tk.call('winfo', 'interps')) + names = map(None, names) # convert tuple to list + names.sort() + for name in names: + file_m_apps.add('command', {'label': name, + 'command': (newApp_tcl, name)}) + +file_m_apps['postcommand'] = fillAppsMenu +mBar.tk_menuBar(file) + +# 7. Miscellaneous initialization. + +app = tk.call('winfo', 'name', '.') +prompt() +tk.call('focus', t) + +root.mainloop() diff --git a/Demo/tkinter/guido/tst.py b/Demo/tkinter/guido/tst.py new file mode 100755 index 0000000..ea573d2 --- /dev/null +++ b/Demo/tkinter/guido/tst.py @@ -0,0 +1,81 @@ +# tst.py +from Tkinter import * +import sys + +def do_hello(): + print 'Hello world!' + +class Quit(Button): + def action(self): + self.quit() + def __init__(self, master=None, cnf={}): + Button.__init__(self, master, + {'text': 'Quit', + 'command': self.action}) + Button.config(self, cnf) + +class Stuff(Canvas): + def enter(self, e): + print 'Enter' + self.itemconfig('current', {'fill': 'red'}) + def leave(self, e): + print 'Leave' + self.itemconfig('current', {'fill': 'blue'}) + def __init__(self, master=None, cnf={}): + Canvas.__init__(self, master, + {'width': 100, 'height': 100}) + Canvas.config(self, cnf) + self.create_rectangle(30, 30, 70, 70, + {'fill': 'red', 'tags': 'box'}) + Canvas.bind(self, 'box', '<Enter>', self.enter) + Canvas.bind(self, 'box', '<Leave>', self.leave) + +class Test(Frame): + text = 'Testing' + num = 1 + def do_xy(self, e): + print (e.x, e.y) + def do_test(self): + if not self.num % 10: + self.text = 'Testing 1 ...' + self.text = self.text + ' ' + `self.num` + self.num = self.num + 1 + self.testing['text'] = self.text + def do_err(self): + 1/0 + def do_after(self): + self.testing.invoke() + self.after(10000, self.do_after) + def __init__(self, master=None): + Frame.__init__(self, master) + self['bd'] = 30 + Pack.config(self) + self.bind('<Motion>', self.do_xy) + self.hello = Button(self, {'name': 'hello', + 'text': 'Hello', + 'command': do_hello, + Pack: {'fill': 'both'}}) + self.testing = Button(self) + self.testing['text'] = self.text + self.testing['command'] = self.do_test + Pack.config(self.testing, {'fill': 'both'}) + self.err = Button(self, {'text': 'Error', + 'command': self.do_err, + Pack: {'fill': 'both'}}) + self.quit = Quit(self, {Pack: {'fill': 'both'}}) + self.exit = Button(self, + {'text': 'Exit', + 'command': lambda: sys.exit(0), + Pack: {'fill': 'both'}}) + self.stuff = Stuff(self, {Pack: {'padx': 2, 'pady': 2}}) + self.do_after() + +test = Test() +test.master.title('Tkinter Test') +test.master.iconname('Test') +test.master.maxsize(500, 500) +test.testing.invoke() + +# Use the -i option and type ^C to get a prompt +test.mainloop() + diff --git a/Demo/tkinter/guido/wish.py b/Demo/tkinter/guido/wish.py new file mode 100755 index 0000000..16cacde --- /dev/null +++ b/Demo/tkinter/guido/wish.py @@ -0,0 +1,26 @@ +# This is about all it requires to write a wish shell in Python! + +import tkinter + +tk = tkinter.create(':0', 'wish', 'Tk', 1) +tk.call('update') + +cmd = '' + +while 1: + if cmd: prompt = '' + else: prompt = '% ' + try: + line = raw_input(prompt) + except EOFError: + break + cmd = cmd + (line + '\n') + tk.record(line) + if tk.getboolean(tk.call('info', 'complete', cmd)): + try: + result = tk.call('eval', cmd) + except tkinter.TclError, msg: + print 'TclError:', msg + else: + if result: print result + cmd = '' |