gh-97681: Remove Tools/demo/ directory (#97682)

Remove the Tools/demo/ directory which contained old demo scripts. A
copy can be found in the old-demos project:
https://github.com/gvanrossum/old-demos

Remove the following old demo scripts:

* beer.py
* eiffel.py
* hanoi.py
* life.py
* markov.py
* mcast.py
* queens.py
* redemo.py
* rpython.py
* rpythond.py
* sortvisu.py
* spreadsheet.py
* vector.py

Changes:

* Remove a reference to the redemo.py script in the regex howto
  documentation.
* Remove a reference to the removed Tools/demo/ directory in the
  curses documentation.
* Update PC/layout/ to remove the reference to Tools/demo/ directory.
This commit is contained in:
Victor Stinner 2022-10-03 17:09:02 +02:00 committed by GitHub
parent e6f9ec5c03
commit 64fe343717
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 14 additions and 2730 deletions

View File

@ -378,11 +378,7 @@ containing information about the match: where it starts and ends, the substring
it matched, and more. it matched, and more.
You can learn about this by interactively experimenting with the :mod:`re` You can learn about this by interactively experimenting with the :mod:`re`
module. If you have :mod:`tkinter` available, you may also want to look at module.
:source:`Tools/demo/redemo.py`, a demonstration program included with the
Python distribution. It allows you to enter REs and strings, and displays
whether the RE matches or fails. :file:`redemo.py` can be quite useful when
trying to debug a complicated RE.
This HOWTO uses the standard Python interpreter for its examples. First, run the This HOWTO uses the standard Python interpreter for its examples. First, run the
Python interpreter, import the :mod:`re` module, and compile a RE:: Python interpreter, import the :mod:`re` module, and compile a RE::

View File

@ -42,9 +42,6 @@ Linux and the BSD variants of Unix.
Tutorial material on using curses with Python, by Andrew Kuchling and Eric Tutorial material on using curses with Python, by Andrew Kuchling and Eric
Raymond. Raymond.
The :source:`Tools/demo/` directory in the Python source distribution contains
some example programs using the curses bindings provided by this module.
.. _curses-functions: .. _curses-functions:

View File

@ -170,6 +170,15 @@ CPython bytecode changes
(Contributed by Ken Jin in :gh:`93429`.) (Contributed by Ken Jin in :gh:`93429`.)
Demos and Tools
===============
* Remove the ``Tools/demo/`` directory which contained old demo scripts. A copy
can be found in the `old-demos project
<https://github.com/gvanrossum/old-demos>`_.
(Contributed by Victor Stinner in :gh:`97681`.)
Deprecated Deprecated
========== ==========

View File

@ -0,0 +1,3 @@
Remove the ``Tools/demo/`` directory which contained old demo scripts. A copy
can be found in the `old-demos project
<https://github.com/gvanrossum/old-demos>`_. Patch by Victor Stinner.

View File

@ -58,7 +58,7 @@ CDF_FILES = FileSuffixSet(".cdf")
DATA_DIRS = FileNameSet("data") DATA_DIRS = FileNameSet("data")
TOOLS_DIRS = FileNameSet("scripts", "i18n", "demo", "parser") TOOLS_DIRS = FileNameSet("scripts", "i18n", "parser")
TOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt") TOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt")

View File

@ -5,8 +5,6 @@ buildbot Batchfiles for running on Windows buildbot workers.
ccbench A Python threads-based concurrency benchmark. (*) ccbench A Python threads-based concurrency benchmark. (*)
demo Several Python programming demos.
freeze Create a stand-alone executable from a Python program. freeze Create a stand-alone executable from a Python program.
gdb Python code to be run inside gdb, to make it easier to gdb Python code to be run inside gdb, to make it easier to

View File

@ -1,16 +0,0 @@
This directory contains a collection of demonstration scripts for
various aspects of Python programming.
beer.py Well-known programming example: Bottles of beer.
eiffel.py Python advanced magic: A metaclass for Eiffel post/preconditions.
hanoi.py Well-known programming example: Towers of Hanoi.
life.py Curses programming: Simple game-of-life.
markov.py Algorithms: Markov chain simulation.
mcast.py Network programming: Send and receive UDP multicast packets.
queens.py Well-known programming example: N-Queens problem.
redemo.py Regular Expressions: GUI script to test regexes.
rpython.py Network programming: Small client for remote code execution.
rpythond.py Network programming: Small server for remote code execution.
sortvisu.py GUI programming: Visualization of different sort algorithms.
spreadsheet.py GUI/Application programming: A simple spreadsheet application.
vector.py Python basics: A vector class demonstrating special methods.

View File

@ -1,25 +0,0 @@
#!/usr/bin/env python3
"""
A Python version of the classic "bottles of beer on the wall" programming
example.
By Guido van Rossum, demystified after a version by Fredrik Lundh.
"""
import sys
n = 100
if sys.argv[1:]:
n = int(sys.argv[1])
def bottle(n):
if n == 0: return "no more bottles of beer"
if n == 1: return "one bottle of beer"
return str(n) + " bottles of beer"
for i in range(n, 0, -1):
print(bottle(i), "on the wall,")
print(bottle(i) + ".")
print("Take one down, pass it around,")
print(bottle(i-1), "on the wall.")

View File

@ -1,146 +0,0 @@
#!/usr/bin/env python3
"""
Support Eiffel-style preconditions and postconditions for functions.
An example for Python metaclasses.
"""
import unittest
from types import FunctionType as function
class EiffelBaseMetaClass(type):
def __new__(meta, name, bases, dict):
meta.convert_methods(dict)
return super(EiffelBaseMetaClass, meta).__new__(
meta, name, bases, dict)
@classmethod
def convert_methods(cls, dict):
"""Replace functions in dict with EiffelMethod wrappers.
The dict is modified in place.
If a method ends in _pre or _post, it is removed from the dict
regardless of whether there is a corresponding method.
"""
# find methods with pre or post conditions
methods = []
for k, v in dict.items():
if k.endswith('_pre') or k.endswith('_post'):
assert isinstance(v, function)
elif isinstance(v, function):
methods.append(k)
for m in methods:
pre = dict.get("%s_pre" % m)
post = dict.get("%s_post" % m)
if pre or post:
dict[m] = cls.make_eiffel_method(dict[m], pre, post)
class EiffelMetaClass1(EiffelBaseMetaClass):
# an implementation of the "eiffel" meta class that uses nested functions
@staticmethod
def make_eiffel_method(func, pre, post):
def method(self, *args, **kwargs):
if pre:
pre(self, *args, **kwargs)
rv = func(self, *args, **kwargs)
if post:
post(self, rv, *args, **kwargs)
return rv
if func.__doc__:
method.__doc__ = func.__doc__
return method
class EiffelMethodWrapper:
def __init__(self, inst, descr):
self._inst = inst
self._descr = descr
def __call__(self, *args, **kwargs):
return self._descr.callmethod(self._inst, args, kwargs)
class EiffelDescriptor:
def __init__(self, func, pre, post):
self._func = func
self._pre = pre
self._post = post
self.__name__ = func.__name__
self.__doc__ = func.__doc__
def __get__(self, obj, cls=None):
return EiffelMethodWrapper(obj, self)
def callmethod(self, inst, args, kwargs):
if self._pre:
self._pre(inst, *args, **kwargs)
x = self._func(inst, *args, **kwargs)
if self._post:
self._post(inst, x, *args, **kwargs)
return x
class EiffelMetaClass2(EiffelBaseMetaClass):
# an implementation of the "eiffel" meta class that uses descriptors
make_eiffel_method = EiffelDescriptor
class Tests(unittest.TestCase):
def testEiffelMetaClass1(self):
self._test(EiffelMetaClass1)
def testEiffelMetaClass2(self):
self._test(EiffelMetaClass2)
def _test(self, metaclass):
class Eiffel(metaclass=metaclass):
pass
class Test(Eiffel):
def m(self, arg):
"""Make it a little larger"""
return arg + 1
def m2(self, arg):
"""Make it a little larger"""
return arg + 1
def m2_pre(self, arg):
assert arg > 0
def m2_post(self, result, arg):
assert result > arg
class Sub(Test):
def m2(self, arg):
return arg**2
def m2_post(self, Result, arg):
super(Sub, self).m2_post(Result, arg)
assert Result < 100
t = Test()
self.assertEqual(t.m(1), 2)
self.assertEqual(t.m2(1), 2)
self.assertRaises(AssertionError, t.m2, 0)
s = Sub()
self.assertRaises(AssertionError, s.m2, 1)
self.assertRaises(AssertionError, s.m2, 10)
self.assertEqual(s.m2(5), 25)
if __name__ == "__main__":
unittest.main()

View File

@ -1,154 +0,0 @@
#!/usr/bin/env python3
"""
Animated Towers of Hanoi using Tk with optional bitmap file in background.
Usage: hanoi.py [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.
"""
from tkinter import Tk, Canvas
# 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 True:
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 True:
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 True:
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 True:
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)
def main():
import sys
# First argument is number of pegs, default 4
if sys.argv[1:]:
n = int(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()

View File

@ -1,262 +0,0 @@
#!/usr/bin/env python3
"""
A curses-based version of Conway's Game of Life.
An empty board will be displayed, and the following commands are available:
E : Erase the board
R : Fill the board randomly
S : Step for a single generation
C : Update continuously until a key is struck
Q : Quit
Cursor keys : Move the cursor around the board
Space or Enter : Toggle the contents of the cursor's position
Contributed by Andrew Kuchling, Mouse support and color by Dafydd Crosby.
"""
import curses
import random
class LifeBoard:
"""Encapsulates a Life board
Attributes:
X,Y : horizontal and vertical size of the board
state : dictionary mapping (x,y) to 0 or 1
Methods:
display(update_board) -- If update_board is true, compute the
next generation. Then display the state
of the board and refresh the screen.
erase() -- clear the entire board
make_random() -- fill the board randomly
set(y,x) -- set the given cell to Live; doesn't refresh the screen
toggle(y,x) -- change the given cell from live to dead, or vice
versa, and refresh the screen display
"""
def __init__(self, scr, char=ord('*')):
"""Create a new LifeBoard instance.
scr -- curses screen object to use for display
char -- character used to render live cells (default: '*')
"""
self.state = {}
self.scr = scr
Y, X = self.scr.getmaxyx()
self.X, self.Y = X - 2, Y - 2 - 1
self.char = char
self.scr.clear()
# Draw a border around the board
border_line = '+' + (self.X * '-') + '+'
self.scr.addstr(0, 0, border_line)
self.scr.addstr(self.Y + 1, 0, border_line)
for y in range(0, self.Y):
self.scr.addstr(1 + y, 0, '|')
self.scr.addstr(1 + y, self.X + 1, '|')
self.scr.refresh()
def set(self, y, x):
"""Set a cell to the live state"""
if x < 0 or self.X <= x or y < 0 or self.Y <= y:
raise ValueError("Coordinates out of range %i,%i" % (y, x))
self.state[x, y] = 1
def toggle(self, y, x):
"""Toggle a cell's state between live and dead"""
if x < 0 or self.X <= x or y < 0 or self.Y <= y:
raise ValueError("Coordinates out of range %i,%i" % (y, x))
if (x, y) in self.state:
del self.state[x, y]
self.scr.addch(y + 1, x + 1, ' ')
else:
self.state[x, y] = 1
if curses.has_colors():
# Let's pick a random color!
self.scr.attrset(curses.color_pair(random.randrange(1, 7)))
self.scr.addch(y + 1, x + 1, self.char)
self.scr.attrset(0)
self.scr.refresh()
def erase(self):
"""Clear the entire board and update the board display"""
self.state = {}
self.display(update_board=False)
def display(self, update_board=True):
"""Display the whole board, optionally computing one generation"""
M, N = self.X, self.Y
if not update_board:
for i in range(0, M):
for j in range(0, N):
if (i, j) in self.state:
self.scr.addch(j + 1, i + 1, self.char)
else:
self.scr.addch(j + 1, i + 1, ' ')
self.scr.refresh()
return
d = {}
self.boring = 1
for i in range(0, M):
L = range(max(0, i - 1), min(M, i + 2))
for j in range(0, N):
s = 0
live = (i, j) in self.state
for k in range(max(0, j - 1), min(N, j + 2)):
for l in L:
if (l, k) in self.state:
s += 1
s -= live
if s == 3:
# Birth
d[i, j] = 1
if curses.has_colors():
# Let's pick a random color!
self.scr.attrset(curses.color_pair(
random.randrange(1, 7)))
self.scr.addch(j + 1, i + 1, self.char)
self.scr.attrset(0)
if not live:
self.boring = 0
elif s == 2 and live:
# Survival
d[i, j] = 1
elif live:
# Death
self.scr.addch(j + 1, i + 1, ' ')
self.boring = 0
self.state = d
self.scr.refresh()
def make_random(self):
"Fill the board with a random pattern"
self.state = {}
for i in range(0, self.X):
for j in range(0, self.Y):
if random.random() > 0.5:
self.set(j, i)
def erase_menu(stdscr, menu_y):
"Clear the space where the menu resides"
stdscr.move(menu_y, 0)
stdscr.clrtoeol()
stdscr.move(menu_y + 1, 0)
stdscr.clrtoeol()
def display_menu(stdscr, menu_y):
"Display the menu of possible keystroke commands"
erase_menu(stdscr, menu_y)
# If color, then light the menu up :-)
if curses.has_colors():
stdscr.attrset(curses.color_pair(1))
stdscr.addstr(menu_y, 4,
'Use the cursor keys to move, and space or Enter to toggle a cell.')
stdscr.addstr(menu_y + 1, 4,
'E)rase the board, R)andom fill, S)tep once or C)ontinuously, Q)uit')
stdscr.attrset(0)
def keyloop(stdscr):
# Clear the screen and display the menu of keys
stdscr.clear()
stdscr_y, stdscr_x = stdscr.getmaxyx()
menu_y = (stdscr_y - 3) - 1
display_menu(stdscr, menu_y)
# If color, then initialize the color pairs
if curses.has_colors():
curses.init_pair(1, curses.COLOR_BLUE, 0)
curses.init_pair(2, curses.COLOR_CYAN, 0)
curses.init_pair(3, curses.COLOR_GREEN, 0)
curses.init_pair(4, curses.COLOR_MAGENTA, 0)
curses.init_pair(5, curses.COLOR_RED, 0)
curses.init_pair(6, curses.COLOR_YELLOW, 0)
curses.init_pair(7, curses.COLOR_WHITE, 0)
# Set up the mask to listen for mouse events
curses.mousemask(curses.BUTTON1_CLICKED)
# Allocate a subwindow for the Life board and create the board object
subwin = stdscr.subwin(stdscr_y - 3, stdscr_x, 0, 0)
board = LifeBoard(subwin, char=ord('*'))
board.display(update_board=False)
# xpos, ypos are the cursor's position
xpos, ypos = board.X // 2, board.Y // 2
# Main loop:
while True:
stdscr.move(1 + ypos, 1 + xpos) # Move the cursor
c = stdscr.getch() # Get a keystroke
if 0 < c < 256:
c = chr(c)
if c in ' \n':
board.toggle(ypos, xpos)
elif c in 'Cc':
erase_menu(stdscr, menu_y)
stdscr.addstr(menu_y, 6, ' Hit any key to stop continuously '
'updating the screen.')
stdscr.refresh()
# Activate nodelay mode; getch() will return -1
# if no keystroke is available, instead of waiting.
stdscr.nodelay(1)
while True:
c = stdscr.getch()
if c != -1:
break
stdscr.addstr(0, 0, '/')
stdscr.refresh()
board.display()
stdscr.addstr(0, 0, '+')
stdscr.refresh()
stdscr.nodelay(0) # Disable nodelay mode
display_menu(stdscr, menu_y)
elif c in 'Ee':
board.erase()
elif c in 'Qq':
break
elif c in 'Rr':
board.make_random()
board.display(update_board=False)
elif c in 'Ss':
board.display()
else:
# Ignore incorrect keys
pass
elif c == curses.KEY_UP and ypos > 0:
ypos -= 1
elif c == curses.KEY_DOWN and ypos + 1 < board.Y:
ypos += 1
elif c == curses.KEY_LEFT and xpos > 0:
xpos -= 1
elif c == curses.KEY_RIGHT and xpos + 1 < board.X:
xpos += 1
elif c == curses.KEY_MOUSE:
mouse_id, mouse_x, mouse_y, mouse_z, button_state = curses.getmouse()
if (mouse_x > 0 and mouse_x < board.X + 1 and
mouse_y > 0 and mouse_y < board.Y + 1):
xpos = mouse_x - 1
ypos = mouse_y - 1
board.toggle(ypos, xpos)
else:
# They've clicked outside the board
curses.flash()
else:
# Ignore incorrect keys
pass
def main(stdscr):
keyloop(stdscr) # Enter the main loop
if __name__ == '__main__':
curses.wrapper(main)

View File

@ -1,125 +0,0 @@
#!/usr/bin/env python3
"""
Markov chain simulation of words or characters.
"""
class Markov:
def __init__(self, histsize, choice):
self.histsize = histsize
self.choice = choice
self.trans = {}
def add(self, state, next):
self.trans.setdefault(state, []).append(next)
def put(self, seq):
n = self.histsize
add = self.add
add(None, seq[:0])
for i in range(len(seq)):
add(seq[max(0, i-n):i], seq[i:i+1])
add(seq[len(seq)-n:], None)
def get(self):
choice = self.choice
trans = self.trans
n = self.histsize
seq = choice(trans[None])
while True:
subseq = seq[max(0, len(seq)-n):]
options = trans[subseq]
next = choice(options)
if not next:
break
seq += next
return seq
def test():
import sys, random, getopt
args = sys.argv[1:]
try:
opts, args = getopt.getopt(args, '0123456789cdwq')
except getopt.error:
print('Usage: %s [-#] [-cddqw] [file] ...' % sys.argv[0])
print('Options:')
print('-#: 1-digit history size (default 2)')
print('-c: characters (default)')
print('-w: words')
print('-d: more debugging output')
print('-q: no debugging output')
print('Input files (default stdin) are split in paragraphs')
print('separated blank lines and each paragraph is split')
print('in words by whitespace, then reconcatenated with')
print('exactly one space separating words.')
print('Output consists of paragraphs separated by blank')
print('lines, where lines are no longer than 72 characters.')
sys.exit(2)
histsize = 2
do_words = False
debug = 1
for o, a in opts:
if '-0' <= o <= '-9': histsize = int(o[1:])
if o == '-c': do_words = False
if o == '-d': debug += 1
if o == '-q': debug = 0
if o == '-w': do_words = True
if not args:
args = ['-']
m = Markov(histsize, random.choice)
try:
for filename in args:
if filename == '-':
f = sys.stdin
if f.isatty():
print('Sorry, need stdin from file')
continue
else:
f = open(filename, 'r')
with f:
if debug: print('processing', filename, '...')
text = f.read()
paralist = text.split('\n\n')
for para in paralist:
if debug > 1: print('feeding ...')
words = para.split()
if words:
if do_words:
data = tuple(words)
else:
data = ' '.join(words)
m.put(data)
except KeyboardInterrupt:
print('Interrupted -- continue with data read so far')
if not m.trans:
print('No valid input files')
return
if debug: print('done.')
if debug > 1:
for key in m.trans.keys():
if key is None or len(key) < histsize:
print(repr(key), m.trans[key])
if histsize == 0: print(repr(''), m.trans[''])
print()
while True:
data = m.get()
if do_words:
words = data
else:
words = data.split()
n = 0
limit = 72
for w in words:
if n + len(w) > limit:
print()
n = 0
print(w, end=' ')
n += len(w) + 1
print()
print()
if __name__ == "__main__":
test()

View File

@ -1,82 +0,0 @@
#!/usr/bin/env python3
"""
Send/receive UDP multicast packets.
Requires that your OS kernel supports IP multicast.
Usage:
mcast -s (sender, IPv4)
mcast -s -6 (sender, IPv6)
mcast (receivers, IPv4)
mcast -6 (receivers, IPv6)
"""
MYPORT = 8123
MYGROUP_4 = '225.0.0.250'
MYGROUP_6 = 'ff15:7079:7468:6f6e:6465:6d6f:6d63:6173'
MYTTL = 1 # Increase to reach other networks
import time
import struct
import socket
import sys
def main():
group = MYGROUP_6 if "-6" in sys.argv[1:] else MYGROUP_4
if "-s" in sys.argv[1:]:
sender(group)
else:
receiver(group)
def sender(group):
addrinfo = socket.getaddrinfo(group, None)[0]
s = socket.socket(addrinfo[0], socket.SOCK_DGRAM)
# Set Time-to-live (optional)
ttl_bin = struct.pack('@i', MYTTL)
if addrinfo[0] == socket.AF_INET: # IPv4
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl_bin)
else:
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, ttl_bin)
while True:
data = repr(time.time()).encode('utf-8') + b'\0'
s.sendto(data, (addrinfo[4][0], MYPORT))
time.sleep(1)
def receiver(group):
# Look up multicast group address in name server and find out IP version
addrinfo = socket.getaddrinfo(group, None)[0]
# Create a socket
s = socket.socket(addrinfo[0], socket.SOCK_DGRAM)
# Allow multiple copies of this program on one machine
# (not strictly needed)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind it to the port
s.bind(('', MYPORT))
group_bin = socket.inet_pton(addrinfo[0], addrinfo[4][0])
# Join group
if addrinfo[0] == socket.AF_INET: # IPv4
mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
else:
mreq = group_bin + struct.pack('@I', 0)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
# Loop, printing any data we receive
while True:
data, sender = s.recvfrom(1500)
while data[-1:] == '\0': data = data[:-1] # Strip trailing \0's
print(str(sender) + ' ' + repr(data))
if __name__ == '__main__':
main()

View File

@ -1,85 +0,0 @@
#!/usr/bin/env python3
"""
N queens problem.
The (well-known) problem is due to Niklaus Wirth.
This solution is inspired by Dijkstra (Structured Programming). It is
a classic recursive backtracking approach.
"""
N = 8 # Default; command line overrides
class Queens:
def __init__(self, n=N):
self.n = n
self.reset()
def reset(self):
n = self.n
self.y = [None] * n # Where is the queen in column x
self.row = [0] * n # Is row[y] safe?
self.up = [0] * (2*n-1) # Is upward diagonal[x-y] safe?
self.down = [0] * (2*n-1) # Is downward diagonal[x+y] safe?
self.nfound = 0 # Instrumentation
def solve(self, x=0): # Recursive solver
for y in range(self.n):
if self.safe(x, y):
self.place(x, y)
if x+1 == self.n:
self.display()
else:
self.solve(x+1)
self.remove(x, y)
def safe(self, x, y):
return not self.row[y] and not self.up[x-y] and not self.down[x+y]
def place(self, x, y):
self.y[x] = y
self.row[y] = 1
self.up[x-y] = 1
self.down[x+y] = 1
def remove(self, x, y):
self.y[x] = None
self.row[y] = 0
self.up[x-y] = 0
self.down[x+y] = 0
silent = 0 # If true, count solutions only
def display(self):
self.nfound = self.nfound + 1
if self.silent:
return
print('+-' + '--'*self.n + '+')
for y in range(self.n-1, -1, -1):
print('|', end=' ')
for x in range(self.n):
if self.y[x] == y:
print("Q", end=' ')
else:
print(".", end=' ')
print('|')
print('+-' + '--'*self.n + '+')
def main():
import sys
silent = 0
n = N
if sys.argv[1:2] == ['-n']:
silent = 1
del sys.argv[1]
if sys.argv[1:]:
n = int(sys.argv[1])
q = Queens(n)
q.silent = silent
q.solve()
print("Found", q.nfound, "solutions.")
if __name__ == "__main__":
main()

View File

@ -1,171 +0,0 @@
#!/usr/bin/env python3
"""Basic regular expression demonstration facility (Perl style syntax)."""
from tkinter import *
import re
class ReDemo:
def __init__(self, master):
self.master = master
self.promptdisplay = Label(self.master, anchor=W,
text="Enter a Perl-style regular expression:")
self.promptdisplay.pack(side=TOP, fill=X)
self.regexdisplay = Entry(self.master)
self.regexdisplay.pack(fill=X)
self.regexdisplay.focus_set()
self.addoptions()
self.statusdisplay = Label(self.master, text="", anchor=W)
self.statusdisplay.pack(side=TOP, fill=X)
self.labeldisplay = Label(self.master, anchor=W,
text="Enter a string to search:")
self.labeldisplay.pack(fill=X)
self.labeldisplay.pack(fill=X)
self.showframe = Frame(master)
self.showframe.pack(fill=X, anchor=W)
self.showvar = StringVar(master)
self.showvar.set("first")
self.showfirstradio = Radiobutton(self.showframe,
text="Highlight first match",
variable=self.showvar,
value="first",
command=self.recompile)
self.showfirstradio.pack(side=LEFT)
self.showallradio = Radiobutton(self.showframe,
text="Highlight all matches",
variable=self.showvar,
value="all",
command=self.recompile)
self.showallradio.pack(side=LEFT)
self.stringdisplay = Text(self.master, width=60, height=4)
self.stringdisplay.pack(fill=BOTH, expand=1)
self.stringdisplay.tag_configure("hit", background="yellow")
self.grouplabel = Label(self.master, text="Groups:", anchor=W)
self.grouplabel.pack(fill=X)
self.grouplist = Listbox(self.master)
self.grouplist.pack(expand=1, fill=BOTH)
self.regexdisplay.bind('<Key>', self.recompile)
self.stringdisplay.bind('<Key>', self.reevaluate)
self.compiled = None
self.recompile()
btags = self.regexdisplay.bindtags()
self.regexdisplay.bindtags(btags[1:] + btags[:1])
btags = self.stringdisplay.bindtags()
self.stringdisplay.bindtags(btags[1:] + btags[:1])
def addoptions(self):
self.frames = []
self.boxes = []
self.vars = []
for name in ('IGNORECASE',
'MULTILINE',
'DOTALL',
'VERBOSE'):
if len(self.boxes) % 3 == 0:
frame = Frame(self.master)
frame.pack(fill=X)
self.frames.append(frame)
val = getattr(re, name).value
var = IntVar()
box = Checkbutton(frame,
variable=var, text=name,
offvalue=0, onvalue=val,
command=self.recompile)
box.pack(side=LEFT)
self.boxes.append(box)
self.vars.append(var)
def getflags(self):
flags = 0
for var in self.vars:
flags = flags | var.get()
return flags
def recompile(self, event=None):
try:
self.compiled = re.compile(self.regexdisplay.get(),
self.getflags())
bg = self.promptdisplay['background']
self.statusdisplay.config(text="", background=bg)
except re.error as msg:
self.compiled = None
self.statusdisplay.config(
text="re.error: %s" % str(msg),
background="red")
self.reevaluate()
def reevaluate(self, event=None):
try:
self.stringdisplay.tag_remove("hit", "1.0", END)
except TclError:
pass
try:
self.stringdisplay.tag_remove("hit0", "1.0", END)
except TclError:
pass
self.grouplist.delete(0, END)
if not self.compiled:
return
self.stringdisplay.tag_configure("hit", background="yellow")
self.stringdisplay.tag_configure("hit0", background="orange")
text = self.stringdisplay.get("1.0", END)
last = 0
nmatches = 0
while last <= len(text):
m = self.compiled.search(text, last)
if m is None:
break
first, last = m.span()
if last == first:
last = first+1
tag = "hit0"
else:
tag = "hit"
pfirst = "1.0 + %d chars" % first
plast = "1.0 + %d chars" % last
self.stringdisplay.tag_add(tag, pfirst, plast)
if nmatches == 0:
self.stringdisplay.yview_pickplace(pfirst)
groups = list(m.groups())
groups.insert(0, m.group())
for i in range(len(groups)):
g = "%2d: %r" % (i, groups[i])
self.grouplist.insert(END, g)
nmatches = nmatches + 1
if self.showvar.get() == "first":
break
if nmatches == 0:
self.statusdisplay.config(text="(no match)",
background="yellow")
else:
self.statusdisplay.config(text="")
# Main function, run when invoked as a stand-alone Python program.
def main():
root = Tk()
demo = ReDemo(root)
root.protocol('WM_DELETE_WINDOW', root.quit)
root.mainloop()
if __name__ == '__main__':
main()

View File

@ -1,37 +0,0 @@
#!/usr/bin/env python3
"""
Remote python client.
Execute Python commands remotely and send output back.
"""
import sys
from socket import socket, AF_INET, SOCK_STREAM, SHUT_WR
PORT = 4127
BUFSIZE = 1024
def main():
if len(sys.argv) < 3:
print("usage: rpython host command")
sys.exit(2)
host = sys.argv[1]
port = PORT
i = host.find(':')
if i >= 0:
port = int(host[i+1:])
host = host[:i]
command = ' '.join(sys.argv[2:])
with socket(AF_INET, SOCK_STREAM) as s:
s.connect((host, port))
s.send(command.encode())
s.shutdown(SHUT_WR)
reply = b''
while True:
data = s.recv(BUFSIZE)
if not data:
break
reply += data
print(reply.decode(), end=' ')
main()

View File

@ -1,58 +0,0 @@
#!/usr/bin/env python3
"""
Remote python server.
Execute Python commands remotely and send output back.
WARNING: This version has a gaping security hole -- it accepts requests
from any host on the internet!
"""
import sys
from socket import socket, AF_INET, SOCK_STREAM
import io
import traceback
PORT = 4127
BUFSIZE = 1024
def main():
if len(sys.argv) > 1:
port = int(sys.argv[1])
else:
port = PORT
s = socket(AF_INET, SOCK_STREAM)
s.bind(('', port))
s.listen(1)
while True:
conn, (remotehost, remoteport) = s.accept()
with conn:
print('connection from', remotehost, remoteport)
request = b''
while True:
data = conn.recv(BUFSIZE)
if not data:
break
request += data
reply = execute(request.decode())
conn.send(reply.encode())
def execute(request):
stdout = sys.stdout
stderr = sys.stderr
sys.stdout = sys.stderr = fakefile = io.StringIO()
try:
try:
exec(request, {}, {})
except:
print()
traceback.print_exc(100)
finally:
sys.stderr = stderr
sys.stdout = stdout
return fakefile.getvalue()
try:
main()
except KeyboardInterrupt:
pass

View File

@ -1,635 +0,0 @@
#!/usr/bin/env python3
"""
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 *
import random
XGRID = 10
YGRID = 10
WIDTH = 6
class Array:
class Cancelled(BaseException):
pass
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 = self.canvas.create_line(0, 0, 0, 0)
self.right = self.canvas.create_line(0, 0, 0, 0)
self.pivot = self.canvas.create_line(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()
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:
self.canvas.itemconfig(item, fill='red')
else:
self.canvas.itemconfig(item, fill='orange')
self.hide_left_right_pivot()
def hide_partition(self):
for i in range(self.size):
item = self.items[i]
self.canvas.itemconfig(item, 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.canvas.coords(self.left, (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.canvas.coords(self.right, (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.canvas.coords(self.left, (0, 0, 0, 0))
def hide_right(self):
self.canvas.coords(self.right, (0, 0, 0, 0))
def show_pivot(self, pivot):
x1, y1, x2, y2 = self.items[pivot].position()
self.canvas.coords(self.pivot, (0, y1 - 2, 9999, y1 - 2))
def hide_pivot(self):
self.canvas.coords(self.pivot, (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
self.canvas = array.canvas
x1, y1, x2, y2 = self.position()
self.item_id = array.canvas.create_rectangle(x1, y1, x2, y2,
fill='red', outline='black', width=1)
self.canvas.tag_bind(self.item_id, '<Button-1>', self.mouse_down)
self.canvas.tag_bind(self.item_id, '<Button1-Motion>', self.mouse_move)
self.canvas.tag_bind(self.item_id, '<ButtonRelease-1>', self.mouse_up)
def delete(self):
item_id = self.item_id
self.array = None
self.item_id = None
self.canvas.delete(item_id)
def mouse_down(self, event):
self.lastx = event.x
self.lasty = event.y
self.origx = event.x
self.origy = event.y
self.canvas.tag_raise(self.item_id)
def mouse_move(self, event):
self.canvas.move(self.item_id,
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.canvas.coords(self.item_id, (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.canvas.tag_raise(self.item_id)
for pts in trajectory:
self.canvas.coords(self.item_id, pts)
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.canvas.itemcget(self.item_id, 'fill')
otherfill = self.canvas.itemcget(other.item_id, 'fill')
self.canvas.itemconfig(self.item_id, fill='green')
self.canvas.itemconfig(other.item_id, fill='yellow')
self.array.master.update()
if self.array.speed == "single-step":
self.canvas.coords(self.item_id, mynewpts)
self.canvas.coords(other.item_id, othernewpts)
self.array.master.update()
self.canvas.itemconfig(self.item_id, fill=myfill)
self.canvas.itemconfig(other.item_id, fill=otherfill)
self.array.wait(0)
return
mytrajectory = interpolate(myoldpts, mynewpts, nsteps)
othertrajectory = interpolate(otheroldpts, othernewpts, nsteps)
if self.value > other.value:
self.canvas.tag_raise(self.item_id)
self.canvas.tag_raise(other.item_id)
else:
self.canvas.tag_raise(other.item_id)
self.canvas.tag_raise(self.item_id)
try:
for i in range(len(mytrajectory)):
mypts = mytrajectory[i]
otherpts = othertrajectory[i]
self.canvas.coords(self.item_id, mypts)
self.canvas.coords(other.item_id, otherpts)
self.array.wait(50)
finally:
mypts = mytrajectory[-1]
otherpts = othertrajectory[-1]
self.canvas.coords(self.item_id, mypts)
self.canvas.coords(other.item_id, otherpts)
self.canvas.itemconfig(self.item_id, fill=myfill)
self.canvas.itemconfig(other.item_id, fill=otherfill)
def compareto(self, other):
myfill = self.canvas.itemcget(self.item_id, 'fill')
otherfill = self.canvas.itemcget(other.item_id, 'fill')
if self.value < other.value:
myflash = 'white'
otherflash = 'black'
outcome = -1
elif self.value > other.value:
myflash = 'black'
otherflash = 'white'
outcome = 1
else:
myflash = otherflash = 'grey'
outcome = 0
try:
self.canvas.itemconfig(self.item_id, fill=myflash)
self.canvas.itemconfig(other.item_id, fill=otherflash)
self.array.wait(500)
finally:
self.canvas.itemconfig(self.item_id, fill=myfill)
self.canvas.itemconfig(other.item_id, 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 True:
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 True:
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] + list(range(5, 55, 5))
if self.size not in sizes:
sizes.append(self.size)
sizes.sort()
self.m_size = OptionMenu(self.botleftframe, self.v_size, *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()

View File

@ -1,829 +0,0 @@
#!/usr/bin/env python3
"""
SS1 -- a spreadsheet-like application.
"""
import os
import re
import sys
from xml.parsers import expat
from xml.sax.saxutils import escape
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.ns = dict(
cell = self.cellvalue,
cells = self.multicellvalue,
sum = sum,
)
def cellvalue(self, x, y):
cell = self.getcell(x, y)
if hasattr(cell, 'recalc'):
return cell.recalc(self.ns)
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.maxsize, y2)
def clearcolumns(self, x1, x2):
self.clearcells(x1, 0, x2, sys.maxsize)
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.maxsize, sys.maxsize, 0, n)
def deleterows(self, y1, y2):
if y1 > y2:
y1, y2 = y2, y1
self.clearrows(y1, y2)
self.movecells(0, y2+1, sys.maxsize, sys.maxsize, 0, y1-y2-1)
def insertcolumns(self, x, n):
assert n > 0
self.movecells(x, 0, sys.maxsize, sys.maxsize, n, 0)
def deletecolumns(self, x1, x2):
if x1 > x2:
x1, x2 = x2, x1
self.clearcells(x1, x2)
self.movecells(x2+1, 0, sys.maxsize, sys.maxsize, 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.values():
if hasattr(cell, 'reset'):
cell.reset()
def recalc(self):
self.reset()
for cell in self.cells.values():
if hasattr(cell, 'recalc'):
cell.recalc(self.ns)
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.items():
if x <= 0 or y <= 0:
continue
if hasattr(cell, 'recalc'):
cell.recalc(self.ns)
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.items():
if hasattr(cell, 'xml'):
cellxml = cell.xml()
else:
cellxml = '<value>%s</value>' % 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()
with open(filename, "w", encoding='utf-8') as f:
f.write(text)
if text and not text.endswith('\n'):
f.write('\n')
def load(self, filename):
with open(filename, 'rb') as f:
SheetParser(self).parsefile(f)
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:
method(attrs)
self.texts = []
def data(self, text):
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 (TypeError, ValueError):
self.value = None
end_long = end_int
def end_double(self, text):
try:
self.value = float(text)
except (TypeError, ValueError):
self.value = None
def end_complex(self, text):
try:
self.value = complex(text)
except (TypeError, ValueError):
self.value = None
def end_string(self, text):
self.value = text
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(ns) -> 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, float, complex))
assert alignment in (LEFT, CENTER, RIGHT)
self.value = value
self.fmt = fmt
self.alignment = alignment
def recalc(self, ns):
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 '<long>%s</long>' % self.value
def _xml_float(self):
return '<double>%r</double>' % self.value
def _xml_complex(self):
return '<complex>%r</complex>' % self.value
class StringCell(BaseCell):
def __init__(self, text, fmt="%s", alignment=LEFT):
assert isinstance(text, str)
assert alignment in (LEFT, CENTER, RIGHT)
self.text = text
self.fmt = fmt
self.alignment = alignment
def recalc(self, ns):
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,
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, ns):
if self.value is None:
try:
self.value = eval(self.translated, ns)
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,
escape(self.formula))
def renumber(self, x1, y1, x2, y2, dx, dy):
out = []
for part in re.split(r'(\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 configure 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.maxsize, sys.maxsize)
def selectcolumn(self, event):
x, y = self.whichxy(event)
self.setcurrent(x, 1)
self.setcorner(x, sys.maxsize)
def extendcolumn(self, event):
x, y = self.whichxy(event)
if x > 0:
self.setcurrent(self.currentxy[0], 1)
self.setcorner(x, sys.maxsize)
def selectrow(self, event):
x, y = self.whichxy(event)
self.setcurrent(1, y)
self.setcorner(sys.maxsize, y)
def extendrow(self, event):
x, y = self.whichxy(event)
if y > 0:
self.setcurrent(1, self.currentxy[1])
self.setcorner(sys.maxsize, 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.items():
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.maxsize:
name = ":"
elif (x1, x2) == (1, sys.maxsize):
if y1 == y2:
name = "%d" % y1
else:
name = "%d:%d" % (y1, y2)
elif (y1, y2) == (1, sys.maxsize):
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.items():
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, float, complex:
try:
value = cls(text)
except (TypeError, ValueError):
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.items():
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."
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()

View File

@ -1,94 +0,0 @@
#!/usr/bin/env python3
"""
A demonstration of classes and their special methods in Python.
"""
class Vec:
"""A simple vector class.
Instances of the Vec class can be constructed from numbers
>>> a = Vec(1, 2, 3)
>>> b = Vec(3, 2, 1)
added
>>> a + b
Vec(4, 4, 4)
subtracted
>>> a - b
Vec(-2, 0, 2)
and multiplied by a scalar on the left
>>> 3.0 * a
Vec(3.0, 6.0, 9.0)
or on the right
>>> a * 3.0
Vec(3.0, 6.0, 9.0)
and dot product
>>> a.dot(b)
10
and printed in vector notation
>>> print(a)
<1 2 3>
"""
def __init__(self, *v):
self.v = list(v)
@classmethod
def fromlist(cls, v):
if not isinstance(v, list):
raise TypeError
inst = cls()
inst.v = v
return inst
def __repr__(self):
args = ', '.join([repr(x) for x in self.v])
return f'{type(self).__name__}({args})'
def __str__(self):
components = ' '.join([str(x) for x in self.v])
return f'<{components}>'
def __len__(self):
return len(self.v)
def __getitem__(self, i):
return self.v[i]
def __add__(self, other):
"Element-wise addition"
v = [x + y for x, y in zip(self.v, other.v)]
return Vec.fromlist(v)
def __sub__(self, other):
"Element-wise subtraction"
v = [x - y for x, y in zip(self.v, other.v)]
return Vec.fromlist(v)
def __mul__(self, scalar):
"Multiply by scalar"
v = [x * scalar for x in self.v]
return Vec.fromlist(v)
__rmul__ = __mul__
def dot(self, other):
"Vector dot product"
if not isinstance(other, Vec):
raise TypeError
return sum(x_i * y_i for (x_i, y_i) in zip(self, other))
def test():
import doctest
doctest.testmod()
test()