diff options
author | cinap_lenrek <cinap_lenrek@localhost> | 2011-05-03 11:25:13 +0000 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@localhost> | 2011-05-03 11:25:13 +0000 |
commit | 458120dd40db6b4df55a4e96b650e16798ef06a0 (patch) | |
tree | 8f82685be24fef97e715c6f5ca4c68d34d5074ee /sys/src/cmd/python/Demo/tkinter | |
parent | 3a742c699f6806c1145aea5149bf15de15a0afd7 (diff) |
add hg and python
Diffstat (limited to 'sys/src/cmd/python/Demo/tkinter')
62 files changed, 6864 insertions, 0 deletions
diff --git a/sys/src/cmd/python/Demo/tkinter/README b/sys/src/cmd/python/Demo/tkinter/README new file mode 100644 index 000000000..f245d163c --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/README @@ -0,0 +1,10 @@ +Several collections of example code for Tkinter. + +See the toplevel README for an explanation of the difference between +Tkinter and _tkinter, how to enable the Python Tk interface, and where +to get Matt Conway's lifesaver document. + +Subdirectories: + +guido my original example set (fairly random collection) +matt Matt Conway's examples, to go with his lifesaver document diff --git a/sys/src/cmd/python/Demo/tkinter/guido/AttrDialog.py b/sys/src/cmd/python/Demo/tkinter/guido/AttrDialog.py new file mode 100755 index 000000000..86333adc7 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/AttrDialog.py @@ -0,0 +1,452 @@ + +# The options of a widget are described by the following attributes +# of the Pack and Widget dialogs: +# +# Dialog.current: {name: value} +# -- changes during Widget's lifetime +# +# Dialog.options: {name: (default, klass)} +# -- depends on widget class only +# +# Dialog.classes: {klass: (v0, v1, v2, ...) | 'boolean' | 'other'} +# -- totally static, though different between PackDialog and WidgetDialog +# (but even that could be unified) + +from Tkinter import * + +class Option: + + varclass = StringVar # May be overridden + + def __init__(self, dialog, option): + self.dialog = dialog + self.option = option + self.master = dialog.top + self.default, self.klass = dialog.options[option] + self.var = self.varclass(self.master) + self.frame = Frame(self.master) + self.frame.pack(fill=X) + self.label = Label(self.frame, text=(option + ":")) + self.label.pack(side=LEFT) + self.update() + self.addoption() + + def refresh(self): + self.dialog.refresh() + self.update() + + def update(self): + try: + self.current = self.dialog.current[self.option] + except KeyError: + self.current = self.default + self.var.set(self.current) + + def set(self, e=None): # Should be overridden + pass + +class BooleanOption(Option): + + varclass = BooleanVar + + def addoption(self): + self.button = Checkbutton(self.frame, + text='on/off', + onvalue=1, + offvalue=0, + variable=self.var, + relief=RAISED, + borderwidth=2, + command=self.set) + self.button.pack(side=RIGHT) + +class EnumOption(Option): + + def addoption(self): + self.button = Menubutton(self.frame, + textvariable=self.var, + relief=RAISED, borderwidth=2) + self.button.pack(side=RIGHT) + self.menu = Menu(self.button) + self.button['menu'] = self.menu + for v in self.dialog.classes[self.klass]: + self.menu.add_radiobutton( + label=v, + variable=self.var, + value=v, + command=self.set) + +class StringOption(Option): + + def addoption(self): + self.entry = Entry(self.frame, + textvariable=self.var, + width=10, + relief=SUNKEN, + borderwidth=2) + self.entry.pack(side=RIGHT, fill=X, expand=1) + self.entry.bind('<Return>', self.set) + +class ReadonlyOption(Option): + + def addoption(self): + self.label = Label(self.frame, textvariable=self.var, + anchor=E) + self.label.pack(side=RIGHT) + +class Dialog: + + def __init__(self, master): + self.master = master + self.fixclasses() + self.refresh() + self.top = Toplevel(self.master) + self.top.title(self.__class__.__name__) + self.top.minsize(1, 1) + self.addchoices() + + def refresh(self): pass # Must override + + def fixclasses(self): pass # May override + + def addchoices(self): + self.choices = {} + list = [] + for k, dc in self.options.items(): + list.append((k, dc)) + list.sort() + for k, (d, c) in list: + try: + cl = self.classes[c] + except KeyError: + cl = 'unknown' + if type(cl) == TupleType: + cl = self.enumoption + elif cl == 'boolean': + cl = self.booleanoption + elif cl == 'readonly': + cl = self.readonlyoption + else: + cl = self.stringoption + self.choices[k] = cl(self, k) + + # Must override: + options = {} + classes = {} + + # May override: + booleanoption = BooleanOption + stringoption = StringOption + enumoption = EnumOption + readonlyoption = ReadonlyOption + +class PackDialog(Dialog): + + def __init__(self, widget): + self.widget = widget + Dialog.__init__(self, widget) + + def refresh(self): + self.current = self.widget.info() + self.current['.class'] = self.widget.winfo_class() + self.current['.name'] = self.widget._w + + class packoption: # Mix-in class + def set(self, e=None): + self.current = self.var.get() + try: + apply(self.dialog.widget.pack, (), + {self.option: self.current}) + except TclError, msg: + print msg + self.refresh() + + class booleanoption(packoption, BooleanOption): pass + class enumoption(packoption, EnumOption): pass + class stringoption(packoption, StringOption): pass + class readonlyoption(packoption, ReadonlyOption): pass + + options = { + '.class': (None, 'Class'), + '.name': (None, 'Name'), + 'after': (None, 'Widget'), + 'anchor': ('center', 'Anchor'), + 'before': (None, 'Widget'), + 'expand': ('no', 'Boolean'), + 'fill': ('none', 'Fill'), + 'in': (None, 'Widget'), + 'ipadx': (0, 'Pad'), + 'ipady': (0, 'Pad'), + 'padx': (0, 'Pad'), + 'pady': (0, 'Pad'), + 'side': ('top', 'Side'), + } + + classes = { + 'Anchor': (N, NE, E, SE, S, SW, W, NW, CENTER), + 'Boolean': 'boolean', + 'Class': 'readonly', + 'Expand': 'boolean', + 'Fill': (NONE, X, Y, BOTH), + 'Name': 'readonly', + 'Pad': 'pixel', + 'Side': (TOP, RIGHT, BOTTOM, LEFT), + 'Widget': 'readonly', + } + +class RemotePackDialog(PackDialog): + + def __init__(self, master, app, widget): + self.master = master + self.app = app + self.widget = widget + self.refresh() + self.top = Toplevel(self.master) + self.top.title(self.app + ' PackDialog') + self.top.minsize(1, 1) + self.addchoices() + + def refresh(self): + try: + words = self.master.tk.splitlist( + self.master.send(self.app, + 'pack', + 'info', + self.widget)) + except TclError, msg: + print msg + return + dict = {} + for i in range(0, len(words), 2): + key = words[i][1:] + value = words[i+1] + dict[key] = value + dict['.class'] = self.master.send(self.app, + 'winfo', + 'class', + self.widget) + dict['.name'] = self.widget + self.current = dict + + class remotepackoption: # Mix-in class + def set(self, e=None): + self.current = self.var.get() + try: + self.dialog.master.send( + self.dialog.app, + 'pack', + 'config', + self.dialog.widget, + '-'+self.option, + self.dialog.master.tk.merge( + self.current)) + except TclError, msg: + print msg + self.refresh() + + class booleanoption(remotepackoption, BooleanOption): pass + class enumoption(remotepackoption, EnumOption): pass + class stringoption(remotepackoption, StringOption): pass + class readonlyoption(remotepackoption, ReadonlyOption): pass + +class WidgetDialog(Dialog): + + def __init__(self, widget): + self.widget = widget + self.klass = widget.winfo_class() + Dialog.__init__(self, widget) + + def fixclasses(self): + if self.addclasses.has_key(self.klass): + classes = {} + for c in (self.classes, + self.addclasses[self.klass]): + for k in c.keys(): + classes[k] = c[k] + self.classes = classes + + def refresh(self): + self.configuration = self.widget.config() + self.update() + self.current['.class'] = self.widget.winfo_class() + self.current['.name'] = self.widget._w + + def update(self): + self.current = {} + self.options = {} + for k, v in self.configuration.items(): + if len(v) > 4: + self.current[k] = v[4] + self.options[k] = v[3], v[2] # default, klass + self.options['.class'] = (None, 'Class') + self.options['.name'] = (None, 'Name') + + class widgetoption: # Mix-in class + def set(self, e=None): + self.current = self.var.get() + try: + self.dialog.widget[self.option] = self.current + except TclError, msg: + print msg + self.refresh() + + class booleanoption(widgetoption, BooleanOption): pass + class enumoption(widgetoption, EnumOption): pass + class stringoption(widgetoption, StringOption): pass + class readonlyoption(widgetoption, ReadonlyOption): pass + + # Universal classes + classes = { + 'Anchor': (N, NE, E, SE, S, SW, W, NW, CENTER), + 'Aspect': 'integer', + 'Background': 'color', + 'Bitmap': 'bitmap', + 'BorderWidth': 'pixel', + 'Class': 'readonly', + 'CloseEnough': 'double', + 'Command': 'command', + 'Confine': 'boolean', + 'Cursor': 'cursor', + 'CursorWidth': 'pixel', + 'DisabledForeground': 'color', + 'ExportSelection': 'boolean', + 'Font': 'font', + 'Foreground': 'color', + 'From': 'integer', + 'Geometry': 'geometry', + 'Height': 'pixel', + 'InsertWidth': 'time', + 'Justify': (LEFT, CENTER, RIGHT), + 'Label': 'string', + 'Length': 'pixel', + 'MenuName': 'widget', + 'Name': 'readonly', + 'OffTime': 'time', + 'OnTime': 'time', + 'Orient': (HORIZONTAL, VERTICAL), + 'Pad': 'pixel', + 'Relief': (RAISED, SUNKEN, FLAT, RIDGE, GROOVE), + 'RepeatDelay': 'time', + 'RepeatInterval': 'time', + 'ScrollCommand': 'command', + 'ScrollIncrement': 'pixel', + 'ScrollRegion': 'rectangle', + 'ShowValue': 'boolean', + 'SetGrid': 'boolean', + 'Sliderforeground': 'color', + 'SliderLength': 'pixel', + 'Text': 'string', + 'TickInterval': 'integer', + 'To': 'integer', + 'Underline': 'index', + 'Variable': 'variable', + 'Value': 'string', + 'Width': 'pixel', + 'Wrap': (NONE, CHAR, WORD), + } + + # Classes that (may) differ per widget type + _tristate = {'State': (NORMAL, ACTIVE, DISABLED)} + _bistate = {'State': (NORMAL, DISABLED)} + addclasses = { + 'Button': _tristate, + 'Radiobutton': _tristate, + 'Checkbutton': _tristate, + 'Entry': _bistate, + 'Text': _bistate, + 'Menubutton': _tristate, + 'Slider': _bistate, + } + +class RemoteWidgetDialog(WidgetDialog): + + def __init__(self, master, app, widget): + self.app = app + self.widget = widget + self.klass = master.send(self.app, + 'winfo', + 'class', + self.widget) + Dialog.__init__(self, master) + + def refresh(self): + try: + items = self.master.tk.splitlist( + self.master.send(self.app, + self.widget, + 'config')) + except TclError, msg: + print msg + return + dict = {} + for item in items: + words = self.master.tk.splitlist(item) + key = words[0][1:] + value = (key,) + words[1:] + dict[key] = value + self.configuration = dict + self.update() + self.current['.class'] = self.klass + self.current['.name'] = self.widget + + class remotewidgetoption: # Mix-in class + def set(self, e=None): + self.current = self.var.get() + try: + self.dialog.master.send( + self.dialog.app, + self.dialog.widget, + 'config', + '-'+self.option, + self.current) + except TclError, msg: + print msg + self.refresh() + + class booleanoption(remotewidgetoption, BooleanOption): pass + class enumoption(remotewidgetoption, EnumOption): pass + class stringoption(remotewidgetoption, StringOption): pass + class readonlyoption(remotewidgetoption, ReadonlyOption): pass + +def test(): + import sys + root = Tk() + root.minsize(1, 1) + if sys.argv[1:]: + remotetest(root, sys.argv[1]) + else: + frame = Frame(root, name='frame') + frame.pack(expand=1, fill=BOTH) + button = Button(frame, name='button', text='button') + button.pack(expand=1) + canvas = Canvas(frame, name='canvas') + canvas.pack() + fpd = PackDialog(frame) + fwd = WidgetDialog(frame) + bpd = PackDialog(button) + bwd = WidgetDialog(button) + cpd = PackDialog(canvas) + cwd = WidgetDialog(canvas) + root.mainloop() + +def remotetest(root, app): + from listtree import listtree + list = listtree(root, app) + list.bind('<Any-Double-1>', opendialogs) + list.app = app # Pass it on to handler + +def opendialogs(e): + import string + list = e.widget + sel = list.curselection() + for i in sel: + item = list.get(i) + widget = string.split(item)[0] + RemoteWidgetDialog(list, list.app, widget) + if widget == '.': continue + try: + RemotePackDialog(list, list.app, widget) + except TclError, msg: + print msg + +test() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/ManPage.py b/sys/src/cmd/python/Demo/tkinter/guido/ManPage.py new file mode 100755 index 000000000..de3117b35 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/ManPage.py @@ -0,0 +1,220 @@ +# Widget to display a man page + +import re +from Tkinter import * +from Tkinter import _tkinter +from ScrolledText import ScrolledText + +# XXX These fonts may have to be changed to match your system +BOLDFONT = '*-Courier-Bold-R-Normal-*-120-*' +ITALICFONT = '*-Courier-Medium-O-Normal-*-120-*' + +# XXX Recognizing footers is system dependent +# (This one works for IRIX 5.2 and Solaris 2.2) +footerprog = re.compile( + '^ Page [1-9][0-9]*[ \t]+\|^.*Last change:.*[1-9][0-9]*\n') +emptyprog = re.compile('^[ \t]*\n') +ulprog = re.compile('^[ \t]*[Xv!_][Xv!_ \t]*\n') + +# Basic Man Page class -- does not disable editing +class EditableManPage(ScrolledText): + + # Initialize instance + def __init__(self, master=None, **cnf): + # Initialize base class + apply(ScrolledText.__init__, (self, master), cnf) + + # Define tags for formatting styles + self.tag_config('X', underline=1) + self.tag_config('!', font=BOLDFONT) + self.tag_config('_', font=ITALICFONT) + + # Set state to idle + self.fp = None + self.lineno = 0 + + # Test whether we are busy parsing a file + def busy(self): + return self.fp != None + + # Ensure we're not busy + def kill(self): + if self.busy(): + self._endparser() + + # Parse a file, in the background + def asyncparsefile(self, fp): + self._startparser(fp) + self.tk.createfilehandler(fp, _tkinter.READABLE, + self._filehandler) + + parsefile = asyncparsefile # Alias + + # I/O handler used by background parsing + def _filehandler(self, fp, mask): + nextline = self.fp.readline() + if not nextline: + self._endparser() + return + self._parseline(nextline) + + # Parse a file, now (cannot be aborted) + def syncparsefile(self, fp): + from select import select + def avail(fp=fp, tout=0.0, select=select): + return select([fp], [], [], tout)[0] + height = self.getint(self['height']) + self._startparser(fp) + while 1: + nextline = fp.readline() + if not nextline: + break + self._parseline(nextline) + self._endparser() + + # Initialize parsing from a particular file -- must not be busy + def _startparser(self, fp): + if self.busy(): + raise RuntimeError, 'startparser: still busy' + fp.fileno() # Test for file-ness + self.fp = fp + self.lineno = 0 + self.ok = 0 + self.empty = 0 + self.buffer = None + savestate = self['state'] + self['state'] = NORMAL + self.delete('1.0', END) + self['state'] = savestate + + # End parsing -- must be busy, need not be at EOF + def _endparser(self): + if not self.busy(): + raise RuntimeError, 'endparser: not busy' + if self.buffer: + self._parseline('') + try: + self.tk.deletefilehandler(self.fp) + except TclError, msg: + pass + self.fp.close() + self.fp = None + del self.ok, self.empty, self.buffer + + # Parse a single line + def _parseline(self, nextline): + if not self.buffer: + # Save this line -- we need one line read-ahead + self.buffer = nextline + return + if emptyprog.match(self.buffer) >= 0: + # Buffered line was empty -- set a flag + self.empty = 1 + self.buffer = nextline + return + textline = self.buffer + if ulprog.match(nextline) >= 0: + # Next line is properties for buffered line + propline = nextline + self.buffer = None + else: + # Next line is read-ahead + propline = None + self.buffer = nextline + if not self.ok: + # First non blank line after footer must be header + # -- skip that too + self.ok = 1 + self.empty = 0 + return + if footerprog.match(textline) >= 0: + # Footer -- start skipping until next non-blank line + self.ok = 0 + self.empty = 0 + return + savestate = self['state'] + self['state'] = NORMAL + if TkVersion >= 4.0: + self.mark_set('insert', 'end-1c') + else: + self.mark_set('insert', END) + if self.empty: + # One or more previous lines were empty + # -- insert one blank line in the text + self._insert_prop('\n') + self.lineno = self.lineno + 1 + self.empty = 0 + if not propline: + # No properties + self._insert_prop(textline) + else: + # Search for properties + p = '' + j = 0 + for i in range(min(len(propline), len(textline))): + if propline[i] != p: + if j < i: + self._insert_prop(textline[j:i], p) + j = i + p = propline[i] + self._insert_prop(textline[j:]) + self.lineno = self.lineno + 1 + self['state'] = savestate + + # Insert a string at the end, with at most one property (tag) + def _insert_prop(self, str, prop = ' '): + here = self.index(AtInsert()) + self.insert(AtInsert(), str) + if TkVersion <= 4.0: + tags = self.tag_names(here) + for tag in tags: + self.tag_remove(tag, here, AtInsert()) + if prop != ' ': + self.tag_add(prop, here, AtInsert()) + +# Readonly Man Page class -- disables editing, otherwise the same +class ReadonlyManPage(EditableManPage): + + # Initialize instance + def __init__(self, master=None, **cnf): + cnf['state'] = DISABLED + apply(EditableManPage.__init__, (self, master), cnf) + +# Alias +ManPage = ReadonlyManPage + +# Test program. +# usage: ManPage [manpage]; or ManPage [-f] file +# -f means that the file is nroff -man output run through ul -i +def test(): + import os + import sys + # XXX This directory may be different on your system + MANDIR = '/usr/local/man/mann' + DEFAULTPAGE = 'Tcl' + formatted = 0 + if sys.argv[1:] and sys.argv[1] == '-f': + formatted = 1 + del sys.argv[1] + if sys.argv[1:]: + name = sys.argv[1] + else: + name = DEFAULTPAGE + if not formatted: + if name[-2:-1] != '.': + name = name + '.n' + name = os.path.join(MANDIR, name) + root = Tk() + root.minsize(1, 1) + manpage = ManPage(root, relief=SUNKEN, borderwidth=2) + manpage.pack(expand=1, fill=BOTH) + if formatted: + fp = open(name, 'r') + else: + fp = os.popen('nroff -man %s | ul -i' % name, 'r') + manpage.parsefile(fp) + root.mainloop() + +# Run the test program when called as a script +if __name__ == '__main__': + test() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/MimeViewer.py b/sys/src/cmd/python/Demo/tkinter/guido/MimeViewer.py new file mode 100755 index 000000000..749442589 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/MimeViewer.py @@ -0,0 +1,143 @@ +#! /usr/bin/env python + +# 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/sys/src/cmd/python/Demo/tkinter/guido/ShellWindow.py b/sys/src/cmd/python/Demo/tkinter/guido/ShellWindow.py new file mode 100755 index 000000000..609101bc8 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/ShellWindow.py @@ -0,0 +1,151 @@ +import os +import sys +import string +from Tkinter import * +from ScrolledText import ScrolledText +from Dialog import Dialog +import signal + +BUFSIZE = 512 + +class ShellWindow(ScrolledText): + + def __init__(self, master=None, shell=None, **cnf): + if not shell: + try: + shell = os.environ['SHELL'] + except KeyError: + shell = '/bin/sh' + shell = shell + ' -i' + args = string.split(shell) + shell = args[0] + + apply(ScrolledText.__init__, (self, master), cnf) + self.pos = '1.0' + self.bind('<Return>', self.inputhandler) + self.bind('<Control-c>', self.sigint) + self.bind('<Control-t>', self.sigterm) + self.bind('<Control-k>', self.sigkill) + self.bind('<Control-d>', self.sendeof) + + self.pid, self.fromchild, self.tochild = spawn(shell, args) + self.tk.createfilehandler(self.fromchild, READABLE, + self.outputhandler) + + def outputhandler(self, file, mask): + data = os.read(file, BUFSIZE) + if not data: + self.tk.deletefilehandler(file) + pid, sts = os.waitpid(self.pid, 0) + print 'pid', pid, 'status', sts + self.pid = None + detail = sts>>8 + cause = sts & 0xff + if cause == 0: + msg = "exit status %d" % detail + else: + msg = "killed by signal %d" % (cause & 0x7f) + if cause & 0x80: + msg = msg + " -- core dumped" + Dialog(self.master, + text=msg, + title="Exit status", + bitmap='warning', + default=0, + strings=('OK',)) + return + self.insert(END, data) + self.pos = self.index("end - 1 char") + self.yview_pickplace(END) + + def inputhandler(self, *args): + if not self.pid: + self.no_process() + return "break" + self.insert(END, "\n") + line = self.get(self.pos, "end - 1 char") + self.pos = self.index(END) + os.write(self.tochild, line) + return "break" + + def sendeof(self, *args): + if not self.pid: + self.no_process() + return "break" + os.close(self.tochild) + return "break" + + def sendsig(self, sig): + if not self.pid: + self.no_process() + return "break" + os.kill(self.pid, sig) + return "break" + + def sigint(self, *args): + return self.sendsig(signal.SIGINT) + + def sigquit(self, *args): + return self.sendsig(signal.SIGQUIT) + + def sigterm(self, *args): + return self.sendsig(signal.SIGTERM) + + def sigkill(self, *args): + return self.sendsig(signal.SIGKILL) + + def no_process(self): + Dialog(self.master, + text="No active process", + title="No process", + bitmap='error', + default=0, + strings=('OK',)) + +MAXFD = 100 # Max number of file descriptors (os.getdtablesize()???) + +def spawn(prog, args): + p2cread, p2cwrite = os.pipe() + c2pread, c2pwrite = os.pipe() + pid = os.fork() + if pid == 0: + # Child + for i in 0, 1, 2: + try: + os.close(i) + except os.error: + pass + if os.dup(p2cread) <> 0: + sys.stderr.write('popen2: bad read dup\n') + if os.dup(c2pwrite) <> 1: + sys.stderr.write('popen2: bad write dup\n') + if os.dup(c2pwrite) <> 2: + sys.stderr.write('popen2: bad write dup\n') + for i in range(3, MAXFD): + try: + os.close(i) + except: + pass + try: + os.execvp(prog, args) + finally: + sys.stderr.write('execvp failed\n') + os._exit(1) + os.close(p2cread) + os.close(c2pwrite) + return pid, c2pread, p2cwrite + +def test(): + shell = string.join(sys.argv[1:]) + root = Tk() + root.minsize(1, 1) + if shell: + w = ShellWindow(root, shell=shell) + else: + w = ShellWindow(root) + w.pack(expand=1, fill=BOTH) + w.focus_set() + w.tk.mainloop() + +if __name__ == '__main__': + test() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/brownian.py b/sys/src/cmd/python/Demo/tkinter/guido/brownian.py new file mode 100644 index 000000000..8007f141c --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/brownian.py @@ -0,0 +1,50 @@ +# Brownian motion -- an example of a multi-threaded Tkinter program. + +from Tkinter import * +import random +import threading +import time +import sys + +WIDTH = 400 +HEIGHT = 300 +SIGMA = 10 +BUZZ = 2 +RADIUS = 2 +LAMBDA = 10 +FILL = 'red' + +stop = 0 # Set when main loop exits + +def particle(canvas): + r = RADIUS + x = random.gauss(WIDTH/2.0, SIGMA) + y = random.gauss(HEIGHT/2.0, SIGMA) + p = canvas.create_oval(x-r, y-r, x+r, y+r, fill=FILL) + while not stop: + dx = random.gauss(0, BUZZ) + dy = random.gauss(0, BUZZ) + dt = random.expovariate(LAMBDA) + try: + canvas.move(p, dx, dy) + except TclError: + break + time.sleep(dt) + +def main(): + global stop + root = Tk() + canvas = Canvas(root, width=WIDTH, height=HEIGHT) + canvas.pack(fill='both', expand=1) + np = 30 + if sys.argv[1:]: + np = int(sys.argv[1]) + for i in range(np): + t = threading.Thread(target=particle, args=(canvas,)) + t.start() + try: + root.mainloop() + finally: + stop = 1 + +main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/canvasevents.py b/sys/src/cmd/python/Demo/tkinter/guido/canvasevents.py new file mode 100644 index 000000000..74ed76f61 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/canvasevents.py @@ -0,0 +1,244 @@ +#! /usr/bin/env python + +from Tkinter import * +from Canvas import Oval, Group, CanvasText + + +# Fix a bug in Canvas.Group as distributed in Python 1.4. The +# distributed bind() method is broken. This is what should be used: + +class Group(Group): + def bind(self, sequence=None, command=None): + return self.canvas.tag_bind(self.id, sequence, command) + +class Object: + + """Base class for composite graphical objects. + + Objects belong to a canvas, and can be moved around on the canvas. + They also belong to at most one ``pile'' of objects, and can be + transferred between piles (or removed from their pile). + + Objects have a canonical ``x, y'' position which is moved when the + object is moved. Where the object is relative to this position + depends on the object; for simple objects, it may be their center. + + Objects have mouse sensitivity. They can be clicked, dragged and + double-clicked. The behavior may actually determined by the pile + they are in. + + All instance attributes are public since the derived class may + need them. + + """ + + def __init__(self, canvas, x=0, y=0, fill='red', text='object'): + self.canvas = canvas + self.x = x + self.y = y + self.pile = None + self.group = Group(self.canvas) + self.createitems(fill, text) + + def __str__(self): + return str(self.group) + + def createitems(self, fill, text): + self.__oval = Oval(self.canvas, + self.x-20, self.y-10, self.x+20, self.y+10, + fill=fill, width=3) + self.group.addtag_withtag(self.__oval) + self.__text = CanvasText(self.canvas, + self.x, self.y, text=text) + self.group.addtag_withtag(self.__text) + + def moveby(self, dx, dy): + if dx == dy == 0: + return + self.group.move(dx, dy) + self.x = self.x + dx + self.y = self.y + dy + + def moveto(self, x, y): + self.moveby(x - self.x, y - self.y) + + def transfer(self, pile): + if self.pile: + self.pile.delete(self) + self.pile = None + self.pile = pile + if self.pile: + self.pile.add(self) + + def tkraise(self): + self.group.tkraise() + + +class Bottom(Object): + + """An object to serve as the bottom of a pile.""" + + def createitems(self, *args): + self.__oval = Oval(self.canvas, + self.x-20, self.y-10, self.x+20, self.y+10, + fill='gray', outline='') + self.group.addtag_withtag(self.__oval) + + +class Pile: + + """A group of graphical objects.""" + + def __init__(self, canvas, x, y, tag=None): + self.canvas = canvas + self.x = x + self.y = y + self.objects = [] + self.bottom = Bottom(self.canvas, self.x, self.y) + self.group = Group(self.canvas, tag=tag) + self.group.addtag_withtag(self.bottom.group) + self.bindhandlers() + + def bindhandlers(self): + self.group.bind('<1>', self.clickhandler) + self.group.bind('<Double-1>', self.doubleclickhandler) + + def add(self, object): + self.objects.append(object) + self.group.addtag_withtag(object.group) + self.position(object) + + def delete(self, object): + object.group.dtag(self.group) + self.objects.remove(object) + + def position(self, object): + object.tkraise() + i = self.objects.index(object) + object.moveto(self.x + i*4, self.y + i*8) + + def clickhandler(self, event): + pass + + def doubleclickhandler(self, event): + pass + + +class MovingPile(Pile): + + def bindhandlers(self): + Pile.bindhandlers(self) + self.group.bind('<B1-Motion>', self.motionhandler) + self.group.bind('<ButtonRelease-1>', self.releasehandler) + + movethis = None + + def clickhandler(self, event): + tags = self.canvas.gettags('current') + for i in range(len(self.objects)): + o = self.objects[i] + if o.group.tag in tags: + break + else: + self.movethis = None + return + self.movethis = self.objects[i:] + for o in self.movethis: + o.tkraise() + self.lastx = event.x + self.lasty = event.y + + doubleclickhandler = clickhandler + + def motionhandler(self, event): + if not self.movethis: + return + dx = event.x - self.lastx + dy = event.y - self.lasty + self.lastx = event.x + self.lasty = event.y + for o in self.movethis: + o.moveby(dx, dy) + + def releasehandler(self, event): + objects = self.movethis + if not objects: + return + self.movethis = None + self.finishmove(objects) + + def finishmove(self, objects): + for o in objects: + self.position(o) + + +class Pile1(MovingPile): + + x = 50 + y = 50 + tag = 'p1' + + def __init__(self, demo): + self.demo = demo + MovingPile.__init__(self, self.demo.canvas, self.x, self.y, self.tag) + + def doubleclickhandler(self, event): + try: + o = self.objects[-1] + except IndexError: + return + o.transfer(self.other()) + MovingPile.doubleclickhandler(self, event) + + def other(self): + return self.demo.p2 + + def finishmove(self, objects): + o = objects[0] + p = self.other() + x, y = o.x, o.y + if (x-p.x)**2 + (y-p.y)**2 < (x-self.x)**2 + (y-self.y)**2: + for o in objects: + o.transfer(p) + else: + MovingPile.finishmove(self, objects) + +class Pile2(Pile1): + + x = 150 + y = 50 + tag = 'p2' + + def other(self): + return self.demo.p1 + + +class Demo: + + def __init__(self, master): + self.master = master + self.canvas = Canvas(master, + width=200, height=200, + background='yellow', + relief=SUNKEN, borderwidth=2) + self.canvas.pack(expand=1, fill=BOTH) + self.p1 = Pile1(self) + self.p2 = Pile2(self) + o1 = Object(self.canvas, fill='red', text='o1') + o2 = Object(self.canvas, fill='green', text='o2') + o3 = Object(self.canvas, fill='light blue', text='o3') + o1.transfer(self.p1) + o2.transfer(self.p1) + o3.transfer(self.p2) + + +# Main function, run when invoked as a stand-alone Python program. + +def main(): + root = Tk() + demo = Demo(root) + root.protocol('WM_DELETE_WINDOW', root.quit) + root.mainloop() + +if __name__ == '__main__': + main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/dialog.py b/sys/src/cmd/python/Demo/tkinter/guido/dialog.py new file mode 100755 index 000000000..50d84b9a3 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/dialog.py @@ -0,0 +1,109 @@ +#! /usr/bin/env python + +# 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 * +import sys + + +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.title(title) + w.iconname('Dialog') + + top = Frame(w, relief=RAISED, borderwidth=1) + top.pack(side=TOP, fill=BOTH) + bot = Frame(w, relief=RAISED, borderwidth=1) + bot.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-*') + msg.pack(side=RIGHT, expand=1, fill=BOTH, padx='3m', pady='3m') + if bitmap: + bm = Label(top, bitmap=bitmap) + bm.pack(side=LEFT, padx='3m', pady='3m') + + # 3. Create a row of buttons at the bottom of the dialog. + + var = IntVar() + buttons = [] + i = 0 + for but in args: + b = Button(bot, text=but, command=lambda v=var,i=i: v.set(i)) + buttons.append(b) + if i == default: + bd = Frame(bot, relief=SUNKEN, borderwidth=1) + bd.pack(side=LEFT, expand=1, padx='3m', pady='2m') + b.lift() + 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', ipadx='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 e, b=buttons[default], v=var, i=default: + (b.flash(), + v.set(i))) + + oldFocus = w.focus_get() + w.grab_set() + w.focus_set() + + # 5. Wait for the user to respond, then restore the focus + # and return the index of the selected button. + + w.waitvar(var) + w.destroy() + if oldFocus: oldFocus.focus_set() + return var.get() + +# 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=sys.exit) + endit.pack(fill=BOTH) + mainWidget.mainloop() + +if __name__ == '__main__': + test() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/electrons.py b/sys/src/cmd/python/Demo/tkinter/guido/electrons.py new file mode 100755 index 000000000..fdc558f88 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/electrons.py @@ -0,0 +1,91 @@ +#! /usr/bin/env python + +# Simulate "electrons" migrating across the screen. +# An optional bitmap file in can be in the background. +# +# Usage: electrons [n [bitmapfile]] +# +# n is the number of electrons to animate; default is 30. +# +# The bitmap file can be any X11 bitmap file (look in +# /usr/include/X11/bitmaps for samples); it is displayed as the +# background of the animation. Default is no bitmap. + +from Tkinter import * +import random + + +# The graphical interface +class Electrons: + + # Create our objects + def __init__(self, n, bitmap = None): + self.n = n + self.tk = tk = Tk() + self.canvas = c = Canvas(tk) + c.pack() + width, height = tk.getint(c['width']), tk.getint(c['height']) + + # Add background bitmap + if bitmap: + self.bitmap = c.create_bitmap(width/2, height/2, + bitmap=bitmap, + foreground='blue') + + self.pieces = [] + x1, y1, x2, y2 = 10,70,14,74 + for i in range(n): + p = c.create_oval(x1, y1, x2, y2, fill='red') + self.pieces.append(p) + y1, y2 = y1 +2, y2 + 2 + self.tk.update() + + def random_move(self, n): + c = self.canvas + for p in self.pieces: + x = random.choice(range(-2,4)) + y = random.choice(range(-3,4)) + c.move(p, x, y) + self.tk.update() + + # Run -- allow 500 movemens + def run(self): + try: + for i in range(500): + self.random_move(self.n) + except TclError: + try: + self.tk.destroy() + except TclError: + pass + + +# Main program +def main(): + import sys, string + + # First argument is number of electrons, default 30 + if sys.argv[1:]: + n = string.atoi(sys.argv[1]) + else: + n = 30 + + # Second argument is bitmap file, default none + if sys.argv[2:]: + bitmap = sys.argv[2] + # Reverse meaning of leading '@' compared to Tk + if bitmap[0] == '@': bitmap = bitmap[1:] + else: bitmap = '@' + bitmap + else: + bitmap = None + + # Create the graphical objects... + h = Electrons(n, bitmap) + + # ...and run! + h.run() + + +# Call main when run as script +if __name__ == '__main__': + main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/hanoi.py b/sys/src/cmd/python/Demo/tkinter/guido/hanoi.py new file mode 100755 index 000000000..078c24611 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/hanoi.py @@ -0,0 +1,154 @@ +# Animated Towers of Hanoi using Tk with optional bitmap file in +# background. +# +# Usage: tkhanoi [n [bitmapfile]] +# +# n is the number of pieces to animate; default is 4, maximum 15. +# +# The bitmap file can be any X11 bitmap file (look in +# /usr/include/X11/bitmaps for samples); it is displayed as the +# background of the animation. Default is no bitmap. + +# This uses Steen Lumholt's Tk interface +from Tkinter import * + + +# Basic Towers-of-Hanoi algorithm: move n pieces from a to b, using c +# as temporary. For each move, call report() +def hanoi(n, a, b, c, report): + if n <= 0: return + hanoi(n-1, a, c, b, report) + report(n, a, b) + hanoi(n-1, c, b, a, report) + + +# The graphical interface +class Tkhanoi: + + # Create our objects + def __init__(self, n, bitmap = None): + self.n = n + self.tk = tk = Tk() + self.canvas = c = Canvas(tk) + c.pack() + width, height = tk.getint(c['width']), tk.getint(c['height']) + + # Add background bitmap + if bitmap: + self.bitmap = c.create_bitmap(width/2, height/2, + bitmap=bitmap, + foreground='blue') + + # Generate pegs + pegwidth = 10 + pegheight = height/2 + pegdist = width/3 + x1, y1 = (pegdist-pegwidth)/2, height*1/3 + x2, y2 = x1+pegwidth, y1+pegheight + self.pegs = [] + p = c.create_rectangle(x1, y1, x2, y2, fill='black') + self.pegs.append(p) + x1, x2 = x1+pegdist, x2+pegdist + p = c.create_rectangle(x1, y1, x2, y2, fill='black') + self.pegs.append(p) + x1, x2 = x1+pegdist, x2+pegdist + p = c.create_rectangle(x1, y1, x2, y2, fill='black') + self.pegs.append(p) + self.tk.update() + + # Generate pieces + pieceheight = pegheight/16 + maxpiecewidth = pegdist*2/3 + minpiecewidth = 2*pegwidth + self.pegstate = [[], [], []] + self.pieces = {} + x1, y1 = (pegdist-maxpiecewidth)/2, y2-pieceheight-2 + x2, y2 = x1+maxpiecewidth, y1+pieceheight + dx = (maxpiecewidth-minpiecewidth) / (2*max(1, n-1)) + for i in range(n, 0, -1): + p = c.create_rectangle(x1, y1, x2, y2, fill='red') + self.pieces[i] = p + self.pegstate[0].append(i) + x1, x2 = x1 + dx, x2-dx + y1, y2 = y1 - pieceheight-2, y2-pieceheight-2 + self.tk.update() + self.tk.after(25) + + # Run -- never returns + def run(self): + while 1: + hanoi(self.n, 0, 1, 2, self.report) + hanoi(self.n, 1, 2, 0, self.report) + hanoi(self.n, 2, 0, 1, self.report) + hanoi(self.n, 0, 2, 1, self.report) + hanoi(self.n, 2, 1, 0, self.report) + hanoi(self.n, 1, 0, 2, self.report) + + # Reporting callback for the actual hanoi function + def report(self, i, a, b): + if self.pegstate[a][-1] != i: raise RuntimeError # Assertion + del self.pegstate[a][-1] + p = self.pieces[i] + c = self.canvas + + # Lift the piece above peg a + ax1, ay1, ax2, ay2 = c.bbox(self.pegs[a]) + while 1: + x1, y1, x2, y2 = c.bbox(p) + if y2 < ay1: break + c.move(p, 0, -1) + self.tk.update() + + # Move it towards peg b + bx1, by1, bx2, by2 = c.bbox(self.pegs[b]) + newcenter = (bx1+bx2)/2 + while 1: + x1, y1, x2, y2 = c.bbox(p) + center = (x1+x2)/2 + if center == newcenter: break + if center > newcenter: c.move(p, -1, 0) + else: c.move(p, 1, 0) + self.tk.update() + + # Move it down on top of the previous piece + pieceheight = y2-y1 + newbottom = by2 - pieceheight*len(self.pegstate[b]) - 2 + while 1: + x1, y1, x2, y2 = c.bbox(p) + if y2 >= newbottom: break + c.move(p, 0, 1) + self.tk.update() + + # Update peg state + self.pegstate[b].append(i) + + +# Main program +def main(): + import sys, string + + # First argument is number of pegs, default 4 + if sys.argv[1:]: + n = string.atoi(sys.argv[1]) + else: + n = 4 + + # Second argument is bitmap file, default none + if sys.argv[2:]: + bitmap = sys.argv[2] + # Reverse meaning of leading '@' compared to Tk + if bitmap[0] == '@': bitmap = bitmap[1:] + else: bitmap = '@' + bitmap + else: + bitmap = None + + # Create the graphical objects... + h = Tkhanoi(n, bitmap) + + # ...and run! + h.run() + + +# Call main when run as script +if __name__ == '__main__': + main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/hello.py b/sys/src/cmd/python/Demo/tkinter/guido/hello.py new file mode 100755 index 000000000..358a7eceb --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/hello.py @@ -0,0 +1,17 @@ +# Display hello, world in a button; clicking it quits the program + +import sys +from Tkinter import * + +def main(): + root = Tk() + button = Button(root) + button['text'] = 'Hello, world' + button['command'] = quit_callback # See below + button.pack() + root.mainloop() + +def quit_callback(): + sys.exit(0) + +main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/imagedraw.py b/sys/src/cmd/python/Demo/tkinter/guido/imagedraw.py new file mode 100755 index 000000000..d3dba4565 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/imagedraw.py @@ -0,0 +1,23 @@ +"""Draw on top of an image""" + +from Tkinter import * +import sys + +def main(): + filename = sys.argv[1] + root = Tk() + img = PhotoImage(file=filename) + w, h = img.width(), img.height() + canv = Canvas(root, width=w, height=h) + canv.create_image(0, 0, anchor=NW, image=img) + canv.pack() + canv.bind('<Button-1>', blob) + root.mainloop() + +def blob(event): + x, y = event.x, event.y + canv = event.widget + r = 5 + canv.create_oval(x-r, y-r, x+r, y+r, fill='red', outline="") + +main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/imageview.py b/sys/src/cmd/python/Demo/tkinter/guido/imageview.py new file mode 100755 index 000000000..d6efed0b2 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/imageview.py @@ -0,0 +1,12 @@ +from Tkinter import * +import sys + +def main(): + filename = sys.argv[1] + root = Tk() + img = PhotoImage(file=filename) + label = Label(root, image=img) + label.pack() + root.mainloop() + +main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/kill.py b/sys/src/cmd/python/Demo/tkinter/guido/kill.py new file mode 100755 index 000000000..e7df26121 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/kill.py @@ -0,0 +1,98 @@ +#! /usr/bin/env python +# Tkinter interface to Linux `kill' command. + +from Tkinter import * +from string import splitfields +from string import split +import commands +import os + +class BarButton(Menubutton): + def __init__(self, master=None, **cnf): + apply(Menubutton.__init__, (self, master), cnf) + self.pack(side=LEFT) + self.menu = Menu(self, name='menu') + self['menu'] = self.menu + +class Kill(Frame): + # List of (name, option, pid_column) + format_list = [('Default', '', 0), + ('Long', '-l', 2), + ('User', '-u', 1), + ('Jobs', '-j', 1), + ('Signal', '-s', 1), + ('Memory', '-m', 0), + ('VM', '-v', 0), + ('Hex', '-X', 0)] + def kill(self, selected): + c = self.format_list[self.format.get()][2] + pid = split(selected)[c] + os.system('kill -9 ' + pid) + self.do_update() + def do_update(self): + name, option, column = self.format_list[self.format.get()] + s = commands.getoutput('ps -w ' + option) + list = splitfields(s, '\n') + self.header.set(list[0]) + del list[0] + y = self.frame.vscroll.get()[0] + self.frame.list.delete(0, AtEnd()) + for line in list: + self.frame.list.insert(0, line) + self.frame.list.yview(int(y)) + def do_motion(self, e): + e.widget.select_clear(0, END) + e.widget.select_set(e.widget.nearest(e.y)) + def do_leave(self, e): + e.widget.select_clear(0, END) + def do_1(self, e): + self.kill(e.widget.get(e.widget.nearest(e.y))) + def __init__(self, master=None, **cnf): + Frame.__init__(self, master, cnf) + self.pack(expand=1, fill=BOTH) + self.bar = Frame(self, name='bar', relief=RAISED, + borderwidth=2) + self.bar.pack(fill=X) + self.bar.file = BarButton(self.bar, text='File') + self.bar.file.menu.add_command( + label='Quit', command=self.quit) + self.bar.view = BarButton(self.bar, text='View') + self.format = IntVar(self) + self.format.set(2) + for num in range(len(self.format_list)): + self.bar.view.menu.add_radiobutton( + label=self.format_list[num][0], + command=self.do_update, + variable=self.format, + value=num) + #self.bar.view.menu.add_separator() + #XXX ... + self.bar.tk_menuBar(self.bar.file, self.bar.view) + self.frame = Frame(self, relief=RAISED, borderwidth=2) + self.frame.pack(expand=1, fill=BOTH) + self.header = StringVar(self) + self.frame.label = Label(self.frame, relief=FLAT, anchor=NW, + borderwidth=0, + textvariable=self.header) + self.frame.label.pack(fill=X) + self.frame.vscroll = Scrollbar(self.frame, orient=VERTICAL) + self.frame.list = Listbox(self.frame, relief=SUNKEN, + selectbackground='#eed5b7', + selectborderwidth=0, + yscroll=self.frame.vscroll.set) + self.frame.vscroll['command'] = self.frame.list.yview + self.frame.vscroll.pack(side=RIGHT, fill=Y) + self.frame.list.pack(expand=1, fill=BOTH) + self.update = Button(self, text="Update", + command=self.do_update) + self.update.pack(expand=1, fill=X) + self.frame.list.bind('<Motion>', self.do_motion) + self.frame.list.bind('<Leave>', self.do_leave) + self.frame.list.bind('<1>', self.do_1) + self.do_update() + +if __name__ == '__main__': + kill = Kill(None, borderwidth=5) + kill.winfo_toplevel().title('Tkinter Process Killer') + kill.winfo_toplevel().minsize(1, 1) + kill.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/listtree.py b/sys/src/cmd/python/Demo/tkinter/guido/listtree.py new file mode 100755 index 000000000..d28ce49ef --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/listtree.py @@ -0,0 +1,37 @@ +# List a remote app's widget tree (names and classes only) + +import sys +import string + +from Tkinter import * + +def listtree(master, app): + list = Listbox(master, name='list') + list.pack(expand=1, fill=BOTH) + listnodes(list, app, '.', 0) + return list + +def listnodes(list, app, widget, level): + klass = list.send(app, 'winfo', 'class', widget) +## i = string.rindex(widget, '.') +## list.insert(END, '%s%s (%s)' % ((level-1)*'. ', widget[i:], klass)) + list.insert(END, '%s (%s)' % (widget, klass)) + children = list.tk.splitlist( + list.send(app, 'winfo', 'children', widget)) + for c in children: + listnodes(list, app, c, level+1) + +def main(): + if not sys.argv[1:]: + sys.stderr.write('Usage: listtree appname\n') + sys.exit(2) + app = sys.argv[1] + tk = Tk() + tk.minsize(1, 1) + f = Frame(tk, name='f') + f.pack(expand=1, fill=BOTH) + list = listtree(f, app) + tk.mainloop() + +if __name__ == '__main__': + main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/mbox.py b/sys/src/cmd/python/Demo/tkinter/guido/mbox.py new file mode 100755 index 000000000..3c36d8899 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/mbox.py @@ -0,0 +1,285 @@ +#! /usr/bin/env python + +# Scan MH folder, display results in window + +import os +import sys +import re +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, {'exportselection': 0}) + 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 = re.compile('^ *([0-9]+)') + +def open_folder(e=None): + 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=None): + 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(e=None): + 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(e=None): + 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))) + if lastrefile != refileto or not tofolder: + 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(repr(i)) + if scanparser.match(line) >= 0: + num = string.atoi(scanparser.group(1)) + if num >= near: + break + else: + i = 'end' + scanbox.select_from(i) + 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/sys/src/cmd/python/Demo/tkinter/guido/newmenubardemo.py b/sys/src/cmd/python/Demo/tkinter/guido/newmenubardemo.py new file mode 100644 index 000000000..57bf13c44 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/newmenubardemo.py @@ -0,0 +1,47 @@ +#! /usr/bin/env python + +"""Play with the new Tk 8.0 toplevel menu option.""" + +from Tkinter import * + +class App: + + def __init__(self, master): + self.master = master + + self.menubar = Menu(self.master) + + self.filemenu = Menu(self.menubar) + + self.filemenu.add_command(label="New") + self.filemenu.add_command(label="Open...") + self.filemenu.add_command(label="Close") + self.filemenu.add_separator() + self.filemenu.add_command(label="Quit", command=self.master.quit) + + self.editmenu = Menu(self.menubar) + + self.editmenu.add_command(label="Cut") + self.editmenu.add_command(label="Copy") + self.editmenu.add_command(label="Paste") + + self.helpmenu = Menu(self.menubar, name='help') + + self.helpmenu.add_command(label="About...") + + self.menubar.add_cascade(label="File", menu=self.filemenu) + self.menubar.add_cascade(label="Edit", menu=self.editmenu) + self.menubar.add_cascade(label="Help", menu=self.helpmenu) + + self.top = Toplevel(menu=self.menubar) + + # Rest of app goes here... + +def main(): + root = Tk() + root.withdraw() + app = App(root) + root.mainloop() + +if __name__ == '__main__': + main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/optionmenu.py b/sys/src/cmd/python/Demo/tkinter/guido/optionmenu.py new file mode 100644 index 000000000..be9d3ac2a --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/optionmenu.py @@ -0,0 +1,27 @@ +# option menu sample (Fredrik Lundh, September 1997) + +from Tkinter import * + +root = Tk() + +# +# standard usage + +var1 = StringVar() +var1.set("One") # default selection + +menu1 = OptionMenu(root, var1, "One", "Two", "Three") +menu1.pack() + +# +# initialize from a sequence + +CHOICES = "Aah", "Bee", "Cee", "Dee", "Eff" + +var2 = StringVar() +var2.set(CHOICES[0]) + +menu2 = apply(OptionMenu, (root, var2) + tuple(CHOICES)) +menu2.pack() + +root.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/paint.py b/sys/src/cmd/python/Demo/tkinter/guido/paint.py new file mode 100644 index 000000000..d46e20b9a --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/paint.py @@ -0,0 +1,60 @@ +""""Paint program by Dave Michell. + +Subject: tkinter "paint" example +From: Dave Mitchell <davem@magnet.com> +To: python-list@cwi.nl +Date: Fri, 23 Jan 1998 12:18:05 -0500 (EST) + + Not too long ago (last week maybe?) someone posted a request +for an example of a paint program using Tkinter. Try as I might +I can't seem to find it in the archive, so i'll just post mine +here and hope that the person who requested it sees this! + + All this does is put up a canvas and draw a smooth black line +whenever you have the mouse button down, but hopefully it will +be enough to start with.. It would be easy enough to add some +options like other shapes or colors... + + yours, + dave mitchell + davem@magnet.com +""" + +from Tkinter import * + +"""paint.py: not exactly a paint program.. just a smooth line drawing demo.""" + +b1 = "up" +xold, yold = None, None + +def main(): + root = Tk() + drawing_area = Canvas(root) + drawing_area.pack() + drawing_area.bind("<Motion>", motion) + drawing_area.bind("<ButtonPress-1>", b1down) + drawing_area.bind("<ButtonRelease-1>", b1up) + root.mainloop() + +def b1down(event): + global b1 + b1 = "down" # you only want to draw when the button is down + # because "Motion" events happen -all the time- + +def b1up(event): + global b1, xold, yold + b1 = "up" + xold = None # reset the line when you let go of the button + yold = None + +def motion(event): + if b1 == "down": + global xold, yold + if xold != None and yold != None: + event.widget.create_line(xold,yold,event.x,event.y,smooth=TRUE) + # here's where you draw it. smooth. neat. + xold = event.x + yold = event.y + +if __name__ == "__main__": + main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/rmt.py b/sys/src/cmd/python/Demo/tkinter/guido/rmt.py new file mode 100755 index 000000000..440650c9c --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/rmt.py @@ -0,0 +1,159 @@ +#! /usr/bin/env python + +# 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. + +# XXX This should be written in a more Python-like style!!! + +from Tkinter import * +import sys + +# 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, borderwidth=2) +mBar.pack(fill=X) + +f = Frame(root) +f.pack(expand=1, fill=BOTH) +s = Scrollbar(f, relief=FLAT) +s.pack(side=RIGHT, fill=Y) +t = Text(f, relief=RAISED, borderwidth=2, yscrollcommand=s.set, setgrid=1) +t.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) +file.pack(side=LEFT) +file_m = Menu(file) +file['menu'] = file_m +file_m_apps = Menu(file_m, tearoff=0) +file_m.add_cascade(label='Select Application', underline=0, + menu=file_m_apps) +file_m.add_command(label='Quit', underline=0, command=sys.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 + t.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 + t.setvar('tk_priv(selectMode)', 'word') + t.tk_textSelectTo(At(x, y)) +t.bind('<Double-1>', double1) + +def triple1(e): + x = e.x + y = e.y + t.setvar('tk_priv(selectMode)', 'line') + t.tk_textSelectTo(At(x, y)) +t.bind('<Triple-1>', triple1) + +def returnkey(e): + t.insert(AtInsert(), '\n') + invoke() +t.bind('<Return>', returnkey) + +def controlv(e): + t.insert(AtInsert(), t.selection_get()) + t.yview_pickplace(AtInsert()) + if t.index(AtInsert())[-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', AtInsert()) + t.yview_pickplace(AtInsert()) +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', AtInsert()) + if t.getboolean(tk.call('info', 'complete', cmd)): # XXX + if app == root.winfo_name(): + msg = tk.call('eval', cmd) # XXX + else: + msg = t.send(app, cmd) + if msg: + t.insert(AtInsert(), msg + '\n') + prompt() + t.yview_pickplace(AtInsert()) + +def prompt(): + t.insert(AtInsert(), 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') + +def fillAppsMenu(): + file_m_apps.add('command') + file_m_apps.delete(0, 'last') + names = root.winfo_interps() + names = map(None, names) # convert tuple to list + names.sort() + for name in names: + try: + root.send(name, 'winfo name .') + except TclError: + # Inoperative window -- ignore it + pass + else: + file_m_apps.add_command( + label=name, + command=lambda name=name: newApp(name)) + +file_m_apps['postcommand'] = fillAppsMenu +mBar.tk_menuBar(file) + +# 7. Miscellaneous initialization. + +app = root.winfo_name() +prompt() +t.focus() + +root.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/solitaire.py b/sys/src/cmd/python/Demo/tkinter/guido/solitaire.py new file mode 100755 index 000000000..50a8b2643 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/solitaire.py @@ -0,0 +1,637 @@ +#! /usr/bin/env python + +"""Solitaire game, much like the one that comes with MS Windows. + +Limitations: + +- No cute graphical images for the playing cards faces or backs. +- No scoring or timer. +- No undo. +- No option to turn 3 cards at a time. +- No keyboard shortcuts. +- Less fancy animation when you win. +- The determination of which stack you drag to is more relaxed. + +Apology: + +I'm not much of a card player, so my terminology in these comments may +at times be a little unusual. If you have suggestions, please let me +know! + +""" + +# Imports + +import math +import random + +from Tkinter import * +from Canvas import Rectangle, CanvasText, Group, Window + + +# Fix a bug in Canvas.Group as distributed in Python 1.4. The +# distributed bind() method is broken. Rather than asking you to fix +# the source, we fix it here by deriving a subclass: + +class Group(Group): + def bind(self, sequence=None, command=None): + return self.canvas.tag_bind(self.id, sequence, command) + + +# Constants determining the size and lay-out of cards and stacks. We +# work in a "grid" where each card/stack is surrounded by MARGIN +# pixels of space on each side, so adjacent stacks are separated by +# 2*MARGIN pixels. OFFSET is the offset used for displaying the +# face down cards in the row stacks. + +CARDWIDTH = 100 +CARDHEIGHT = 150 +MARGIN = 10 +XSPACING = CARDWIDTH + 2*MARGIN +YSPACING = CARDHEIGHT + 4*MARGIN +OFFSET = 5 + +# The background color, green to look like a playing table. The +# standard green is way too bright, and dark green is way to dark, so +# we use something in between. (There are a few more colors that +# could be customized, but they are less controversial.) + +BACKGROUND = '#070' + + +# Suits and colors. The values of the symbolic suit names are the +# strings used to display them (you change these and VALNAMES to +# internationalize the game). The COLOR dictionary maps suit names to +# colors (red and black) which must be Tk color names. The keys() of +# the COLOR dictionary conveniently provides us with a list of all +# suits (in arbitrary order). + +HEARTS = 'Heart' +DIAMONDS = 'Diamond' +CLUBS = 'Club' +SPADES = 'Spade' + +RED = 'red' +BLACK = 'black' + +COLOR = {} +for s in (HEARTS, DIAMONDS): + COLOR[s] = RED +for s in (CLUBS, SPADES): + COLOR[s] = BLACK + +ALLSUITS = COLOR.keys() +NSUITS = len(ALLSUITS) + + +# Card values are 1-13. We also define symbolic names for the picture +# cards. ALLVALUES is a list of all card values. + +ACE = 1 +JACK = 11 +QUEEN = 12 +KING = 13 +ALLVALUES = range(1, 14) # (one more than the highest value) +NVALUES = len(ALLVALUES) + + +# VALNAMES is a list that maps a card value to string. It contains a +# dummy element at index 0 so it can be indexed directly with the card +# value. + +VALNAMES = ["", "A"] + map(str, range(2, 11)) + ["J", "Q", "K"] + + +# Solitaire constants. The only one I can think of is the number of +# row stacks. + +NROWS = 7 + + +# The rest of the program consists of class definitions. These are +# further described in their documentation strings. + + +class Card: + + """A playing card. + + A card doesn't record to which stack it belongs; only the stack + records this (it turns out that we always know this from the + context, and this saves a ``double update'' with potential for + inconsistencies). + + Public methods: + + moveto(x, y) -- move the card to an absolute position + moveby(dx, dy) -- move the card by a relative offset + tkraise() -- raise the card to the top of its stack + showface(), showback() -- turn the card face up or down & raise it + + Public read-only instance variables: + + suit, value, color -- the card's suit, value and color + face_shown -- true when the card is shown face up, else false + + Semi-public read-only instance variables (XXX should be made + private): + + group -- the Canvas.Group representing the card + x, y -- the position of the card's top left corner + + Private instance variables: + + __back, __rect, __text -- the canvas items making up the card + + (To show the card face up, the text item is placed in front of + rect and the back is placed behind it. To show it face down, this + is reversed. The card is created face down.) + + """ + + def __init__(self, suit, value, canvas): + """Card constructor. + + Arguments are the card's suit and value, and the canvas widget. + + The card is created at position (0, 0), with its face down + (adding it to a stack will position it according to that + stack's rules). + + """ + self.suit = suit + self.value = value + self.color = COLOR[suit] + self.face_shown = 0 + + self.x = self.y = 0 + self.group = Group(canvas) + + text = "%s %s" % (VALNAMES[value], suit) + self.__text = CanvasText(canvas, CARDWIDTH/2, 0, + anchor=N, fill=self.color, text=text) + self.group.addtag_withtag(self.__text) + + self.__rect = Rectangle(canvas, 0, 0, CARDWIDTH, CARDHEIGHT, + outline='black', fill='white') + self.group.addtag_withtag(self.__rect) + + self.__back = Rectangle(canvas, MARGIN, MARGIN, + CARDWIDTH-MARGIN, CARDHEIGHT-MARGIN, + outline='black', fill='blue') + self.group.addtag_withtag(self.__back) + + def __repr__(self): + """Return a string for debug print statements.""" + return "Card(%r, %r)" % (self.suit, self.value) + + def moveto(self, x, y): + """Move the card to absolute position (x, y).""" + self.moveby(x - self.x, y - self.y) + + def moveby(self, dx, dy): + """Move the card by (dx, dy).""" + self.x = self.x + dx + self.y = self.y + dy + self.group.move(dx, dy) + + def tkraise(self): + """Raise the card above all other objects in its canvas.""" + self.group.tkraise() + + def showface(self): + """Turn the card's face up.""" + self.tkraise() + self.__rect.tkraise() + self.__text.tkraise() + self.face_shown = 1 + + def showback(self): + """Turn the card's face down.""" + self.tkraise() + self.__rect.tkraise() + self.__back.tkraise() + self.face_shown = 0 + + +class Stack: + + """A generic stack of cards. + + This is used as a base class for all other stacks (e.g. the deck, + the suit stacks, and the row stacks). + + Public methods: + + add(card) -- add a card to the stack + delete(card) -- delete a card from the stack + showtop() -- show the top card (if any) face up + deal() -- delete and return the top card, or None if empty + + Method that subclasses may override: + + position(card) -- move the card to its proper (x, y) position + + The default position() method places all cards at the stack's + own (x, y) position. + + userclickhandler(), userdoubleclickhandler() -- called to do + subclass specific things on single and double clicks + + The default user (single) click handler shows the top card + face up. The default user double click handler calls the user + single click handler. + + usermovehandler(cards) -- called to complete a subpile move + + The default user move handler moves all moved cards back to + their original position (by calling the position() method). + + Private methods: + + clickhandler(event), doubleclickhandler(event), + motionhandler(event), releasehandler(event) -- event handlers + + The default event handlers turn the top card of the stack with + its face up on a (single or double) click, and also support + moving a subpile around. + + startmoving(event) -- begin a move operation + finishmoving() -- finish a move operation + + """ + + def __init__(self, x, y, game=None): + """Stack constructor. + + Arguments are the stack's nominal x and y position (the top + left corner of the first card placed in the stack), and the + game object (which is used to get the canvas; subclasses use + the game object to find other stacks). + + """ + self.x = x + self.y = y + self.game = game + self.cards = [] + self.group = Group(self.game.canvas) + self.group.bind('<1>', self.clickhandler) + self.group.bind('<Double-1>', self.doubleclickhandler) + self.group.bind('<B1-Motion>', self.motionhandler) + self.group.bind('<ButtonRelease-1>', self.releasehandler) + self.makebottom() + + def makebottom(self): + pass + + def __repr__(self): + """Return a string for debug print statements.""" + return "%s(%d, %d)" % (self.__class__.__name__, self.x, self.y) + + # Public methods + + def add(self, card): + self.cards.append(card) + card.tkraise() + self.position(card) + self.group.addtag_withtag(card.group) + + def delete(self, card): + self.cards.remove(card) + card.group.dtag(self.group) + + def showtop(self): + if self.cards: + self.cards[-1].showface() + + def deal(self): + if not self.cards: + return None + card = self.cards[-1] + self.delete(card) + return card + + # Subclass overridable methods + + def position(self, card): + card.moveto(self.x, self.y) + + def userclickhandler(self): + self.showtop() + + def userdoubleclickhandler(self): + self.userclickhandler() + + def usermovehandler(self, cards): + for card in cards: + self.position(card) + + # Event handlers + + def clickhandler(self, event): + self.finishmoving() # In case we lost an event + self.userclickhandler() + self.startmoving(event) + + def motionhandler(self, event): + self.keepmoving(event) + + def releasehandler(self, event): + self.keepmoving(event) + self.finishmoving() + + def doubleclickhandler(self, event): + self.finishmoving() # In case we lost an event + self.userdoubleclickhandler() + self.startmoving(event) + + # Move internals + + moving = None + + def startmoving(self, event): + self.moving = None + tags = self.game.canvas.gettags('current') + for i in range(len(self.cards)): + card = self.cards[i] + if card.group.tag in tags: + break + else: + return + if not card.face_shown: + return + self.moving = self.cards[i:] + self.lastx = event.x + self.lasty = event.y + for card in self.moving: + card.tkraise() + + def keepmoving(self, event): + if not self.moving: + return + dx = event.x - self.lastx + dy = event.y - self.lasty + self.lastx = event.x + self.lasty = event.y + if dx or dy: + for card in self.moving: + card.moveby(dx, dy) + + def finishmoving(self): + cards = self.moving + self.moving = None + if cards: + self.usermovehandler(cards) + + +class Deck(Stack): + + """The deck is a stack with support for shuffling. + + New methods: + + fill() -- create the playing cards + shuffle() -- shuffle the playing cards + + A single click moves the top card to the game's open deck and + moves it face up; if we're out of cards, it moves the open deck + back to the deck. + + """ + + def makebottom(self): + bottom = Rectangle(self.game.canvas, + self.x, self.y, + self.x+CARDWIDTH, self.y+CARDHEIGHT, + outline='black', fill=BACKGROUND) + self.group.addtag_withtag(bottom) + + def fill(self): + for suit in ALLSUITS: + for value in ALLVALUES: + self.add(Card(suit, value, self.game.canvas)) + + def shuffle(self): + n = len(self.cards) + newcards = [] + for i in randperm(n): + newcards.append(self.cards[i]) + self.cards = newcards + + def userclickhandler(self): + opendeck = self.game.opendeck + card = self.deal() + if not card: + while 1: + card = opendeck.deal() + if not card: + break + self.add(card) + card.showback() + else: + self.game.opendeck.add(card) + card.showface() + + +def randperm(n): + """Function returning a random permutation of range(n).""" + r = range(n) + x = [] + while r: + i = random.choice(r) + x.append(i) + r.remove(i) + return x + + +class OpenStack(Stack): + + def acceptable(self, cards): + return 0 + + def usermovehandler(self, cards): + card = cards[0] + stack = self.game.closeststack(card) + if not stack or stack is self or not stack.acceptable(cards): + Stack.usermovehandler(self, cards) + else: + for card in cards: + self.delete(card) + stack.add(card) + self.game.wincheck() + + def userdoubleclickhandler(self): + if not self.cards: + return + card = self.cards[-1] + if not card.face_shown: + self.userclickhandler() + return + for s in self.game.suits: + if s.acceptable([card]): + self.delete(card) + s.add(card) + self.game.wincheck() + break + + +class SuitStack(OpenStack): + + def makebottom(self): + bottom = Rectangle(self.game.canvas, + self.x, self.y, + self.x+CARDWIDTH, self.y+CARDHEIGHT, + outline='black', fill='') + + def userclickhandler(self): + pass + + def userdoubleclickhandler(self): + pass + + def acceptable(self, cards): + if len(cards) != 1: + return 0 + card = cards[0] + if not self.cards: + return card.value == ACE + topcard = self.cards[-1] + return card.suit == topcard.suit and card.value == topcard.value + 1 + + +class RowStack(OpenStack): + + def acceptable(self, cards): + card = cards[0] + if not self.cards: + return card.value == KING + topcard = self.cards[-1] + if not topcard.face_shown: + return 0 + return card.color != topcard.color and card.value == topcard.value - 1 + + def position(self, card): + y = self.y + for c in self.cards: + if c == card: + break + if c.face_shown: + y = y + 2*MARGIN + else: + y = y + OFFSET + card.moveto(self.x, y) + + +class Solitaire: + + def __init__(self, master): + self.master = master + + self.canvas = Canvas(self.master, + background=BACKGROUND, + highlightthickness=0, + width=NROWS*XSPACING, + height=3*YSPACING + 20 + MARGIN) + self.canvas.pack(fill=BOTH, expand=TRUE) + + self.dealbutton = Button(self.canvas, + text="Deal", + highlightthickness=0, + background=BACKGROUND, + activebackground="green", + command=self.deal) + Window(self.canvas, MARGIN, 3*YSPACING + 20, + window=self.dealbutton, anchor=SW) + + x = MARGIN + y = MARGIN + + self.deck = Deck(x, y, self) + + x = x + XSPACING + self.opendeck = OpenStack(x, y, self) + + x = x + XSPACING + self.suits = [] + for i in range(NSUITS): + x = x + XSPACING + self.suits.append(SuitStack(x, y, self)) + + x = MARGIN + y = y + YSPACING + + self.rows = [] + for i in range(NROWS): + self.rows.append(RowStack(x, y, self)) + x = x + XSPACING + + self.openstacks = [self.opendeck] + self.suits + self.rows + + self.deck.fill() + self.deal() + + def wincheck(self): + for s in self.suits: + if len(s.cards) != NVALUES: + return + self.win() + self.deal() + + def win(self): + """Stupid animation when you win.""" + cards = [] + for s in self.openstacks: + cards = cards + s.cards + while cards: + card = random.choice(cards) + cards.remove(card) + self.animatedmoveto(card, self.deck) + + def animatedmoveto(self, card, dest): + for i in range(10, 0, -1): + dx, dy = (dest.x-card.x)/i, (dest.y-card.y)/i + card.moveby(dx, dy) + self.master.update_idletasks() + + def closeststack(self, card): + closest = None + cdist = 999999999 + # Since we only compare distances, + # we don't bother to take the square root. + for stack in self.openstacks: + dist = (stack.x - card.x)**2 + (stack.y - card.y)**2 + if dist < cdist: + closest = stack + cdist = dist + return closest + + def deal(self): + self.reset() + self.deck.shuffle() + for i in range(NROWS): + for r in self.rows[i:]: + card = self.deck.deal() + r.add(card) + for r in self.rows: + r.showtop() + + def reset(self): + for stack in self.openstacks: + while 1: + card = stack.deal() + if not card: + break + self.deck.add(card) + card.showback() + + +# Main function, run when invoked as a stand-alone Python program. + +def main(): + root = Tk() + game = Solitaire(root) + root.protocol('WM_DELETE_WINDOW', root.quit) + root.mainloop() + +if __name__ == '__main__': + main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/sortvisu.py b/sys/src/cmd/python/Demo/tkinter/guido/sortvisu.py new file mode 100644 index 000000000..f18f2c116 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/sortvisu.py @@ -0,0 +1,634 @@ +#! /usr/bin/env python + +"""Sorting algorithms visualizer using Tkinter. + +This module is comprised of three ``components'': + +- an array visualizer with methods that implement basic sorting +operations (compare, swap) as well as methods for ``annotating'' the +sorting algorithm (e.g. to show the pivot element); + +- a number of sorting algorithms (currently quicksort, insertion sort, +selection sort and bubble sort, as well as a randomization function), +all using the array visualizer for its basic operations and with calls +to its annotation methods; + +- and a ``driver'' class which can be used as a Grail applet or as a +stand-alone application. + +""" + + +from Tkinter import * +from Canvas import Line, Rectangle +import random + + +XGRID = 10 +YGRID = 10 +WIDTH = 6 + + +class Array: + + def __init__(self, master, data=None): + self.master = master + self.frame = Frame(self.master) + self.frame.pack(fill=X) + self.label = Label(self.frame) + self.label.pack() + self.canvas = Canvas(self.frame) + self.canvas.pack() + self.report = Label(self.frame) + self.report.pack() + self.left = Line(self.canvas, 0, 0, 0, 0) + self.right = Line(self.canvas, 0, 0, 0, 0) + self.pivot = Line(self.canvas, 0, 0, 0, 0) + self.items = [] + self.size = self.maxvalue = 0 + if data: + self.setdata(data) + + def setdata(self, data): + olditems = self.items + self.items = [] + for item in olditems: + item.delete() + self.size = len(data) + self.maxvalue = max(data) + self.canvas.config(width=(self.size+1)*XGRID, + height=(self.maxvalue+1)*YGRID) + for i in range(self.size): + self.items.append(ArrayItem(self, i, data[i])) + self.reset("Sort demo, size %d" % self.size) + + speed = "normal" + + def setspeed(self, speed): + self.speed = speed + + def destroy(self): + self.frame.destroy() + + in_mainloop = 0 + stop_mainloop = 0 + + def cancel(self): + self.stop_mainloop = 1 + if self.in_mainloop: + self.master.quit() + + def step(self): + if self.in_mainloop: + self.master.quit() + + Cancelled = "Array.Cancelled" # Exception + + def wait(self, msecs): + if self.speed == "fastest": + msecs = 0 + elif self.speed == "fast": + msecs = msecs/10 + elif self.speed == "single-step": + msecs = 1000000000 + if not self.stop_mainloop: + self.master.update() + id = self.master.after(msecs, self.master.quit) + self.in_mainloop = 1 + self.master.mainloop() + self.master.after_cancel(id) + self.in_mainloop = 0 + if self.stop_mainloop: + self.stop_mainloop = 0 + self.message("Cancelled") + raise Array.Cancelled + + def getsize(self): + return self.size + + def show_partition(self, first, last): + for i in range(self.size): + item = self.items[i] + if first <= i < last: + item.item.config(fill='red') + else: + item.item.config(fill='orange') + self.hide_left_right_pivot() + + def hide_partition(self): + for i in range(self.size): + item = self.items[i] + item.item.config(fill='red') + self.hide_left_right_pivot() + + def show_left(self, left): + if not 0 <= left < self.size: + self.hide_left() + return + x1, y1, x2, y2 = self.items[left].position() +## top, bot = HIRO + self.left.coords([(x1-2, 0), (x1-2, 9999)]) + self.master.update() + + def show_right(self, right): + if not 0 <= right < self.size: + self.hide_right() + return + x1, y1, x2, y2 = self.items[right].position() + self.right.coords(((x2+2, 0), (x2+2, 9999))) + self.master.update() + + def hide_left_right_pivot(self): + self.hide_left() + self.hide_right() + self.hide_pivot() + + def hide_left(self): + self.left.coords(((0, 0), (0, 0))) + + def hide_right(self): + self.right.coords(((0, 0), (0, 0))) + + def show_pivot(self, pivot): + x1, y1, x2, y2 = self.items[pivot].position() + self.pivot.coords(((0, y1-2), (9999, y1-2))) + + def hide_pivot(self): + self.pivot.coords(((0, 0), (0, 0))) + + def swap(self, i, j): + if i == j: return + self.countswap() + item = self.items[i] + other = self.items[j] + self.items[i], self.items[j] = other, item + item.swapwith(other) + + def compare(self, i, j): + self.countcompare() + item = self.items[i] + other = self.items[j] + return item.compareto(other) + + def reset(self, msg): + self.ncompares = 0 + self.nswaps = 0 + self.message(msg) + self.updatereport() + self.hide_partition() + + def message(self, msg): + self.label.config(text=msg) + + def countswap(self): + self.nswaps = self.nswaps + 1 + self.updatereport() + + def countcompare(self): + self.ncompares = self.ncompares + 1 + self.updatereport() + + def updatereport(self): + text = "%d cmps, %d swaps" % (self.ncompares, self.nswaps) + self.report.config(text=text) + + +class ArrayItem: + + def __init__(self, array, index, value): + self.array = array + self.index = index + self.value = value + x1, y1, x2, y2 = self.position() + self.item = Rectangle(array.canvas, x1, y1, x2, y2, + fill='red', outline='black', width=1) + self.item.bind('<Button-1>', self.mouse_down) + self.item.bind('<Button1-Motion>', self.mouse_move) + self.item.bind('<ButtonRelease-1>', self.mouse_up) + + def delete(self): + item = self.item + self.array = None + self.item = None + item.delete() + + def mouse_down(self, event): + self.lastx = event.x + self.lasty = event.y + self.origx = event.x + self.origy = event.y + self.item.tkraise() + + def mouse_move(self, event): + self.item.move(event.x - self.lastx, event.y - self.lasty) + self.lastx = event.x + self.lasty = event.y + + def mouse_up(self, event): + i = self.nearestindex(event.x) + if i >= self.array.getsize(): + i = self.array.getsize() - 1 + if i < 0: + i = 0 + other = self.array.items[i] + here = self.index + self.array.items[here], self.array.items[i] = other, self + self.index = i + x1, y1, x2, y2 = self.position() + self.item.coords(((x1, y1), (x2, y2))) + other.setindex(here) + + def setindex(self, index): + nsteps = steps(self.index, index) + if not nsteps: return + if self.array.speed == "fastest": + nsteps = 0 + oldpts = self.position() + self.index = index + newpts = self.position() + trajectory = interpolate(oldpts, newpts, nsteps) + self.item.tkraise() + for pts in trajectory: + self.item.coords((pts[:2], pts[2:])) + self.array.wait(50) + + def swapwith(self, other): + nsteps = steps(self.index, other.index) + if not nsteps: return + if self.array.speed == "fastest": + nsteps = 0 + myoldpts = self.position() + otheroldpts = other.position() + self.index, other.index = other.index, self.index + mynewpts = self.position() + othernewpts = other.position() + myfill = self.item['fill'] + otherfill = other.item['fill'] + self.item.config(fill='green') + other.item.config(fill='yellow') + self.array.master.update() + if self.array.speed == "single-step": + self.item.coords((mynewpts[:2], mynewpts[2:])) + other.item.coords((othernewpts[:2], othernewpts[2:])) + self.array.master.update() + self.item.config(fill=myfill) + other.item.config(fill=otherfill) + self.array.wait(0) + return + mytrajectory = interpolate(myoldpts, mynewpts, nsteps) + othertrajectory = interpolate(otheroldpts, othernewpts, nsteps) + if self.value > other.value: + self.item.tkraise() + other.item.tkraise() + else: + other.item.tkraise() + self.item.tkraise() + try: + for i in range(len(mytrajectory)): + mypts = mytrajectory[i] + otherpts = othertrajectory[i] + self.item.coords((mypts[:2], mypts[2:])) + other.item.coords((otherpts[:2], otherpts[2:])) + self.array.wait(50) + finally: + mypts = mytrajectory[-1] + otherpts = othertrajectory[-1] + self.item.coords((mypts[:2], mypts[2:])) + other.item.coords((otherpts[:2], otherpts[2:])) + self.item.config(fill=myfill) + other.item.config(fill=otherfill) + + def compareto(self, other): + myfill = self.item['fill'] + otherfill = other.item['fill'] + outcome = cmp(self.value, other.value) + if outcome < 0: + myflash = 'white' + otherflash = 'black' + elif outcome > 0: + myflash = 'black' + otherflash = 'white' + else: + myflash = otherflash = 'grey' + try: + self.item.config(fill=myflash) + other.item.config(fill=otherflash) + self.array.wait(500) + finally: + self.item.config(fill=myfill) + other.item.config(fill=otherfill) + return outcome + + def position(self): + x1 = (self.index+1)*XGRID - WIDTH/2 + x2 = x1+WIDTH + y2 = (self.array.maxvalue+1)*YGRID + y1 = y2 - (self.value)*YGRID + return x1, y1, x2, y2 + + def nearestindex(self, x): + return int(round(float(x)/XGRID)) - 1 + + +# Subroutines that don't need an object + +def steps(here, there): + nsteps = abs(here - there) + if nsteps <= 3: + nsteps = nsteps * 3 + elif nsteps <= 5: + nsteps = nsteps * 2 + elif nsteps > 10: + nsteps = 10 + return nsteps + +def interpolate(oldpts, newpts, n): + if len(oldpts) != len(newpts): + raise ValueError, "can't interpolate arrays of different length" + pts = [0]*len(oldpts) + res = [tuple(oldpts)] + for i in range(1, n): + for k in range(len(pts)): + pts[k] = oldpts[k] + (newpts[k] - oldpts[k])*i/n + res.append(tuple(pts)) + res.append(tuple(newpts)) + return res + + +# Various (un)sorting algorithms + +def uniform(array): + size = array.getsize() + array.setdata([(size+1)/2] * size) + array.reset("Uniform data, size %d" % size) + +def distinct(array): + size = array.getsize() + array.setdata(range(1, size+1)) + array.reset("Distinct data, size %d" % size) + +def randomize(array): + array.reset("Randomizing") + n = array.getsize() + for i in range(n): + j = random.randint(0, n-1) + array.swap(i, j) + array.message("Randomized") + +def insertionsort(array): + size = array.getsize() + array.reset("Insertion sort") + for i in range(1, size): + j = i-1 + while j >= 0: + if array.compare(j, j+1) <= 0: + break + array.swap(j, j+1) + j = j-1 + array.message("Sorted") + +def selectionsort(array): + size = array.getsize() + array.reset("Selection sort") + try: + for i in range(size): + array.show_partition(i, size) + for j in range(i+1, size): + if array.compare(i, j) > 0: + array.swap(i, j) + array.message("Sorted") + finally: + array.hide_partition() + +def bubblesort(array): + size = array.getsize() + array.reset("Bubble sort") + for i in range(size): + for j in range(1, size): + if array.compare(j-1, j) > 0: + array.swap(j-1, j) + array.message("Sorted") + +def quicksort(array): + size = array.getsize() + array.reset("Quicksort") + try: + stack = [(0, size)] + while stack: + first, last = stack[-1] + del stack[-1] + array.show_partition(first, last) + if last-first < 5: + array.message("Insertion sort") + for i in range(first+1, last): + j = i-1 + while j >= first: + if array.compare(j, j+1) <= 0: + break + array.swap(j, j+1) + j = j-1 + continue + array.message("Choosing pivot") + j, i, k = first, (first+last)/2, last-1 + if array.compare(k, i) < 0: + array.swap(k, i) + if array.compare(k, j) < 0: + array.swap(k, j) + if array.compare(j, i) < 0: + array.swap(j, i) + pivot = j + array.show_pivot(pivot) + array.message("Pivot at left of partition") + array.wait(1000) + left = first + right = last + while 1: + array.message("Sweep right pointer") + right = right-1 + array.show_right(right) + while right > first and array.compare(right, pivot) >= 0: + right = right-1 + array.show_right(right) + array.message("Sweep left pointer") + left = left+1 + array.show_left(left) + while left < last and array.compare(left, pivot) <= 0: + left = left+1 + array.show_left(left) + if left > right: + array.message("End of partition") + break + array.message("Swap items") + array.swap(left, right) + array.message("Swap pivot back") + array.swap(pivot, right) + n1 = right-first + n2 = last-left + if n1 > 1: stack.append((first, right)) + if n2 > 1: stack.append((left, last)) + array.message("Sorted") + finally: + array.hide_partition() + +def demosort(array): + while 1: + for alg in [quicksort, insertionsort, selectionsort, bubblesort]: + randomize(array) + alg(array) + + +# Sort demo class -- usable as a Grail applet + +class SortDemo: + + def __init__(self, master, size=15): + self.master = master + self.size = size + self.busy = 0 + self.array = Array(self.master) + + self.botframe = Frame(master) + self.botframe.pack(side=BOTTOM) + self.botleftframe = Frame(self.botframe) + self.botleftframe.pack(side=LEFT, fill=Y) + self.botrightframe = Frame(self.botframe) + self.botrightframe.pack(side=RIGHT, fill=Y) + + self.b_qsort = Button(self.botleftframe, + text="Quicksort", command=self.c_qsort) + self.b_qsort.pack(fill=X) + self.b_isort = Button(self.botleftframe, + text="Insertion sort", command=self.c_isort) + self.b_isort.pack(fill=X) + self.b_ssort = Button(self.botleftframe, + text="Selection sort", command=self.c_ssort) + self.b_ssort.pack(fill=X) + self.b_bsort = Button(self.botleftframe, + text="Bubble sort", command=self.c_bsort) + self.b_bsort.pack(fill=X) + + # Terrible hack to overcome limitation of OptionMenu... + class MyIntVar(IntVar): + def __init__(self, master, demo): + self.demo = demo + IntVar.__init__(self, master) + def set(self, value): + IntVar.set(self, value) + if str(value) != '0': + self.demo.resize(value) + + self.v_size = MyIntVar(self.master, self) + self.v_size.set(size) + sizes = [1, 2, 3, 4] + range(5, 55, 5) + if self.size not in sizes: + sizes.append(self.size) + sizes.sort() + self.m_size = apply(OptionMenu, + (self.botleftframe, self.v_size) + tuple(sizes)) + self.m_size.pack(fill=X) + + self.v_speed = StringVar(self.master) + self.v_speed.set("normal") + self.m_speed = OptionMenu(self.botleftframe, self.v_speed, + "single-step", "normal", "fast", "fastest") + self.m_speed.pack(fill=X) + + self.b_step = Button(self.botleftframe, + text="Step", command=self.c_step) + self.b_step.pack(fill=X) + + self.b_randomize = Button(self.botrightframe, + text="Randomize", command=self.c_randomize) + self.b_randomize.pack(fill=X) + self.b_uniform = Button(self.botrightframe, + text="Uniform", command=self.c_uniform) + self.b_uniform.pack(fill=X) + self.b_distinct = Button(self.botrightframe, + text="Distinct", command=self.c_distinct) + self.b_distinct.pack(fill=X) + self.b_demo = Button(self.botrightframe, + text="Demo", command=self.c_demo) + self.b_demo.pack(fill=X) + self.b_cancel = Button(self.botrightframe, + text="Cancel", command=self.c_cancel) + self.b_cancel.pack(fill=X) + self.b_cancel.config(state=DISABLED) + self.b_quit = Button(self.botrightframe, + text="Quit", command=self.c_quit) + self.b_quit.pack(fill=X) + + def resize(self, newsize): + if self.busy: + self.master.bell() + return + self.size = newsize + self.array.setdata(range(1, self.size+1)) + + def c_qsort(self): + self.run(quicksort) + + def c_isort(self): + self.run(insertionsort) + + def c_ssort(self): + self.run(selectionsort) + + def c_bsort(self): + self.run(bubblesort) + + def c_demo(self): + self.run(demosort) + + def c_randomize(self): + self.run(randomize) + + def c_uniform(self): + self.run(uniform) + + def c_distinct(self): + self.run(distinct) + + def run(self, func): + if self.busy: + self.master.bell() + return + self.busy = 1 + self.array.setspeed(self.v_speed.get()) + self.b_cancel.config(state=NORMAL) + try: + func(self.array) + except Array.Cancelled: + pass + self.b_cancel.config(state=DISABLED) + self.busy = 0 + + def c_cancel(self): + if not self.busy: + self.master.bell() + return + self.array.cancel() + + def c_step(self): + if not self.busy: + self.master.bell() + return + self.v_speed.set("single-step") + self.array.setspeed("single-step") + self.array.step() + + def c_quit(self): + if self.busy: + self.array.cancel() + self.master.after_idle(self.master.quit) + + +# Main program -- for stand-alone operation outside Grail + +def main(): + root = Tk() + demo = SortDemo(root) + root.protocol('WM_DELETE_WINDOW', demo.c_quit) + root.mainloop() + +if __name__ == '__main__': + main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/ss1.py b/sys/src/cmd/python/Demo/tkinter/guido/ss1.py new file mode 100644 index 000000000..89354753e --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/ss1.py @@ -0,0 +1,845 @@ +"""SS1 -- a spreadsheet.""" + +import os +import re +import sys +import cgi +import rexec +from xml.parsers import expat + +LEFT, CENTER, RIGHT = "LEFT", "CENTER", "RIGHT" + +def ljust(x, n): + return x.ljust(n) +def center(x, n): + return x.center(n) +def rjust(x, n): + return x.rjust(n) +align2action = {LEFT: ljust, CENTER: center, RIGHT: rjust} + +align2xml = {LEFT: "left", CENTER: "center", RIGHT: "right"} +xml2align = {"left": LEFT, "center": CENTER, "right": RIGHT} + +align2anchor = {LEFT: "w", CENTER: "center", RIGHT: "e"} + +def sum(seq): + total = 0 + for x in seq: + if x is not None: + total += x + return total + +class Sheet: + + def __init__(self): + self.cells = {} # {(x, y): cell, ...} + self.rexec = rexec.RExec() + m = self.rexec.add_module('__main__') + m.cell = self.cellvalue + m.cells = self.multicellvalue + m.sum = sum + + def cellvalue(self, x, y): + cell = self.getcell(x, y) + if hasattr(cell, 'recalc'): + return cell.recalc(self.rexec) + else: + return cell + + def multicellvalue(self, x1, y1, x2, y2): + if x1 > x2: + x1, x2 = x2, x1 + if y1 > y2: + y1, y2 = y2, y1 + seq = [] + for y in range(y1, y2+1): + for x in range(x1, x2+1): + seq.append(self.cellvalue(x, y)) + return seq + + def getcell(self, x, y): + return self.cells.get((x, y)) + + def setcell(self, x, y, cell): + assert x > 0 and y > 0 + assert isinstance(cell, BaseCell) + self.cells[x, y] = cell + + def clearcell(self, x, y): + try: + del self.cells[x, y] + except KeyError: + pass + + def clearcells(self, x1, y1, x2, y2): + for xy in self.selectcells(x1, y1, x2, y2): + del self.cells[xy] + + def clearrows(self, y1, y2): + self.clearcells(0, y1, sys.maxint, y2) + + def clearcolumns(self, x1, x2): + self.clearcells(x1, 0, x2, sys.maxint) + + def selectcells(self, x1, y1, x2, y2): + if x1 > x2: + x1, x2 = x2, x1 + if y1 > y2: + y1, y2 = y2, y1 + return [(x, y) for x, y in self.cells + if x1 <= x <= x2 and y1 <= y <= y2] + + def movecells(self, x1, y1, x2, y2, dx, dy): + if dx == 0 and dy == 0: + return + if x1 > x2: + x1, x2 = x2, x1 + if y1 > y2: + y1, y2 = y2, y1 + assert x1+dx > 0 and y1+dy > 0 + new = {} + for x, y in self.cells: + cell = self.cells[x, y] + if hasattr(cell, 'renumber'): + cell = cell.renumber(x1, y1, x2, y2, dx, dy) + if x1 <= x <= x2 and y1 <= y <= y2: + x += dx + y += dy + new[x, y] = cell + self.cells = new + + def insertrows(self, y, n): + assert n > 0 + self.movecells(0, y, sys.maxint, sys.maxint, 0, n) + + def deleterows(self, y1, y2): + if y1 > y2: + y1, y2 = y2, y1 + self.clearrows(y1, y2) + self.movecells(0, y2+1, sys.maxint, sys.maxint, 0, y1-y2-1) + + def insertcolumns(self, x, n): + assert n > 0 + self.movecells(x, 0, sys.maxint, sys.maxint, n, 0) + + def deletecolumns(self, x1, x2): + if x1 > x2: + x1, x2 = x2, x1 + self.clearcells(x1, x2) + self.movecells(x2+1, 0, sys.maxint, sys.maxint, x1-x2-1, 0) + + def getsize(self): + maxx = maxy = 0 + for x, y in self.cells: + maxx = max(maxx, x) + maxy = max(maxy, y) + return maxx, maxy + + def reset(self): + for cell in self.cells.itervalues(): + if hasattr(cell, 'reset'): + cell.reset() + + def recalc(self): + self.reset() + for cell in self.cells.itervalues(): + if hasattr(cell, 'recalc'): + cell.recalc(self.rexec) + + def display(self): + maxx, maxy = self.getsize() + width, height = maxx+1, maxy+1 + colwidth = [1] * width + full = {} + # Add column heading labels in row 0 + for x in range(1, width): + full[x, 0] = text, alignment = colnum2name(x), RIGHT + colwidth[x] = max(colwidth[x], len(text)) + # Add row labels in column 0 + for y in range(1, height): + full[0, y] = text, alignment = str(y), RIGHT + colwidth[0] = max(colwidth[0], len(text)) + # Add sheet cells in columns with x>0 and y>0 + for (x, y), cell in self.cells.iteritems(): + if x <= 0 or y <= 0: + continue + if hasattr(cell, 'recalc'): + cell.recalc(self.rexec) + if hasattr(cell, 'format'): + text, alignment = cell.format() + assert isinstance(text, str) + assert alignment in (LEFT, CENTER, RIGHT) + else: + text = str(cell) + if isinstance(cell, str): + alignment = LEFT + else: + alignment = RIGHT + full[x, y] = (text, alignment) + colwidth[x] = max(colwidth[x], len(text)) + # Calculate the horizontal separator line (dashes and dots) + sep = "" + for x in range(width): + if sep: + sep += "+" + sep += "-"*colwidth[x] + # Now print The full grid + for y in range(height): + line = "" + for x in range(width): + text, alignment = full.get((x, y)) or ("", LEFT) + text = align2action[alignment](text, colwidth[x]) + if line: + line += '|' + line += text + print line + if y == 0: + print sep + + def xml(self): + out = ['<spreadsheet>'] + for (x, y), cell in self.cells.iteritems(): + if hasattr(cell, 'xml'): + cellxml = cell.xml() + else: + cellxml = '<value>%s</value>' % cgi.escape(cell) + out.append('<cell row="%s" col="%s">\n %s\n</cell>' % + (y, x, cellxml)) + out.append('</spreadsheet>') + return '\n'.join(out) + + def save(self, filename): + text = self.xml() + f = open(filename, "w") + f.write(text) + if text and not text.endswith('\n'): + f.write('\n') + f.close() + + def load(self, filename): + f = open(filename, 'r') + SheetParser(self).parsefile(f) + f.close() + +class SheetParser: + + def __init__(self, sheet): + self.sheet = sheet + + def parsefile(self, f): + parser = expat.ParserCreate() + parser.StartElementHandler = self.startelement + parser.EndElementHandler = self.endelement + parser.CharacterDataHandler = self.data + parser.ParseFile(f) + + def startelement(self, tag, attrs): + method = getattr(self, 'start_'+tag, None) + if method: + for key, value in attrs.iteritems(): + attrs[key] = str(value) # XXX Convert Unicode to 8-bit + method(attrs) + self.texts = [] + + def data(self, text): + text = str(text) # XXX Convert Unicode to 8-bit + self.texts.append(text) + + def endelement(self, tag): + method = getattr(self, 'end_'+tag, None) + if method: + method("".join(self.texts)) + + def start_cell(self, attrs): + self.y = int(attrs.get("row")) + self.x = int(attrs.get("col")) + + def start_value(self, attrs): + self.fmt = attrs.get('format') + self.alignment = xml2align.get(attrs.get('align')) + + start_formula = start_value + + def end_int(self, text): + try: + self.value = int(text) + except: + self.value = None + + def end_long(self, text): + try: + self.value = long(text) + except: + self.value = None + + def end_double(self, text): + try: + self.value = float(text) + except: + self.value = None + + def end_complex(self, text): + try: + self.value = complex(text) + except: + self.value = None + + def end_string(self, text): + try: + self.value = text + except: + self.value = None + + def end_value(self, text): + if isinstance(self.value, BaseCell): + self.cell = self.value + elif isinstance(self.value, str): + self.cell = StringCell(self.value, + self.fmt or "%s", + self.alignment or LEFT) + else: + self.cell = NumericCell(self.value, + self.fmt or "%s", + self.alignment or RIGHT) + + def end_formula(self, text): + self.cell = FormulaCell(text, + self.fmt or "%s", + self.alignment or RIGHT) + + def end_cell(self, text): + self.sheet.setcell(self.x, self.y, self.cell) + +class BaseCell: + __init__ = None # Must provide + """Abstract base class for sheet cells. + + Subclasses may but needn't provide the following APIs: + + cell.reset() -- prepare for recalculation + cell.recalc(rexec) -> value -- recalculate formula + cell.format() -> (value, alignment) -- return formatted value + cell.xml() -> string -- return XML + """ + +class NumericCell(BaseCell): + + def __init__(self, value, fmt="%s", alignment=RIGHT): + assert isinstance(value, (int, long, float, complex)) + assert alignment in (LEFT, CENTER, RIGHT) + self.value = value + self.fmt = fmt + self.alignment = alignment + + def recalc(self, rexec): + return self.value + + def format(self): + try: + text = self.fmt % self.value + except: + text = str(self.value) + return text, self.alignment + + def xml(self): + method = getattr(self, '_xml_' + type(self.value).__name__) + return '<value align="%s" format="%s">%s</value>' % ( + align2xml[self.alignment], + self.fmt, + method()) + + def _xml_int(self): + if -2**31 <= self.value < 2**31: + return '<int>%s</int>' % self.value + else: + return self._xml_long() + + def _xml_long(self): + return '<long>%s</long>' % self.value + + def _xml_float(self): + return '<double>%s</double>' % repr(self.value) + + def _xml_complex(self): + return '<complex>%s</double>' % repr(self.value) + +class StringCell(BaseCell): + + def __init__(self, text, fmt="%s", alignment=LEFT): + assert isinstance(text, (str, unicode)) + assert alignment in (LEFT, CENTER, RIGHT) + self.text = text + self.fmt = fmt + self.alignment = alignment + + def recalc(self, rexec): + return self.text + + def format(self): + return self.text, self.alignment + + def xml(self): + s = '<value align="%s" format="%s"><string>%s</string></value>' + return s % ( + align2xml[self.alignment], + self.fmt, + cgi.escape(self.text)) + +class FormulaCell(BaseCell): + + def __init__(self, formula, fmt="%s", alignment=RIGHT): + assert alignment in (LEFT, CENTER, RIGHT) + self.formula = formula + self.translated = translate(self.formula) + self.fmt = fmt + self.alignment = alignment + self.reset() + + def reset(self): + self.value = None + + def recalc(self, rexec): + if self.value is None: + try: + # A hack to evaluate expressions using true division + rexec.r_exec("from __future__ import division\n" + + "__value__ = eval(%s)" % repr(self.translated)) + self.value = rexec.r_eval("__value__") + except: + exc = sys.exc_info()[0] + if hasattr(exc, "__name__"): + self.value = exc.__name__ + else: + self.value = str(exc) + return self.value + + def format(self): + try: + text = self.fmt % self.value + except: + text = str(self.value) + return text, self.alignment + + def xml(self): + return '<formula align="%s" format="%s">%s</formula>' % ( + align2xml[self.alignment], + self.fmt, + self.formula) + + def renumber(self, x1, y1, x2, y2, dx, dy): + out = [] + for part in re.split('(\w+)', self.formula): + m = re.match('^([A-Z]+)([1-9][0-9]*)$', part) + if m is not None: + sx, sy = m.groups() + x = colname2num(sx) + y = int(sy) + if x1 <= x <= x2 and y1 <= y <= y2: + part = cellname(x+dx, y+dy) + out.append(part) + return FormulaCell("".join(out), self.fmt, self.alignment) + +def translate(formula): + """Translate a formula containing fancy cell names to valid Python code. + + Examples: + B4 -> cell(2, 4) + B4:Z100 -> cells(2, 4, 26, 100) + """ + out = [] + for part in re.split(r"(\w+(?::\w+)?)", formula): + m = re.match(r"^([A-Z]+)([1-9][0-9]*)(?::([A-Z]+)([1-9][0-9]*))?$", part) + if m is None: + out.append(part) + else: + x1, y1, x2, y2 = m.groups() + x1 = colname2num(x1) + if x2 is None: + s = "cell(%s, %s)" % (x1, y1) + else: + x2 = colname2num(x2) + s = "cells(%s, %s, %s, %s)" % (x1, y1, x2, y2) + out.append(s) + return "".join(out) + +def cellname(x, y): + "Translate a cell coordinate to a fancy cell name (e.g. (1, 1)->'A1')." + assert x > 0 # Column 0 has an empty name, so can't use that + return colnum2name(x) + str(y) + +def colname2num(s): + "Translate a column name to number (e.g. 'A'->1, 'Z'->26, 'AA'->27)." + s = s.upper() + n = 0 + for c in s: + assert 'A' <= c <= 'Z' + n = n*26 + ord(c) - ord('A') + 1 + return n + +def colnum2name(n): + "Translate a column number to name (e.g. 1->'A', etc.)." + assert n > 0 + s = "" + while n: + n, m = divmod(n-1, 26) + s = chr(m+ord('A')) + s + return s + +import Tkinter as Tk + +class SheetGUI: + + """Beginnings of a GUI for a spreadsheet. + + TO DO: + - clear multiple cells + - Insert, clear, remove rows or columns + - Show new contents while typing + - Scroll bars + - Grow grid when window is grown + - Proper menus + - Undo, redo + - Cut, copy and paste + - Formatting and alignment + """ + + def __init__(self, filename="sheet1.xml", rows=10, columns=5): + """Constructor. + + Load the sheet from the filename argument. + Set up the Tk widget tree. + """ + # Create and load the sheet + self.filename = filename + self.sheet = Sheet() + if os.path.isfile(filename): + self.sheet.load(filename) + # Calculate the needed grid size + maxx, maxy = self.sheet.getsize() + rows = max(rows, maxy) + columns = max(columns, maxx) + # Create the widgets + self.root = Tk.Tk() + self.root.wm_title("Spreadsheet: %s" % self.filename) + self.beacon = Tk.Label(self.root, text="A1", + font=('helvetica', 16, 'bold')) + self.entry = Tk.Entry(self.root) + self.savebutton = Tk.Button(self.root, text="Save", + command=self.save) + self.cellgrid = Tk.Frame(self.root) + # Configure the widget lay-out + self.cellgrid.pack(side="bottom", expand=1, fill="both") + self.beacon.pack(side="left") + self.savebutton.pack(side="right") + self.entry.pack(side="left", expand=1, fill="x") + # Bind some events + self.entry.bind("<Return>", self.return_event) + self.entry.bind("<Shift-Return>", self.shift_return_event) + self.entry.bind("<Tab>", self.tab_event) + self.entry.bind("<Shift-Tab>", self.shift_tab_event) + self.entry.bind("<Delete>", self.delete_event) + self.entry.bind("<Escape>", self.escape_event) + # Now create the cell grid + self.makegrid(rows, columns) + # Select the top-left cell + self.currentxy = None + self.cornerxy = None + self.setcurrent(1, 1) + # Copy the sheet cells to the GUI cells + self.sync() + + def delete_event(self, event): + if self.cornerxy != self.currentxy and self.cornerxy is not None: + self.sheet.clearcells(*(self.currentxy + self.cornerxy)) + else: + self.sheet.clearcell(*self.currentxy) + self.sync() + self.entry.delete(0, 'end') + return "break" + + def escape_event(self, event): + x, y = self.currentxy + self.load_entry(x, y) + + def load_entry(self, x, y): + cell = self.sheet.getcell(x, y) + if cell is None: + text = "" + elif isinstance(cell, FormulaCell): + text = '=' + cell.formula + else: + text, alignment = cell.format() + self.entry.delete(0, 'end') + self.entry.insert(0, text) + self.entry.selection_range(0, 'end') + + def makegrid(self, rows, columns): + """Helper to create the grid of GUI cells. + + The edge (x==0 or y==0) is filled with labels; the rest is real cells. + """ + self.rows = rows + self.columns = columns + self.gridcells = {} + # Create the top left corner cell (which selects all) + cell = Tk.Label(self.cellgrid, relief='raised') + cell.grid_configure(column=0, row=0, sticky='NSWE') + cell.bind("<ButtonPress-1>", self.selectall) + # Create the top row of labels, and confiure the grid columns + for x in range(1, columns+1): + self.cellgrid.grid_columnconfigure(x, minsize=64) + cell = Tk.Label(self.cellgrid, text=colnum2name(x), relief='raised') + cell.grid_configure(column=x, row=0, sticky='WE') + self.gridcells[x, 0] = cell + cell.__x = x + cell.__y = 0 + cell.bind("<ButtonPress-1>", self.selectcolumn) + cell.bind("<B1-Motion>", self.extendcolumn) + cell.bind("<ButtonRelease-1>", self.extendcolumn) + cell.bind("<Shift-Button-1>", self.extendcolumn) + # Create the leftmost column of labels + for y in range(1, rows+1): + cell = Tk.Label(self.cellgrid, text=str(y), relief='raised') + cell.grid_configure(column=0, row=y, sticky='WE') + self.gridcells[0, y] = cell + cell.__x = 0 + cell.__y = y + cell.bind("<ButtonPress-1>", self.selectrow) + cell.bind("<B1-Motion>", self.extendrow) + cell.bind("<ButtonRelease-1>", self.extendrow) + cell.bind("<Shift-Button-1>", self.extendrow) + # Create the real cells + for x in range(1, columns+1): + for y in range(1, rows+1): + cell = Tk.Label(self.cellgrid, relief='sunken', + bg='white', fg='black') + cell.grid_configure(column=x, row=y, sticky='NSWE') + self.gridcells[x, y] = cell + cell.__x = x + cell.__y = y + # Bind mouse events + cell.bind("<ButtonPress-1>", self.press) + cell.bind("<B1-Motion>", self.motion) + cell.bind("<ButtonRelease-1>", self.release) + cell.bind("<Shift-Button-1>", self.release) + + def selectall(self, event): + self.setcurrent(1, 1) + self.setcorner(sys.maxint, sys.maxint) + + def selectcolumn(self, event): + x, y = self.whichxy(event) + self.setcurrent(x, 1) + self.setcorner(x, sys.maxint) + + def extendcolumn(self, event): + x, y = self.whichxy(event) + if x > 0: + self.setcurrent(self.currentxy[0], 1) + self.setcorner(x, sys.maxint) + + def selectrow(self, event): + x, y = self.whichxy(event) + self.setcurrent(1, y) + self.setcorner(sys.maxint, y) + + def extendrow(self, event): + x, y = self.whichxy(event) + if y > 0: + self.setcurrent(1, self.currentxy[1]) + self.setcorner(sys.maxint, y) + + def press(self, event): + x, y = self.whichxy(event) + if x > 0 and y > 0: + self.setcurrent(x, y) + + def motion(self, event): + x, y = self.whichxy(event) + if x > 0 and y > 0: + self.setcorner(x, y) + + release = motion + + def whichxy(self, event): + w = self.cellgrid.winfo_containing(event.x_root, event.y_root) + if w is not None and isinstance(w, Tk.Label): + try: + return w.__x, w.__y + except AttributeError: + pass + return 0, 0 + + def save(self): + self.sheet.save(self.filename) + + def setcurrent(self, x, y): + "Make (x, y) the current cell." + if self.currentxy is not None: + self.change_cell() + self.clearfocus() + self.beacon['text'] = cellname(x, y) + self.load_entry(x, y) + self.entry.focus_set() + self.currentxy = x, y + self.cornerxy = None + gridcell = self.gridcells.get(self.currentxy) + if gridcell is not None: + gridcell['bg'] = 'yellow' + + def setcorner(self, x, y): + if self.currentxy is None or self.currentxy == (x, y): + self.setcurrent(x, y) + return + self.clearfocus() + self.cornerxy = x, y + x1, y1 = self.currentxy + x2, y2 = self.cornerxy or self.currentxy + if x1 > x2: + x1, x2 = x2, x1 + if y1 > y2: + y1, y2 = y2, y1 + for (x, y), cell in self.gridcells.iteritems(): + if x1 <= x <= x2 and y1 <= y <= y2: + cell['bg'] = 'lightBlue' + gridcell = self.gridcells.get(self.currentxy) + if gridcell is not None: + gridcell['bg'] = 'yellow' + self.setbeacon(x1, y1, x2, y2) + + def setbeacon(self, x1, y1, x2, y2): + if x1 == y1 == 1 and x2 == y2 == sys.maxint: + name = ":" + elif (x1, x2) == (1, sys.maxint): + if y1 == y2: + name = "%d" % y1 + else: + name = "%d:%d" % (y1, y2) + elif (y1, y2) == (1, sys.maxint): + if x1 == x2: + name = "%s" % colnum2name(x1) + else: + name = "%s:%s" % (colnum2name(x1), colnum2name(x2)) + else: + name1 = cellname(*self.currentxy) + name2 = cellname(*self.cornerxy) + name = "%s:%s" % (name1, name2) + self.beacon['text'] = name + + + def clearfocus(self): + if self.currentxy is not None: + x1, y1 = self.currentxy + x2, y2 = self.cornerxy or self.currentxy + if x1 > x2: + x1, x2 = x2, x1 + if y1 > y2: + y1, y2 = y2, y1 + for (x, y), cell in self.gridcells.iteritems(): + if x1 <= x <= x2 and y1 <= y <= y2: + cell['bg'] = 'white' + + def return_event(self, event): + "Callback for the Return key." + self.change_cell() + x, y = self.currentxy + self.setcurrent(x, y+1) + return "break" + + def shift_return_event(self, event): + "Callback for the Return key with Shift modifier." + self.change_cell() + x, y = self.currentxy + self.setcurrent(x, max(1, y-1)) + return "break" + + def tab_event(self, event): + "Callback for the Tab key." + self.change_cell() + x, y = self.currentxy + self.setcurrent(x+1, y) + return "break" + + def shift_tab_event(self, event): + "Callback for the Tab key with Shift modifier." + self.change_cell() + x, y = self.currentxy + self.setcurrent(max(1, x-1), y) + return "break" + + def change_cell(self): + "Set the current cell from the entry widget." + x, y = self.currentxy + text = self.entry.get() + cell = None + if text.startswith('='): + cell = FormulaCell(text[1:]) + else: + for cls in int, long, float, complex: + try: + value = cls(text) + except: + continue + else: + cell = NumericCell(value) + break + if cell is None and text: + cell = StringCell(text) + if cell is None: + self.sheet.clearcell(x, y) + else: + self.sheet.setcell(x, y, cell) + self.sync() + + def sync(self): + "Fill the GUI cells from the sheet cells." + self.sheet.recalc() + for (x, y), gridcell in self.gridcells.iteritems(): + if x == 0 or y == 0: + continue + cell = self.sheet.getcell(x, y) + if cell is None: + gridcell['text'] = "" + else: + if hasattr(cell, 'format'): + text, alignment = cell.format() + else: + text, alignment = str(cell), LEFT + gridcell['text'] = text + gridcell['anchor'] = align2anchor[alignment] + + +def test_basic(): + "Basic non-gui self-test." + import os + a = Sheet() + for x in range(1, 11): + for y in range(1, 11): + if x == 1: + cell = NumericCell(y) + elif y == 1: + cell = NumericCell(x) + else: + c1 = cellname(x, 1) + c2 = cellname(1, y) + formula = "%s*%s" % (c1, c2) + cell = FormulaCell(formula) + a.setcell(x, y, cell) +## if os.path.isfile("sheet1.xml"): +## print "Loading from sheet1.xml" +## a.load("sheet1.xml") + a.display() + a.save("sheet1.xml") + +def test_gui(): + "GUI test." + if sys.argv[1:]: + filename = sys.argv[1] + else: + filename = "sheet1.xml" + g = SheetGUI(filename) + g.root.mainloop() + +if __name__ == '__main__': + #test_basic() + test_gui() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/svkill.py b/sys/src/cmd/python/Demo/tkinter/guido/svkill.py new file mode 100755 index 000000000..69f7f3b16 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/svkill.py @@ -0,0 +1,128 @@ +#! /usr/bin/env python + +# Tkinter interface to SYSV `ps' and `kill' commands. + +from Tkinter import * + +if TkVersion < 4.0: + raise ImportError, "This version of svkill requires Tk 4.0 or later" + +from string import splitfields +from string import split +import commands +import os + +user = os.environ['LOGNAME'] + +class BarButton(Menubutton): + def __init__(self, master=None, **cnf): + apply(Menubutton.__init__, (self, master), cnf) + self.pack(side=LEFT) + self.menu = Menu(self, name='menu') + self['menu'] = self.menu + +class Kill(Frame): + # List of (name, option, pid_column) + view_list = [ + ('Default', ''), + ('Every (-e)', '-e'), + ('Non process group leaders (-d)', '-d'), + ('Non leaders with tty (-a)', '-a'), + ('For this user (-u %s)' % user, '-u %s' % user), + ] + format_list = [ + ('Default', '', 0), + ('Long (-l)', '-l', 3), + ('Full (-f)', '-f', 1), + ('Full Long (-f -l)', '-l -f', 3), + ('Session and group ID (-j)', '-j', 0), + ('Scheduler properties (-c)', '-c', 0), + ] + def kill(self, selected): + c = self.format_list[self.format.get()][2] + pid = split(selected)[c] + os.system('kill -9 ' + pid) + self.do_update() + def do_update(self): + format = self.format_list[self.format.get()][1] + view = self.view_list[self.view.get()][1] + s = commands.getoutput('ps %s %s' % (view, format)) + list = splitfields(s, '\n') + self.header.set(list[0] + ' ') + del list[0] + self.frame.list.delete(0, AtEnd()) + for line in list: + self.frame.list.insert(0, line) + def do_motion(self, e): + e.widget.select_clear('0', 'end') + e.widget.select_set(e.widget.nearest(e.y)) + def do_leave(self, e): + e.widget.select_clear('0', 'end') + def do_1(self, e): + self.kill(e.widget.get(e.widget.nearest(e.y))) + def __init__(self, master=None, **cnf): + apply(Frame.__init__, (self, master), cnf) + self.pack(expand=1, fill=BOTH) + self.bar = Frame(self, name='bar', relief=RAISED, + borderwidth=2) + self.bar.pack(fill=X) + self.bar.file = BarButton(self.bar, text='File') + self.bar.file.menu.add_command( + label='Quit', command=self.quit) + self.bar.view = BarButton(self.bar, text='View') + self.bar.format = BarButton(self.bar, text='Format') + self.view = IntVar(self) + self.view.set(0) + self.format = IntVar(self) + self.format.set(0) + for num in range(len(self.view_list)): + label, option = self.view_list[num] + self.bar.view.menu.add_radiobutton( + label=label, + command=self.do_update, + variable=self.view, + value=num) + for num in range(len(self.format_list)): + label, option, col = self.format_list[num] + self.bar.format.menu.add_radiobutton( + label=label, + command=self.do_update, + variable=self.format, + value=num) + self.bar.tk_menuBar(self.bar.file, + self.bar.view, + self.bar.format) + self.frame = Frame(self, relief=RAISED, borderwidth=2) + self.frame.pack(expand=1, fill=BOTH) + self.header = StringVar(self) + self.frame.label = Label( + self.frame, relief=FLAT, anchor=NW, borderwidth=0, + font='*-Courier-Bold-R-Normal-*-120-*', + textvariable=self.header) + self.frame.label.pack(fill=Y, anchor=W) + self.frame.vscroll = Scrollbar(self.frame, orient=VERTICAL) + self.frame.list = Listbox( + self.frame, + relief=SUNKEN, + font='*-Courier-Medium-R-Normal-*-120-*', + width=40, height=10, + selectbackground='#eed5b7', + selectborderwidth=0, + selectmode=BROWSE, + yscroll=self.frame.vscroll.set) + self.frame.vscroll['command'] = self.frame.list.yview + self.frame.vscroll.pack(side=RIGHT, fill=Y) + self.frame.list.pack(expand=1, fill=BOTH) + self.update = Button(self, text='Update', + command=self.do_update) + self.update.pack(fill=X) + self.frame.list.bind('<Motion>', self.do_motion) + self.frame.list.bind('<Leave>', self.do_leave) + self.frame.list.bind('<1>', self.do_1) + self.do_update() + +if __name__ == '__main__': + kill = Kill(None, borderwidth=5) + kill.winfo_toplevel().title('Tkinter Process Killer (SYSV)') + kill.winfo_toplevel().minsize(1, 1) + kill.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/switch.py b/sys/src/cmd/python/Demo/tkinter/guido/switch.py new file mode 100644 index 000000000..3b58f7ce4 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/switch.py @@ -0,0 +1,55 @@ +# Show how to do switchable panels. + +from Tkinter import * + +class App: + + def __init__(self, top=None, master=None): + if top is None: + if master is None: + top = Tk() + else: + top = Toplevel(master) + self.top = top + self.buttonframe = Frame(top) + self.buttonframe.pack() + self.panelframe = Frame(top, borderwidth=2, relief=GROOVE) + self.panelframe.pack(expand=1, fill=BOTH) + self.panels = {} + self.curpanel = None + + def addpanel(self, name, klass): + button = Button(self.buttonframe, text=name, + command=lambda self=self, name=name: self.show(name)) + button.pack(side=LEFT) + frame = Frame(self.panelframe) + instance = klass(frame) + self.panels[name] = (button, frame, instance) + if self.curpanel is None: + self.show(name) + + def show(self, name): + (button, frame, instance) = self.panels[name] + if self.curpanel: + self.curpanel.pack_forget() + self.curpanel = frame + frame.pack(expand=1, fill="both") + +class LabelPanel: + def __init__(self, frame): + self.label = Label(frame, text="Hello world") + self.label.pack() + +class ButtonPanel: + def __init__(self, frame): + self.button = Button(frame, text="Press me") + self.button.pack() + +def main(): + app = App() + app.addpanel("label", LabelPanel) + app.addpanel("button", ButtonPanel) + app.top.mainloop() + +if __name__ == '__main__': + main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/tkman.py b/sys/src/cmd/python/Demo/tkinter/guido/tkman.py new file mode 100755 index 000000000..6b0b64118 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/tkman.py @@ -0,0 +1,267 @@ +#! /usr/bin/env python + +# Tk man page browser -- currently only shows the Tcl/Tk man pages + +import sys +import os +import string +import re +from Tkinter import * +from ManPage import ManPage + +MANNDIRLIST = ['/depot/sundry/man/mann','/usr/local/man/mann'] +MAN3DIRLIST = ['/depot/sundry/man/man3','/usr/local/man/man3'] + +foundmanndir = 0 +for dir in MANNDIRLIST: + if os.path.exists(dir): + MANNDIR = dir + foundmanndir = 1 + +foundman3dir = 0 +for dir in MAN3DIRLIST: + if os.path.exists(dir): + MAN3DIR = dir + foundman3dir = 1 + +if not foundmanndir or not foundman3dir: + sys.stderr.write('\n') + if not foundmanndir: + msg = """\ +Failed to find mann directory. +Please add the correct entry to the MANNDIRLIST +at the top of %s script.""" % \ +sys.argv[0] + sys.stderr.write("%s\n\n" % msg) + if not foundman3dir: + msg = """\ +Failed to find man3 directory. +Please add the correct entry to the MAN3DIRLIST +at the top of %s script.""" % \ +sys.argv[0] + sys.stderr.write("%s\n\n" % msg) + sys.exit(1) + +del foundmanndir +del foundman3dir + +def listmanpages(mandir): + files = os.listdir(mandir) + names = [] + for file in files: + if file[-2:-1] == '.' and (file[-1] in 'ln123456789'): + names.append(file[:-2]) + names.sort() + return names + +class SelectionBox: + + def __init__(self, master=None): + self.choices = [] + + self.frame = Frame(master, name="frame") + self.frame.pack(expand=1, fill=BOTH) + self.master = self.frame.master + self.subframe = Frame(self.frame, name="subframe") + self.subframe.pack(expand=0, fill=BOTH) + self.leftsubframe = Frame(self.subframe, name='leftsubframe') + self.leftsubframe.pack(side=LEFT, expand=1, fill=BOTH) + self.rightsubframe = Frame(self.subframe, name='rightsubframe') + self.rightsubframe.pack(side=RIGHT, expand=1, fill=BOTH) + self.chaptervar = StringVar(master) + self.chapter = Menubutton(self.rightsubframe, name='chapter', + text='Directory', relief=RAISED, + borderwidth=2) + self.chapter.pack(side=TOP) + self.chaptermenu = Menu(self.chapter, name='chaptermenu') + self.chaptermenu.add_radiobutton(label='C functions', + value=MAN3DIR, + variable=self.chaptervar, + command=self.newchapter) + self.chaptermenu.add_radiobutton(label='Tcl/Tk functions', + value=MANNDIR, + variable=self.chaptervar, + command=self.newchapter) + self.chapter['menu'] = self.chaptermenu + self.listbox = Listbox(self.rightsubframe, name='listbox', + relief=SUNKEN, borderwidth=2, + width=20, height=5) + self.listbox.pack(expand=1, fill=BOTH) + self.l1 = Button(self.leftsubframe, name='l1', + text='Display manual page named:', + command=self.entry_cb) + self.l1.pack(side=TOP) + self.entry = Entry(self.leftsubframe, name='entry', + relief=SUNKEN, borderwidth=2, + width=20) + self.entry.pack(expand=0, fill=X) + self.l2frame = Frame(self.leftsubframe, name='l2frame') + self.l2frame.pack(expand=0, fill=NONE) + self.l2 = Button(self.l2frame, name='l2', + text='Search regexp:', + command=self.search_cb) + self.l2.pack(side=LEFT) + self.casevar = BooleanVar() + self.casesense = Checkbutton(self.l2frame, name='casesense', + text='Case sensitive', + variable=self.casevar, + relief=FLAT) + self.casesense.pack(side=LEFT) + self.search = Entry(self.leftsubframe, name='search', + relief=SUNKEN, borderwidth=2, + width=20) + self.search.pack(expand=0, fill=X) + self.title = Label(self.leftsubframe, name='title', + text='(none)') + self.title.pack(side=BOTTOM) + self.text = ManPage(self.frame, name='text', + relief=SUNKEN, borderwidth=2, + wrap=NONE, width=72, + selectbackground='pink') + self.text.pack(expand=1, fill=BOTH) + + self.entry.bind('<Return>', self.entry_cb) + self.search.bind('<Return>', self.search_cb) + self.listbox.bind('<Double-1>', self.listbox_cb) + + self.entry.bind('<Tab>', self.entry_tab) + self.search.bind('<Tab>', self.search_tab) + self.text.bind('<Tab>', self.text_tab) + + self.entry.focus_set() + + self.chaptervar.set(MANNDIR) + self.newchapter() + + def newchapter(self): + mandir = self.chaptervar.get() + self.choices = [] + self.addlist(listmanpages(mandir)) + + def addchoice(self, choice): + if choice not in self.choices: + self.choices.append(choice) + self.choices.sort() + self.update() + + def addlist(self, list): + self.choices[len(self.choices):] = list + self.choices.sort() + self.update() + + def entry_cb(self, *e): + self.update() + + def listbox_cb(self, e): + selection = self.listbox.curselection() + if selection and len(selection) == 1: + name = self.listbox.get(selection[0]) + self.show_page(name) + + def search_cb(self, *e): + self.search_string(self.search.get()) + + def entry_tab(self, e): + self.search.focus_set() + + def search_tab(self, e): + self.entry.focus_set() + + def text_tab(self, e): + self.entry.focus_set() + + def updatelist(self): + key = self.entry.get() + ok = filter(lambda name, key=key, n=len(key): name[:n]==key, + self.choices) + if not ok: + self.frame.bell() + self.listbox.delete(0, AtEnd()) + exactmatch = 0 + for item in ok: + if item == key: exactmatch = 1 + self.listbox.insert(AtEnd(), item) + if exactmatch: + return key + n = self.listbox.size() + if n == 1: + return self.listbox.get(0) + # Else return None, meaning not a unique selection + + def update(self): + name = self.updatelist() + if name: + self.show_page(name) + self.entry.delete(0, AtEnd()) + self.updatelist() + + def show_page(self, name): + file = '%s/%s.?' % (self.chaptervar.get(), name) + fp = os.popen('nroff -man %s | ul -i' % file, 'r') + self.text.kill() + self.title['text'] = name + self.text.parsefile(fp) + + def search_string(self, search): + if not search: + self.frame.bell() + print 'Empty search string' + return + if not self.casevar.get(): + map = re.IGNORECASE + else: + map = None + try: + if map: + prog = re.compile(search, map) + else: + prog = re.compile(search) + except re.error, msg: + self.frame.bell() + print 'Regex error:', msg + return + here = self.text.index(AtInsert()) + lineno = string.atoi(here[:string.find(here, '.')]) + end = self.text.index(AtEnd()) + endlineno = string.atoi(end[:string.find(end, '.')]) + wraplineno = lineno + found = 0 + while 1: + lineno = lineno + 1 + if lineno > endlineno: + if wraplineno <= 0: + break + endlineno = wraplineno + lineno = 0 + wraplineno = 0 + line = self.text.get('%d.0 linestart' % lineno, + '%d.0 lineend' % lineno) + i = prog.search(line) + if i >= 0: + found = 1 + n = max(1, len(prog.group(0))) + try: + self.text.tag_remove('sel', + AtSelFirst(), + AtSelLast()) + except TclError: + pass + self.text.tag_add('sel', + '%d.%d' % (lineno, i), + '%d.%d' % (lineno, i+n)) + self.text.mark_set(AtInsert(), + '%d.%d' % (lineno, i)) + self.text.yview_pickplace(AtInsert()) + break + if not found: + self.frame.bell() + +def main(): + root = Tk() + sb = SelectionBox(root) + if sys.argv[1:]: + sb.show_page(sys.argv[1]) + root.minsize(1, 1) + root.mainloop() + +main() diff --git a/sys/src/cmd/python/Demo/tkinter/guido/wish.py b/sys/src/cmd/python/Demo/tkinter/guido/wish.py new file mode 100755 index 000000000..0a61ad88d --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/guido/wish.py @@ -0,0 +1,27 @@ +# This is about all it requires to write a wish shell in Python! + +import _tkinter +import os + +tk = _tkinter.create(os.environ['DISPLAY'], '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') + if tk.getboolean(tk.call('info', 'complete', cmd)): + tk.record(line) + try: + result = tk.call('eval', cmd) + except _tkinter.TclError, msg: + print 'TclError:', msg + else: + if result: print result + cmd = '' diff --git a/sys/src/cmd/python/Demo/tkinter/matt/00-HELLO-WORLD.py b/sys/src/cmd/python/Demo/tkinter/matt/00-HELLO-WORLD.py new file mode 100644 index 000000000..1c3151b11 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/00-HELLO-WORLD.py @@ -0,0 +1,27 @@ +from Tkinter import * + +# note that there is no explicit call to start Tk. +# Tkinter is smart enough to start the system if it's not already going. + +class Test(Frame): + def printit(self): + print "hi" + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + + self.QUIT.pack(side=LEFT, fill=BOTH) + + # a hello button + self.hi_there = Button(self, text='Hello', + command=self.printit) + self.hi_there.pack(side=LEFT) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/README b/sys/src/cmd/python/Demo/tkinter/matt/README new file mode 100644 index 000000000..eb9d30246 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/README @@ -0,0 +1,30 @@ +This directory contains some ad-hoc examples of Tkinter widget +creation. The files named + + *-simple.py + +are the ones to start with if you're looking for a bare-bones usage of +a widget. The other files are meant to show common usage patters that +are a tad more involved. + +If you have a suggestion for an example program, please send mail to + + conway@virginia.edu + +and I'll include it. + + +matt + +TODO +------- +The X selection +Dialog Boxes +More canvas examples +Message widgets +Text Editors +Scrollbars +Listboxes + + + diff --git a/sys/src/cmd/python/Demo/tkinter/matt/animation-simple.py b/sys/src/cmd/python/Demo/tkinter/matt/animation-simple.py new file mode 100644 index 000000000..b52e1dc3a --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/animation-simple.py @@ -0,0 +1,35 @@ +from Tkinter import * + +# This program shows how to use the "after" function to make animation. + +class Test(Frame): + def printit(self): + print "hi" + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + self.QUIT.pack(side=LEFT, fill=BOTH) + + self.draw = Canvas(self, width="5i", height="5i") + + # all of these work.. + self.draw.create_rectangle(0, 0, 10, 10, tags="thing", fill="blue") + self.draw.pack(side=LEFT) + + def moveThing(self, *args): + # move 1/10 of an inch every 1/10 sec (1" per second, smoothly) + self.draw.move("thing", "0.01i", "0.01i") + self.after(10, self.moveThing) + + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + self.after(10, self.moveThing) + + +test = Test() + +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/animation-w-velocity-ctrl.py b/sys/src/cmd/python/Demo/tkinter/matt/animation-w-velocity-ctrl.py new file mode 100644 index 000000000..e676338fe --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/animation-w-velocity-ctrl.py @@ -0,0 +1,44 @@ +from Tkinter import * + +# this is the same as simple-demo-1.py, but uses +# subclassing. +# note that there is no explicit call to start Tk. +# Tkinter is smart enough to start the system if it's not already going. + + +class Test(Frame): + def printit(self): + print "hi" + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + self.QUIT.pack(side=BOTTOM, fill=BOTH) + + self.draw = Canvas(self, width="5i", height="5i") + + self.speed = Scale(self, orient=HORIZONTAL, from_=-100, to=100) + + self.speed.pack(side=BOTTOM, fill=X) + + # all of these work.. + self.draw.create_rectangle(0, 0, 10, 10, tags="thing", fill="blue") + self.draw.pack(side=LEFT) + + def moveThing(self, *args): + velocity = self.speed.get() + str = float(velocity) / 1000.0 + str = "%ri" % (str,) + self.draw.move("thing", str, str) + self.after(10, self.moveThing) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + self.after(10, self.moveThing) + + +test = Test() + +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/bind-w-mult-calls-p-type.py b/sys/src/cmd/python/Demo/tkinter/matt/bind-w-mult-calls-p-type.py new file mode 100644 index 000000000..f3220da50 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/bind-w-mult-calls-p-type.py @@ -0,0 +1,44 @@ +from Tkinter import * +import string + +# This program shows how to use a simple type-in box + +class App(Frame): + def __init__(self, master=None): + Frame.__init__(self, master) + self.pack() + + self.entrythingy = Entry() + self.entrythingy.pack() + + # and here we get a callback when the user hits return. we could + # make the key that triggers the callback anything we wanted to. + # other typical options might be <Key-Tab> or <Key> (for anything) + self.entrythingy.bind('<Key-Return>', self.print_contents) + + # Note that here is where we bind a completely different callback to + # the same event. We pass "+" here to indicate that we wish to ADD + # this callback to the list associated with this event type. + # Not specifying "+" would simply override whatever callback was + # defined on this event. + self.entrythingy.bind('<Key-Return>', self.print_something_else, "+") + + def print_contents(self, event): + print "hi. contents of entry is now ---->", self.entrythingy.get() + + + def print_something_else(self, event): + print "hi. Now doing something completely different" + + +root = App() +root.master.title("Foo") +root.mainloop() + + + +# secret tip for experts: if you pass *any* non-false value as +# the third parameter to bind(), Tkinter.py will accumulate +# callbacks instead of overwriting. I use "+" here because that's +# the Tk notation for getting this sort of behavior. The perfect GUI +# interface would use a less obscure notation. diff --git a/sys/src/cmd/python/Demo/tkinter/matt/canvas-demo-simple.py b/sys/src/cmd/python/Demo/tkinter/matt/canvas-demo-simple.py new file mode 100644 index 000000000..a01679a66 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/canvas-demo-simple.py @@ -0,0 +1,28 @@ +from Tkinter import * + +# this program creates a canvas and puts a single polygon on the canvas + +class Test(Frame): + def printit(self): + print "hi" + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + self.QUIT.pack(side=BOTTOM, fill=BOTH) + + self.draw = Canvas(self, width="5i", height="5i") + + # see the other demos for other ways of specifying coords for a polygon + self.draw.create_rectangle(0, 0, "3i", "3i", fill="black") + + self.draw.pack(side=LEFT) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() + +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/canvas-gridding.py b/sys/src/cmd/python/Demo/tkinter/matt/canvas-gridding.py new file mode 100644 index 000000000..3c52b91af --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/canvas-gridding.py @@ -0,0 +1,61 @@ +from Tkinter import * + +# this is the same as simple-demo-1.py, but uses +# subclassing. +# note that there is no explicit call to start Tk. +# Tkinter is smart enough to start the system if it's not already going. + +class Test(Frame): + def printit(self): + print "hi" + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', + background='red', + foreground='white', + height=3, + command=self.quit) + self.QUIT.pack(side=BOTTOM, fill=BOTH) + + self.canvasObject = Canvas(self, width="5i", height="5i") + self.canvasObject.pack(side=LEFT) + + def mouseDown(self, event): + # canvas x and y take the screen coords from the event and translate + # them into the coordinate system of the canvas object + self.startx = self.canvasObject.canvasx(event.x, self.griddingSize) + self.starty = self.canvasObject.canvasy(event.y, self.griddingSize) + + def mouseMotion(self, event): + # canvas x and y take the screen coords from the event and translate + # them into the coordinate system of the canvas object + x = self.canvasObject.canvasx(event.x, self.griddingSize) + y = self.canvasObject.canvasy(event.y, self.griddingSize) + + if (self.startx != event.x) and (self.starty != event.y) : + self.canvasObject.delete(self.rubberbandBox) + self.rubberbandBox = self.canvasObject.create_rectangle( + self.startx, self.starty, x, y) + # this flushes the output, making sure that + # the rectangle makes it to the screen + # before the next event is handled + self.update_idletasks() + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + + # this is a "tagOrId" for the rectangle we draw on the canvas + self.rubberbandBox = None + + # this is the size of the gridding squares + self.griddingSize = 50 + + Widget.bind(self.canvasObject, "<Button-1>", self.mouseDown) + Widget.bind(self.canvasObject, "<Button1-Motion>", self.mouseMotion) + + +test = Test() + +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/canvas-moving-or-creating.py b/sys/src/cmd/python/Demo/tkinter/matt/canvas-moving-or-creating.py new file mode 100644 index 000000000..5327c0827 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/canvas-moving-or-creating.py @@ -0,0 +1,62 @@ +from Tkinter import * + +# this file demonstrates a more sophisticated movement -- +# move dots or create new ones if you click outside the dots + +class Test(Frame): + ################################################################### + ###### Event callbacks for THE CANVAS (not the stuff drawn on it) + ################################################################### + def mouseDown(self, event): + # see if we're inside a dot. If we are, it + # gets tagged as CURRENT for free by tk. + if not event.widget.find_withtag(CURRENT): + # there is no dot here, so we can make one, + # and bind some interesting behavior to it. + # ------ + # create a dot, and mark it as CURRENT + fred = self.draw.create_oval( + event.x - 10, event.y -10, event.x +10, event.y + 10, + fill="green", tags=CURRENT) + + self.draw.tag_bind(fred, "<Any-Enter>", self.mouseEnter) + self.draw.tag_bind(fred, "<Any-Leave>", self.mouseLeave) + + self.lastx = event.x + self.lasty = event.y + + def mouseMove(self, event): + self.draw.move(CURRENT, event.x - self.lastx, event.y - self.lasty) + self.lastx = event.x + self.lasty = event.y + + ################################################################### + ###### Event callbacks for canvas ITEMS (stuff drawn on the canvas) + ################################################################### + def mouseEnter(self, event): + # the CURRENT tag is applied to the object the cursor is over. + # this happens automatically. + self.draw.itemconfig(CURRENT, fill="red") + + def mouseLeave(self, event): + # the CURRENT tag is applied to the object the cursor is over. + # this happens automatically. + self.draw.itemconfig(CURRENT, fill="blue") + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + self.QUIT.pack(side=LEFT, fill=BOTH) + self.draw = Canvas(self, width="5i", height="5i") + self.draw.pack(side=LEFT) + + Widget.bind(self.draw, "<1>", self.mouseDown) + Widget.bind(self.draw, "<B1-Motion>", self.mouseMove) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/canvas-moving-w-mouse.py b/sys/src/cmd/python/Demo/tkinter/matt/canvas-moving-w-mouse.py new file mode 100644 index 000000000..81785d86a --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/canvas-moving-w-mouse.py @@ -0,0 +1,55 @@ +from Tkinter import * + +# this file demonstrates the movement of a single canvas item under mouse control + +class Test(Frame): + ################################################################### + ###### Event callbacks for THE CANVAS (not the stuff drawn on it) + ################################################################### + def mouseDown(self, event): + # remember where the mouse went down + self.lastx = event.x + self.lasty = event.y + + def mouseMove(self, event): + # whatever the mouse is over gets tagged as CURRENT for free by tk. + self.draw.move(CURRENT, event.x - self.lastx, event.y - self.lasty) + self.lastx = event.x + self.lasty = event.y + + ################################################################### + ###### Event callbacks for canvas ITEMS (stuff drawn on the canvas) + ################################################################### + def mouseEnter(self, event): + # the CURRENT tag is applied to the object the cursor is over. + # this happens automatically. + self.draw.itemconfig(CURRENT, fill="red") + + def mouseLeave(self, event): + # the CURRENT tag is applied to the object the cursor is over. + # this happens automatically. + self.draw.itemconfig(CURRENT, fill="blue") + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + self.QUIT.pack(side=LEFT, fill=BOTH) + self.draw = Canvas(self, width="5i", height="5i") + self.draw.pack(side=LEFT) + + fred = self.draw.create_oval(0, 0, 20, 20, + fill="green", tags="selected") + + self.draw.tag_bind(fred, "<Any-Enter>", self.mouseEnter) + self.draw.tag_bind(fred, "<Any-Leave>", self.mouseLeave) + + Widget.bind(self.draw, "<1>", self.mouseDown) + Widget.bind(self.draw, "<B1-Motion>", self.mouseMove) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/canvas-mult-item-sel.py b/sys/src/cmd/python/Demo/tkinter/matt/canvas-mult-item-sel.py new file mode 100644 index 000000000..a4f267cc1 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/canvas-mult-item-sel.py @@ -0,0 +1,78 @@ +from Tkinter import * + +# allows moving dots with multiple selection. + +SELECTED_COLOR = "red" +UNSELECTED_COLOR = "blue" + +class Test(Frame): + ################################################################### + ###### Event callbacks for THE CANVAS (not the stuff drawn on it) + ################################################################### + def mouseDown(self, event): + # see if we're inside a dot. If we are, it + # gets tagged as CURRENT for free by tk. + + if not event.widget.find_withtag(CURRENT): + # we clicked outside of all dots on the canvas. unselect all. + + # re-color everything back to an unselected color + self.draw.itemconfig("selected", fill=UNSELECTED_COLOR) + # unselect everything + self.draw.dtag("selected") + else: + # mark as "selected" the thing the cursor is under + self.draw.addtag("selected", "withtag", CURRENT) + # color it as selected + self.draw.itemconfig("selected", fill=SELECTED_COLOR) + + self.lastx = event.x + self.lasty = event.y + + + def mouseMove(self, event): + self.draw.move("selected", event.x - self.lastx, event.y - self.lasty) + self.lastx = event.x + self.lasty = event.y + + def makeNewDot(self): + # create a dot, and mark it as current + fred = self.draw.create_oval(0, 0, 20, 20, + fill=SELECTED_COLOR, tags=CURRENT) + # and make it selected + self.draw.addtag("selected", "withtag", CURRENT) + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + + ################ + # make the canvas and bind some behavior to it + ################ + self.draw = Canvas(self, width="5i", height="5i") + Widget.bind(self.draw, "<1>", self.mouseDown) + Widget.bind(self.draw, "<B1-Motion>", self.mouseMove) + + # and other things..... + self.button = Button(self, text="make a new dot", foreground="blue", + command=self.makeNewDot) + + message = ("%s dots are selected and can be dragged.\n" + "%s are not selected.\n" + "Click in a dot to select it.\n" + "Click on empty space to deselect all dots." + ) % (SELECTED_COLOR, UNSELECTED_COLOR) + self.label = Message(self, width="5i", text=message) + + self.QUIT.pack(side=BOTTOM, fill=BOTH) + self.label.pack(side=BOTTOM, fill=X, expand=1) + self.button.pack(side=BOTTOM, fill=X) + self.draw.pack(side=LEFT) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/canvas-reading-tag-info.py b/sys/src/cmd/python/Demo/tkinter/matt/canvas-reading-tag-info.py new file mode 100644 index 000000000..f57ea180a --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/canvas-reading-tag-info.py @@ -0,0 +1,49 @@ +from Tkinter import * + + +class Test(Frame): + def printit(self): + print "hi" + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + self.QUIT.pack(side=BOTTOM, fill=BOTH) + + self.drawing = Canvas(self, width="5i", height="5i") + + # make a shape + pgon = self.drawing.create_polygon( + 10, 10, 110, 10, 110, 110, 10 , 110, + fill="red", tags=("weee", "foo", "groo")) + + # this is how you query an object for its attributes + # config options FOR CANVAS ITEMS always come back in tuples of length 5. + # 0 attribute name + # 1 BLANK + # 2 BLANK + # 3 default value + # 4 current value + # the blank spots are for consistency with the config command that + # is used for widgets. (remember, this is for ITEMS drawn + # on a canvas widget, not widgets) + option_value = self.drawing.itemconfig(pgon, "stipple") + print "pgon's current stipple value is -->", option_value[4], "<--" + option_value = self.drawing.itemconfig(pgon, "fill") + print "pgon's current fill value is -->", option_value[4], "<--" + print " when he is usually colored -->", option_value[3], "<--" + + ## here we print out all the tags associated with this object + option_value = self.drawing.itemconfig(pgon, "tags") + print "pgon's tags are", option_value[4] + + self.drawing.pack(side=LEFT) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() + +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/canvas-w-widget-draw-el.py b/sys/src/cmd/python/Demo/tkinter/matt/canvas-w-widget-draw-el.py new file mode 100644 index 000000000..5b26210c0 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/canvas-w-widget-draw-el.py @@ -0,0 +1,36 @@ +from Tkinter import * + +# this file demonstrates the creation of widgets as part of a canvas object + +class Test(Frame): + def printhi(self): + print "hi" + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + self.QUIT.pack(side=BOTTOM, fill=BOTH) + + self.draw = Canvas(self, width="5i", height="5i") + + self.button = Button(self, text="this is a button", + command=self.printhi) + + # note here the coords are given in pixels (form the + # upper right and corner of the window, as usual for X) + # but might just have well been given in inches or points or + # whatever...use the "anchor" option to control what point of the + # widget (in this case the button) gets mapped to the given x, y. + # you can specify corners, edges, center, etc... + self.draw.create_window(300, 300, window=self.button) + + self.draw.pack(side=LEFT) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() + +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/canvas-with-scrollbars.py b/sys/src/cmd/python/Demo/tkinter/matt/canvas-with-scrollbars.py new file mode 100644 index 000000000..81ef25a88 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/canvas-with-scrollbars.py @@ -0,0 +1,60 @@ +from Tkinter import * + +# This example program creates a scroling canvas, and demonstrates +# how to tie scrollbars and canvses together. The mechanism +# is analogus for listboxes and other widgets with +# "xscroll" and "yscroll" configuration options. + +class Test(Frame): + def printit(self): + print "hi" + + def createWidgets(self): + self.question = Label(self, text="Can Find The BLUE Square??????") + self.question.pack() + + self.QUIT = Button(self, text='QUIT', background='red', + height=3, command=self.quit) + self.QUIT.pack(side=BOTTOM, fill=BOTH) + spacer = Frame(self, height="0.25i") + spacer.pack(side=BOTTOM) + + # notice that the scroll region (20" x 20") is larger than + # displayed size of the widget (5" x 5") + self.draw = Canvas(self, width="5i", height="5i", + background="white", + scrollregion=(0, 0, "20i", "20i")) + + self.draw.scrollX = Scrollbar(self, orient=HORIZONTAL) + self.draw.scrollY = Scrollbar(self, orient=VERTICAL) + + # now tie the three together. This is standard boilerplate text + self.draw['xscrollcommand'] = self.draw.scrollX.set + self.draw['yscrollcommand'] = self.draw.scrollY.set + self.draw.scrollX['command'] = self.draw.xview + self.draw.scrollY['command'] = self.draw.yview + + # draw something. Note that the first square + # is visible, but you need to scroll to see the second one. + self.draw.create_rectangle(0, 0, "3.5i", "3.5i", fill="black") + self.draw.create_rectangle("10i", "10i", "13.5i", "13.5i", fill="blue") + + # pack 'em up + self.draw.scrollX.pack(side=BOTTOM, fill=X) + self.draw.scrollY.pack(side=RIGHT, fill=Y) + self.draw.pack(side=LEFT) + + + def scrollCanvasX(self, *args): + print "scrolling", args + print self.draw.scrollX.get() + + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() + +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/dialog-box.py b/sys/src/cmd/python/Demo/tkinter/matt/dialog-box.py new file mode 100644 index 000000000..dea8f3900 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/dialog-box.py @@ -0,0 +1,64 @@ +from Tkinter import * +from Dialog import Dialog + +# this shows how to create a new window with a button in it +# that can create new windows + +class Test(Frame): + def printit(self): + print "hi" + + def makeWindow(self): + """Create a top-level dialog with some buttons. + + This uses the Dialog class, which is a wrapper around the Tcl/Tk + tk_dialog script. The function returns 0 if the user clicks 'yes' + or 1 if the user clicks 'no'. + """ + # the parameters to this call are as follows: + d = Dialog( + self, ## name of a toplevel window + title="fred the dialog box",## title on the window + text="click on a choice", ## message to appear in window + bitmap="info", ## bitmap (if any) to appear; + ## if none, use "" + # legal values here are: + # string what it looks like + # ---------------------------------------------- + # error a circle with a slash through it + # grey25 grey square + # grey50 darker grey square + # hourglass use for "wait.." + # info a large, lower case "i" + # questhead a human head with a "?" in it + # question a large "?" + # warning a large "!" + # @fname X bitmap where fname is the path to the file + # + default=0, # the index of the default button choice. + # hitting return selects this + strings=("yes", "no")) + # values of the 'strings' key are the labels for the + # buttons that appear left to right in the dialog box + return d.num + + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + self.QUIT.pack(side=LEFT, fill=BOTH) + + # a hello button + self.hi_there = Button(self, text='Make a New Window', + command=self.makeWindow) + self.hi_there.pack(side=LEFT) + + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.windownum = 0 + self.createWidgets() + +test = Test() +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/entry-simple.py b/sys/src/cmd/python/Demo/tkinter/matt/entry-simple.py new file mode 100644 index 000000000..5146e6fd9 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/entry-simple.py @@ -0,0 +1,24 @@ +from Tkinter import * +import string + +# This program shows how to use a simple type-in box + +class App(Frame): + def __init__(self, master=None): + Frame.__init__(self, master) + self.pack() + + self.entrythingy = Entry() + self.entrythingy.pack() + + # and here we get a callback when the user hits return. we could + # make the key that triggers the callback anything we wanted to. + # other typical options might be <Key-Tab> or <Key> (for anything) + self.entrythingy.bind('<Key-Return>', self.print_contents) + + def print_contents(self, event): + print "hi. contents of entry is now ---->", self.entrythingy.get() + +root = App() +root.master.title("Foo") +root.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/entry-with-shared-variable.py b/sys/src/cmd/python/Demo/tkinter/matt/entry-with-shared-variable.py new file mode 100644 index 000000000..2b76162bd --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/entry-with-shared-variable.py @@ -0,0 +1,46 @@ +from Tkinter import * +import string + +# This program shows how to make a typein box shadow a program variable. + +class App(Frame): + def __init__(self, master=None): + Frame.__init__(self, master) + self.pack() + + self.entrythingy = Entry(self) + self.entrythingy.pack() + + self.button = Button(self, text="Uppercase The Entry", + command=self.upper) + self.button.pack() + + # here we have the text in the entry widget tied to a variable. + # changes in the variable are echoed in the widget and vice versa. + # Very handy. + # there are other Variable types. See Tkinter.py for all + # the other variable types that can be shadowed + self.contents = StringVar() + self.contents.set("this is a variable") + self.entrythingy.config(textvariable=self.contents) + + # and here we get a callback when the user hits return. we could + # make the key that triggers the callback anything we wanted to. + # other typical options might be <Key-Tab> or <Key> (for anything) + self.entrythingy.bind('<Key-Return>', self.print_contents) + + def upper(self): + # notice here, we don't actually refer to the entry box. + # we just operate on the string variable and we + # because it's being looked at by the entry widget, changing + # the variable changes the entry widget display automatically. + # the strange get/set operators are clunky, true... + str = string.upper(self.contents.get()) + self.contents.set(str) + + def print_contents(self, event): + print "hi. contents of entry is now ---->", self.contents.get() + +root = App() +root.master.title("Foo") +root.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/killing-window-w-wm.py b/sys/src/cmd/python/Demo/tkinter/matt/killing-window-w-wm.py new file mode 100644 index 000000000..6a0e2fe62 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/killing-window-w-wm.py @@ -0,0 +1,42 @@ +from Tkinter import * + +# This file shows how to trap the killing of a window +# when the user uses window manager menus (typ. upper left hand corner +# menu in the decoration border). + + +### ******* this isn't really called -- read the comments +def my_delete_callback(): + print "whoops -- tried to delete me!" + +class Test(Frame): + def deathHandler(self, event): + print self, "is now getting nuked. performing some save here...." + + def createWidgets(self): + # a hello button + self.hi_there = Button(self, text='Hello') + self.hi_there.pack(side=LEFT) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + + ### + ### PREVENT WM kills from happening + ### + + # the docs would have you do this: + +# self.master.protocol("WM_DELETE_WINDOW", my_delete_callback) + + # unfortunately, some window managers will not send this request to a window. + # the "protocol" function seems incapable of trapping these "aggressive" window kills. + # this line of code catches everything, tho. The window is deleted, but you have a chance + # of cleaning up first. + self.bind_all("<Destroy>", self.deathHandler) + + +test = Test() +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/menu-all-types-of-entries.py b/sys/src/cmd/python/Demo/tkinter/matt/menu-all-types-of-entries.py new file mode 100644 index 000000000..f4afe4a8b --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/menu-all-types-of-entries.py @@ -0,0 +1,244 @@ +from Tkinter import * + +# some vocabulary to keep from getting confused. This terminology +# is something I cooked up for this file, but follows the man pages +# pretty closely +# +# +# +# This is a MENUBUTTON +# V +# +-------------+ +# | | +# +# +------------++------------++------------+ +# | || || | +# | File || Edit || Options | <-------- the MENUBAR +# | || || | +# +------------++------------++------------+ +# | New... | +# | Open... | +# | Print | +# | | <-------- This is a MENU. The lines of text in the menu are +# | | MENU ENTRIES +# | +---------------+ +# | Open Files > | file1 | +# | | file2 | +# | | another file | <------ this cascading part is also a MENU +# +----------------| | +# | | +# | | +# | | +# +---------------+ + + + +# some miscellaneous callbacks +def new_file(): + print "opening new file" + +def open_file(): + print "opening OLD file" + +def print_something(): + print "picked a menu item" + + + +anchovies = 0 + +def print_anchovies(): + global anchovies + anchovies = not anchovies + print "anchovies?", anchovies + +def makeCommandMenu(): + # make menu button + Command_button = Menubutton(mBar, text='Simple Button Commands', + underline=0) + Command_button.pack(side=LEFT, padx="2m") + + # make the pulldown part of the File menu. The parameter passed is the master. + # we attach it to the button as a python attribute called "menu" by convention. + # hopefully this isn't too confusing... + Command_button.menu = Menu(Command_button) + + # just to be cute, let's disable the undo option: + Command_button.menu.add_command(label="Undo") + # undo is the 0th entry... + Command_button.menu.entryconfig(0, state=DISABLED) + + Command_button.menu.add_command(label='New...', underline=0, + command=new_file) + Command_button.menu.add_command(label='Open...', underline=0, + command=open_file) + Command_button.menu.add_command(label='Different Font', underline=0, + font='-*-helvetica-*-r-*-*-*-180-*-*-*-*-*-*', + command=print_something) + + # we can make bitmaps be menu entries too. File format is X11 bitmap. + # if you use XV, save it under X11 bitmap format. duh-uh.,.. + Command_button.menu.add_command( + bitmap="info") + #bitmap='@/home/mjc4y/dilbert/project.status.is.doomed.last.panel.bm') + + # this is just a line + Command_button.menu.add('separator') + + # change the color + Command_button.menu.add_command(label='Quit', underline=0, + background='red', + activebackground='green', + command=Command_button.quit) + + # set up a pointer from the file menubutton back to the file menu + Command_button['menu'] = Command_button.menu + + return Command_button + + + +def makeCascadeMenu(): + # make menu button + Cascade_button = Menubutton(mBar, text='Cascading Menus', underline=0) + Cascade_button.pack(side=LEFT, padx="2m") + + # the primary pulldown + Cascade_button.menu = Menu(Cascade_button) + + # this is the menu that cascades from the primary pulldown.... + Cascade_button.menu.choices = Menu(Cascade_button.menu) + + # ...and this is a menu that cascades from that. + Cascade_button.menu.choices.wierdones = Menu(Cascade_button.menu.choices) + + # then you define the menus from the deepest level on up. + Cascade_button.menu.choices.wierdones.add_command(label='avacado') + Cascade_button.menu.choices.wierdones.add_command(label='belgian endive') + Cascade_button.menu.choices.wierdones.add_command(label='beefaroni') + + # definition of the menu one level up... + Cascade_button.menu.choices.add_command(label='Chocolate') + Cascade_button.menu.choices.add_command(label='Vanilla') + Cascade_button.menu.choices.add_command(label='TuttiFruiti') + Cascade_button.menu.choices.add_command(label='WopBopaLoopBapABopBamBoom') + Cascade_button.menu.choices.add_command(label='Rocky Road') + Cascade_button.menu.choices.add_command(label='BubbleGum') + Cascade_button.menu.choices.add_cascade( + label='Wierd Flavors', + menu=Cascade_button.menu.choices.wierdones) + + # and finally, the definition for the top level + Cascade_button.menu.add_cascade(label='more choices', + menu=Cascade_button.menu.choices) + + Cascade_button['menu'] = Cascade_button.menu + + return Cascade_button + +def makeCheckbuttonMenu(): + global fred + # make menu button + Checkbutton_button = Menubutton(mBar, text='Checkbutton Menus', + underline=0) + Checkbutton_button.pack(side=LEFT, padx='2m') + + # the primary pulldown + Checkbutton_button.menu = Menu(Checkbutton_button) + + # and all the check buttons. Note that the "variable" "onvalue" and "offvalue" options + # are not supported correctly at present. You have to do all your application + # work through the calback. + Checkbutton_button.menu.add_checkbutton(label='Pepperoni') + Checkbutton_button.menu.add_checkbutton(label='Sausage') + Checkbutton_button.menu.add_checkbutton(label='Extra Cheese') + + # so here's a callback + Checkbutton_button.menu.add_checkbutton(label='Anchovy', + command=print_anchovies) + + # and start with anchovies selected to be on. Do this by + # calling invoke on this menu option. To refer to the "anchovy" menu + # entry we need to know it's index. To do this, we use the index method + # which takes arguments of several forms: + # + # argument what it does + # ----------------------------------- + # a number -- this is useless. + # "last" -- last option in the menu + # "none" -- used with the activate command. see the man page on menus + # "active" -- the currently active menu option. A menu option is made active + # with the 'activate' method + # "@number" -- where 'number' is an integer and is treated like a y coordinate in pixels + # string pattern -- this is the option used below, and attempts to match "labels" using the + # rules of Tcl_StringMatch + Checkbutton_button.menu.invoke(Checkbutton_button.menu.index('Anchovy')) + + # set up a pointer from the file menubutton back to the file menu + Checkbutton_button['menu'] = Checkbutton_button.menu + + return Checkbutton_button + + +def makeRadiobuttonMenu(): + # make menu button + Radiobutton_button = Menubutton(mBar, text='Radiobutton Menus', + underline=0) + Radiobutton_button.pack(side=LEFT, padx='2m') + + # the primary pulldown + Radiobutton_button.menu = Menu(Radiobutton_button) + + # and all the Radio buttons. Note that the "variable" "onvalue" and "offvalue" options + # are not supported correctly at present. You have to do all your application + # work through the calback. + Radiobutton_button.menu.add_radiobutton(label='Republican') + Radiobutton_button.menu.add_radiobutton(label='Democrat') + Radiobutton_button.menu.add_radiobutton(label='Libertarian') + Radiobutton_button.menu.add_radiobutton(label='Commie') + Radiobutton_button.menu.add_radiobutton(label='Facist') + Radiobutton_button.menu.add_radiobutton(label='Labor Party') + Radiobutton_button.menu.add_radiobutton(label='Torie') + Radiobutton_button.menu.add_radiobutton(label='Independent') + Radiobutton_button.menu.add_radiobutton(label='Anarchist') + Radiobutton_button.menu.add_radiobutton(label='No Opinion') + + # set up a pointer from the file menubutton back to the file menu + Radiobutton_button['menu'] = Radiobutton_button.menu + + return Radiobutton_button + + +def makeDisabledMenu(): + Dummy_button = Menubutton(mBar, text='Dead Menu', underline=0) + Dummy_button.pack(side=LEFT, padx='2m') + + # this is the standard way of turning off a whole menu + Dummy_button["state"] = DISABLED + return Dummy_button + + +################################################# +#### Main starts here ... +root = Tk() + + +# make a menu bar +mBar = Frame(root, relief=RAISED, borderwidth=2) +mBar.pack(fill=X) + +Command_button = makeCommandMenu() +Cascade_button = makeCascadeMenu() +Checkbutton_button = makeCheckbuttonMenu() +Radiobutton_button = makeRadiobuttonMenu() +NoMenu = makeDisabledMenu() + +# finally, install the buttons in the menu bar. +# This allows for scanning from one menubutton to the next. +mBar.tk_menuBar(Command_button, Cascade_button, Checkbutton_button, Radiobutton_button, NoMenu) + + +root.title('menu demo') +root.iconname('menu demo') + +root.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/menu-simple.py b/sys/src/cmd/python/Demo/tkinter/matt/menu-simple.py new file mode 100644 index 000000000..46b53642e --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/menu-simple.py @@ -0,0 +1,112 @@ +from Tkinter import * + +# some vocabulary to keep from getting confused. This terminology +# is something I cooked up for this file, but follows the man pages +# pretty closely +# +# +# +# This is a MENUBUTTON +# V +# +-------------+ +# | | +# +# +------------++------------++------------+ +# | || || | +# | File || Edit || Options | <-------- the MENUBAR +# | || || | +# +------------++------------++------------+ +# | New... | +# | Open... | +# | Print | +# | | <------ This is a MENU. The lines of text in the menu are +# | | MENU ENTRIES +# | +---------------+ +# | Open Files > | file1 | +# | | file2 | +# | | another file | <------ this cascading part is also a MENU +# +----------------| | +# | | +# | | +# | | +# +---------------+ + + + +def new_file(): + print "opening new file" + + +def open_file(): + print "opening OLD file" + + +def makeFileMenu(): + # make menu button : "File" + File_button = Menubutton(mBar, text='File', underline=0) + File_button.pack(side=LEFT, padx="1m") + File_button.menu = Menu(File_button) + + # add an item. The first param is a menu entry type, + # must be one of: "cascade", "checkbutton", "command", "radiobutton", "seperator" + # see menu-demo-2.py for examples of use + File_button.menu.add_command(label='New...', underline=0, + command=new_file) + + + File_button.menu.add_command(label='Open...', underline=0, + command=open_file) + + File_button.menu.add_command(label='Quit', underline=0, + command='exit') + + # set up a pointer from the file menubutton back to the file menu + File_button['menu'] = File_button.menu + + return File_button + + + +def makeEditMenu(): + Edit_button = Menubutton(mBar, text='Edit', underline=0) + Edit_button.pack(side=LEFT, padx="1m") + Edit_button.menu = Menu(Edit_button) + + # just to be cute, let's disable the undo option: + Edit_button.menu.add('command', label="Undo") + # Since the tear-off bar is the 0th entry, + # undo is the 1st entry... + Edit_button.menu.entryconfig(1, state=DISABLED) + + # and these are just for show. No "command" callbacks attached. + Edit_button.menu.add_command(label="Cut") + Edit_button.menu.add_command(label="Copy") + Edit_button.menu.add_command(label="Paste") + + # set up a pointer from the file menubutton back to the file menu + Edit_button['menu'] = Edit_button.menu + + return Edit_button + + +################################################# + +#### Main starts here ... +root = Tk() + + +# make a menu bar +mBar = Frame(root, relief=RAISED, borderwidth=2) +mBar.pack(fill=X) + +File_button = makeFileMenu() +Edit_button = makeEditMenu() + +# finally, install the buttons in the menu bar. +# This allows for scanning from one menubutton to the next. +mBar.tk_menuBar(File_button, Edit_button) + +root.title('menu demo') +root.iconname('packer') + +root.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/not-what-you-might-think-1.py b/sys/src/cmd/python/Demo/tkinter/matt/not-what-you-might-think-1.py new file mode 100644 index 000000000..7b20f02b3 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/not-what-you-might-think-1.py @@ -0,0 +1,28 @@ +from Tkinter import * + + +class Test(Frame): + def createWidgets(self): + + self.Gpanel = Frame(self, width='1i', height='1i', + background='green') + self.Gpanel.pack(side=LEFT) + + # a QUIT button + self.Gpanel.QUIT = Button(self.Gpanel, text='QUIT', + foreground='red', + command=self.quit) + self.Gpanel.QUIT.pack(side=LEFT) + + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() + +test.master.title('packer demo') +test.master.iconname('packer') + +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/not-what-you-might-think-2.py b/sys/src/cmd/python/Demo/tkinter/matt/not-what-you-might-think-2.py new file mode 100644 index 000000000..9ee197cf9 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/not-what-you-might-think-2.py @@ -0,0 +1,30 @@ +from Tkinter import * + + +class Test(Frame): + def createWidgets(self): + + self.Gpanel = Frame(self, width='1i', height='1i', + background='green') + + # this line turns off the recalculation of geometry by masters. + self.Gpanel.propagate(0) + + self.Gpanel.pack(side=LEFT) + + # a QUIT button + self.Gpanel.QUIT = Button(self.Gpanel, text='QUIT', foreground='red', + command=self.quit) + self.Gpanel.QUIT.pack(side=LEFT) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() + +test.master.title('packer demo') +test.master.iconname('packer') + +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/packer-and-placer-together.py b/sys/src/cmd/python/Demo/tkinter/matt/packer-and-placer-together.py new file mode 100644 index 000000000..184d56bc1 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/packer-and-placer-together.py @@ -0,0 +1,41 @@ +from Tkinter import * + +# This is a program that tests the placer geom manager in conjunction with +# the packer. The background (green) is packed, while the widget inside is placed + + +def do_motion(event): + app.button.place(x=event.x, y=event.y) + +def dothis(): + print 'calling me!' + +def createWidgets(top): + # make a frame. Note that the widget is 200 x 200 + # and the window containing is 400x400. We do this + # simply to show that this is possible. The rest of the + # area is inaccesssible. + f = Frame(top, width=200, height=200, background='green') + + # note that we use a different manager here. + # This way, the top level frame widget resizes when the + # application window does. + f.pack(fill=BOTH, expand=1) + + # now make a button + f.button = Button(f, foreground='red', text='amazing', command=dothis) + + # and place it so that the nw corner is + # 1/2 way along the top X edge of its' parent + f.button.place(relx=0.5, rely=0.0, anchor=NW) + + # allow the user to move the button SUIT-style. + f.bind('<Control-Shift-Motion>', do_motion) + + return f + +root = Tk() +app = createWidgets(root) +root.geometry("400x400") +root.maxsize(1000, 1000) +root.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/packer-simple.py b/sys/src/cmd/python/Demo/tkinter/matt/packer-simple.py new file mode 100644 index 000000000..f55f1bee7 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/packer-simple.py @@ -0,0 +1,32 @@ +from Tkinter import * + + +class Test(Frame): + def printit(self): + print self.hi_there["command"] + + def createWidgets(self): + # a hello button + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + self.QUIT.pack(side=LEFT, fill=BOTH) + + self.hi_there = Button(self, text='Hello', + command=self.printit) + self.hi_there.pack(side=LEFT) + + # note how Packer defaults to side=TOP + + self.guy2 = Button(self, text='button 2') + self.guy2.pack() + + self.guy3 = Button(self, text='button 3') + self.guy3.pack() + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/placer-simple.py b/sys/src/cmd/python/Demo/tkinter/matt/placer-simple.py new file mode 100644 index 000000000..30d9e9e90 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/placer-simple.py @@ -0,0 +1,39 @@ +from Tkinter import * + +# This is a program that tests the placer geom manager + +def do_motion(event): + app.button.place(x=event.x, y=event.y) + +def dothis(): + print 'calling me!' + +def createWidgets(top): + # make a frame. Note that the widget is 200 x 200 + # and the window containing is 400x400. We do this + # simply to show that this is possible. The rest of the + # area is inaccesssible. + f = Frame(top, width=200, height=200, background='green') + + # place it so the upper left hand corner of + # the frame is in the upper left corner of + # the parent + f.place(relx=0.0, rely=0.0) + + # now make a button + f.button = Button(f, foreground='red', text='amazing', command=dothis) + + # and place it so that the nw corner is + # 1/2 way along the top X edge of its' parent + f.button.place(relx=0.5, rely=0.0, anchor=NW) + + # allow the user to move the button SUIT-style. + f.bind('<Control-Shift-Motion>', do_motion) + + return f + +root = Tk() +app = createWidgets(root) +root.geometry("400x400") +root.maxsize(1000, 1000) +root.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/pong-demo-1.py b/sys/src/cmd/python/Demo/tkinter/matt/pong-demo-1.py new file mode 100644 index 000000000..7fcf800b6 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/pong-demo-1.py @@ -0,0 +1,54 @@ +from Tkinter import * + +import string + + +class Pong(Frame): + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + self.QUIT.pack(side=LEFT, fill=BOTH) + + ## The playing field + self.draw = Canvas(self, width="5i", height="5i") + + ## The speed control for the ball + self.speed = Scale(self, orient=HORIZONTAL, label="ball speed", + from_=-100, to=100) + + self.speed.pack(side=BOTTOM, fill=X) + + # The ball + self.ball = self.draw.create_oval("0i", "0i", "0.10i", "0.10i", + fill="red") + self.x = 0.05 + self.y = 0.05 + self.velocity_x = 0.3 + self.velocity_y = 0.5 + + self.draw.pack(side=LEFT) + + def moveBall(self, *args): + if (self.x > 5.0) or (self.x < 0.0): + self.velocity_x = -1.0 * self.velocity_x + if (self.y > 5.0) or (self.y < 0.0): + self.velocity_y = -1.0 * self.velocity_y + + deltax = (self.velocity_x * self.speed.get() / 100.0) + deltay = (self.velocity_y * self.speed.get() / 100.0) + self.x = self.x + deltax + self.y = self.y + deltay + + self.draw.move(self.ball, "%ri" % deltax, "%ri" % deltay) + self.after(10, self.moveBall) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + self.after(10, self.moveBall) + + +game = Pong() + +game.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/printing-coords-of-items.py b/sys/src/cmd/python/Demo/tkinter/matt/printing-coords-of-items.py new file mode 100644 index 000000000..a37733d03 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/printing-coords-of-items.py @@ -0,0 +1,61 @@ +from Tkinter import * + +# this file demonstrates the creation of widgets as part of a canvas object + +class Test(Frame): + ################################################################### + ###### Event callbacks for THE CANVAS (not the stuff drawn on it) + ################################################################### + def mouseDown(self, event): + # see if we're inside a dot. If we are, it + # gets tagged as CURRENT for free by tk. + + if not event.widget.find_withtag(CURRENT): + # there is no dot here, so we can make one, + # and bind some interesting behavior to it. + # ------ + # create a dot, and mark it as current + fred = self.draw.create_oval( + event.x - 10, event.y -10, event.x +10, event.y + 10, + fill="green") + self.draw.tag_bind(fred, "<Enter>", self.mouseEnter) + self.draw.tag_bind(fred, "<Leave>", self.mouseLeave) + self.lastx = event.x + self.lasty = event.y + + def mouseMove(self, event): + self.draw.move(CURRENT, event.x - self.lastx, event.y - self.lasty) + self.lastx = event.x + self.lasty = event.y + + ################################################################### + ###### Event callbacks for canvas ITEMS (stuff drawn on the canvas) + ################################################################### + def mouseEnter(self, event): + # the "current" tag is applied to the object the cursor is over. + # this happens automatically. + self.draw.itemconfig(CURRENT, fill="red") + print self.draw.coords(CURRENT) + + def mouseLeave(self, event): + # the "current" tag is applied to the object the cursor is over. + # this happens automatically. + self.draw.itemconfig(CURRENT, fill="blue") + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + self.QUIT.pack(side=LEFT, fill=BOTH) + self.draw = Canvas(self, width="5i", height="5i") + self.draw.pack(side=LEFT) + + Widget.bind(self.draw, "<1>", self.mouseDown) + Widget.bind(self.draw, "<B1-Motion>", self.mouseMove) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/radiobutton-simple.py b/sys/src/cmd/python/Demo/tkinter/matt/radiobutton-simple.py new file mode 100644 index 000000000..e9d6afee1 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/radiobutton-simple.py @@ -0,0 +1,62 @@ +from Tkinter import * + +# This is a demo program that shows how to +# create radio buttons and how to get other widgets to +# share the information in a radio button. +# +# There are other ways of doing this too, but +# the "variable" option of radiobuttons seems to be the easiest. +# +# note how each button has a value it sets the variable to as it gets hit. + + +class Test(Frame): + def printit(self): + print "hi" + + def createWidgets(self): + + self.flavor = StringVar() + self.flavor.set("chocolate") + + self.radioframe = Frame(self) + self.radioframe.pack() + + # 'text' is the label + # 'variable' is the name of the variable that all these radio buttons share + # 'value' is the value this variable takes on when the radio button is selected + # 'anchor' makes the text appear left justified (default is centered. ick) + self.radioframe.choc = Radiobutton( + self.radioframe, text="Chocolate Flavor", + variable=self.flavor, value="chocolate", + anchor=W) + self.radioframe.choc.pack(fill=X) + + self.radioframe.straw = Radiobutton( + self.radioframe, text="Strawberry Flavor", + variable=self.flavor, value="strawberry", + anchor=W) + self.radioframe.straw.pack(fill=X) + + self.radioframe.lemon = Radiobutton( + self.radioframe, text="Lemon Flavor", + variable=self.flavor, value="lemon", + anchor=W) + self.radioframe.lemon.pack(fill=X) + + # this is a text entry that lets you type in the name of a flavor too. + self.entry = Entry(self, textvariable=self.flavor) + self.entry.pack(fill=X) + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + self.QUIT.pack(side=BOTTOM, fill=BOTH) + + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() + +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/rubber-band-box-demo-1.py b/sys/src/cmd/python/Demo/tkinter/matt/rubber-band-box-demo-1.py new file mode 100644 index 000000000..b00518e00 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/rubber-band-box-demo-1.py @@ -0,0 +1,58 @@ +from Tkinter import * + +class Test(Frame): + def printit(self): + print "hi" + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', + background='red', + foreground='white', + height=3, + command=self.quit) + self.QUIT.pack(side=BOTTOM, fill=BOTH) + + self.canvasObject = Canvas(self, width="5i", height="5i") + self.canvasObject.pack(side=LEFT) + + def mouseDown(self, event): + # canvas x and y take the screen coords from the event and translate + # them into the coordinate system of the canvas object + self.startx = self.canvasObject.canvasx(event.x) + self.starty = self.canvasObject.canvasy(event.y) + + def mouseMotion(self, event): + # canvas x and y take the screen coords from the event and translate + # them into the coordinate system of the canvas object + x = self.canvasObject.canvasx(event.x) + y = self.canvasObject.canvasy(event.y) + + if (self.startx != event.x) and (self.starty != event.y) : + self.canvasObject.delete(self.rubberbandBox) + self.rubberbandBox = self.canvasObject.create_rectangle( + self.startx, self.starty, x, y) + # this flushes the output, making sure that + # the rectangle makes it to the screen + # before the next event is handled + self.update_idletasks() + + def mouseUp(self, event): + self.canvasObject.delete(self.rubberbandBox) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + + # this is a "tagOrId" for the rectangle we draw on the canvas + self.rubberbandBox = None + + # and the bindings that make it work.. + Widget.bind(self.canvasObject, "<Button-1>", self.mouseDown) + Widget.bind(self.canvasObject, "<Button1-Motion>", self.mouseMotion) + Widget.bind(self.canvasObject, "<Button1-ButtonRelease>", self.mouseUp) + + +test = Test() + +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/rubber-line-demo-1.py b/sys/src/cmd/python/Demo/tkinter/matt/rubber-line-demo-1.py new file mode 100644 index 000000000..59b8bd992 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/rubber-line-demo-1.py @@ -0,0 +1,51 @@ +from Tkinter import * + +class Test(Frame): + def printit(self): + print "hi" + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', + background='red', + foreground='white', + height=3, + command=self.quit) + self.QUIT.pack(side=BOTTOM, fill=BOTH) + + self.canvasObject = Canvas(self, width="5i", height="5i") + self.canvasObject.pack(side=LEFT) + + def mouseDown(self, event): + # canvas x and y take the screen coords from the event and translate + # them into the coordinate system of the canvas object + self.startx = self.canvasObject.canvasx(event.x) + self.starty = self.canvasObject.canvasy(event.y) + + def mouseMotion(self, event): + # canvas x and y take the screen coords from the event and translate + # them into the coordinate system of the canvas object + x = self.canvasObject.canvasx(event.x) + y = self.canvasObject.canvasy(event.y) + + if (self.startx != event.x) and (self.starty != event.y) : + self.canvasObject.delete(self.rubberbandLine) + self.rubberbandLine = self.canvasObject.create_line( + self.startx, self.starty, x, y) + # this flushes the output, making sure that + # the rectangle makes it to the screen + # before the next event is handled + self.update_idletasks() + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + # this is a "tagOrId" for the rectangle we draw on the canvas + self.rubberbandLine = None + Widget.bind(self.canvasObject, "<Button-1>", self.mouseDown) + Widget.bind(self.canvasObject, "<Button1-Motion>", self.mouseMotion) + + +test = Test() + +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/slider-demo-1.py b/sys/src/cmd/python/Demo/tkinter/matt/slider-demo-1.py new file mode 100644 index 000000000..db6114b1a --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/slider-demo-1.py @@ -0,0 +1,36 @@ +from Tkinter import * + +# shows how to make a slider, set and get its value under program control + + +class Test(Frame): + def print_value(self, val): + print "slider now at", val + + def reset(self): + self.slider.set(0) + + def createWidgets(self): + self.slider = Scale(self, from_=0, to=100, + orient=HORIZONTAL, + length="3i", + label="happy slider", + command=self.print_value) + + self.reset = Button(self, text='reset slider', + command=self.reset) + + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + + self.slider.pack(side=LEFT) + self.reset.pack(side=LEFT) + self.QUIT.pack(side=LEFT, fill=BOTH) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/subclass-existing-widgets.py b/sys/src/cmd/python/Demo/tkinter/matt/subclass-existing-widgets.py new file mode 100644 index 000000000..0e08f9206 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/subclass-existing-widgets.py @@ -0,0 +1,28 @@ +from Tkinter import * + +# This is a program that makes a simple two button application + + +class New_Button(Button): + def callback(self): + print self.counter + self.counter = self.counter + 1 + +def createWidgets(top): + f = Frame(top) + f.pack() + f.QUIT = Button(f, text='QUIT', foreground='red', command=top.quit) + + f.QUIT.pack(side=LEFT, fill=BOTH) + + # a hello button + f.hi_there = New_Button(f, text='Hello') + # we do this on a different line because we need to reference f.hi_there + f.hi_there.config(command=f.hi_there.callback) + f.hi_there.pack(side=LEFT) + f.hi_there.counter = 43 + + +root = Tk() +createWidgets(root) +root.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/two-radio-groups.py b/sys/src/cmd/python/Demo/tkinter/matt/two-radio-groups.py new file mode 100644 index 000000000..9fd8f4f07 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/two-radio-groups.py @@ -0,0 +1,110 @@ +from Tkinter import * + +# The way to think about this is that each radio button menu +# controls a different variable -- clicking on one of the +# mutually exclusive choices in a radiobutton assigns some value +# to an application variable you provide. When you define a +# radiobutton menu choice, you have the option of specifying the +# name of a varaible and value to assign to that variable when +# that choice is selected. This clever mechanism relieves you, +# the programmer, from having to write a dumb callback that +# probably wouldn't have done anything more than an assignment +# anyway. The Tkinter options for this follow their Tk +# counterparts: +# {"variable" : my_flavor_variable, "value" : "strawberry"} +# where my_flavor_variable is an instance of one of the +# subclasses of Variable, provided in Tkinter.py (there is +# StringVar(), IntVar(), DoubleVar() and BooleanVar() to choose +# from) + + + +def makePoliticalParties(var): + # make menu button + Radiobutton_button = Menubutton(mBar, text='Political Party', + underline=0) + Radiobutton_button.pack(side=LEFT, padx='2m') + + # the primary pulldown + Radiobutton_button.menu = Menu(Radiobutton_button) + + Radiobutton_button.menu.add_radiobutton(label='Republican', + variable=var, value=1) + + Radiobutton_button.menu.add('radiobutton', {'label': 'Democrat', + 'variable' : var, + 'value' : 2}) + + Radiobutton_button.menu.add('radiobutton', {'label': 'Libertarian', + 'variable' : var, + 'value' : 3}) + + var.set(2) + + # set up a pointer from the file menubutton back to the file menu + Radiobutton_button['menu'] = Radiobutton_button.menu + + return Radiobutton_button + + +def makeFlavors(var): + # make menu button + Radiobutton_button = Menubutton(mBar, text='Flavors', + underline=0) + Radiobutton_button.pack(side=LEFT, padx='2m') + + # the primary pulldown + Radiobutton_button.menu = Menu(Radiobutton_button) + + Radiobutton_button.menu.add_radiobutton(label='Strawberry', + variable=var, value='Strawberry') + + Radiobutton_button.menu.add_radiobutton(label='Chocolate', + variable=var, value='Chocolate') + + Radiobutton_button.menu.add_radiobutton(label='Rocky Road', + variable=var, value='Rocky Road') + + # choose a default + var.set("Chocolate") + + # set up a pointer from the file menubutton back to the file menu + Radiobutton_button['menu'] = Radiobutton_button.menu + + return Radiobutton_button + + +def printStuff(): + print "party is", party.get() + print "flavor is", flavor.get() + print + +################################################# +#### Main starts here ... +root = Tk() + + +# make a menu bar +mBar = Frame(root, relief=RAISED, borderwidth=2) +mBar.pack(fill=X) + +# make two application variables, +# one to control each radio button set +party = IntVar() +flavor = StringVar() + +Radiobutton_button = makePoliticalParties(party) +Radiobutton_button2 = makeFlavors(flavor) + +# finally, install the buttons in the menu bar. +# This allows for scanning from one menubutton to the next. +mBar.tk_menuBar(Radiobutton_button, Radiobutton_button2) + +b = Button(root, text="print party and flavor", foreground="red", + command=printStuff) +b.pack(side=TOP) + +root.title('menu demo') +root.iconname('menu demo') + +root.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/window-creation-more.py b/sys/src/cmd/python/Demo/tkinter/matt/window-creation-more.py new file mode 100644 index 000000000..eb0eb6fc1 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/window-creation-more.py @@ -0,0 +1,35 @@ +from Tkinter import * + +# this shows how to create a new window with a button in it +# that can create new windows + +class Test(Frame): + def printit(self): + print "hi" + + def makeWindow(self): + fred = Toplevel() + fred.label = Button(fred, + text="This is window number %d." % self.windownum, + command=self.makeWindow) + fred.label.pack() + self.windownum = self.windownum + 1 + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + self.QUIT.pack(side=LEFT, fill=BOTH) + + # a hello button + self.hi_there = Button(self, text='Make a New Window', + command=self.makeWindow) + self.hi_there.pack(side=LEFT) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.windownum = 0 + self.createWidgets() + +test = Test() +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/window-creation-simple.py b/sys/src/cmd/python/Demo/tkinter/matt/window-creation-simple.py new file mode 100644 index 000000000..c990ed9bf --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/window-creation-simple.py @@ -0,0 +1,31 @@ +from Tkinter import * + +# this shows how to spawn off new windows at a button press + +class Test(Frame): + def printit(self): + print "hi" + + def makeWindow(self): + fred = Toplevel() + fred.label = Label(fred, text="Here's a new window") + fred.label.pack() + + def createWidgets(self): + self.QUIT = Button(self, text='QUIT', foreground='red', + command=self.quit) + + self.QUIT.pack(side=LEFT, fill=BOTH) + + # a hello button + self.hi_there = Button(self, text='Make a New Window', + command=self.makeWindow) + self.hi_there.pack(side=LEFT) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() +test.mainloop() diff --git a/sys/src/cmd/python/Demo/tkinter/matt/window-creation-w-location.py b/sys/src/cmd/python/Demo/tkinter/matt/window-creation-w-location.py new file mode 100644 index 000000000..3f2b5b069 --- /dev/null +++ b/sys/src/cmd/python/Demo/tkinter/matt/window-creation-w-location.py @@ -0,0 +1,45 @@ +from Tkinter import * + +import sys +##sys.path.append("/users/mjc4y/projects/python/tkinter/utils") +##from TkinterUtils import * + +# this shows how to create a new window with a button in it that +# can create new windows + +class QuitButton(Button): + def __init__(self, master, *args, **kwargs): + if not kwargs.has_key("text"): + kwargs["text"] = "QUIT" + if not kwargs.has_key("command"): + kwargs["command"] = master.quit + apply(Button.__init__, (self, master) + args, kwargs) + +class Test(Frame): + def makeWindow(self, *args): + fred = Toplevel() + + fred.label = Canvas (fred, width="2i", height="2i") + + fred.label.create_line("0", "0", "2i", "2i") + fred.label.create_line("0", "2i", "2i", "0") + fred.label.pack() + + ##centerWindow(fred, self.master) + + def createWidgets(self): + self.QUIT = QuitButton(self) + self.QUIT.pack(side=LEFT, fill=BOTH) + + self.makeWindow = Button(self, text='Make a New Window', + width=50, height=20, + command=self.makeWindow) + self.makeWindow.pack(side=LEFT) + + def __init__(self, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.createWidgets() + +test = Test() +test.mainloop() |