Kaydet (Commit) 9cf8f337 authored tarafından Guido van Rossum's avatar Guido van Rossum

Initial revision

üst 7ebb23c6
#! /usr/local/python
testlabels = 'Name', 'Address', 'City', 'Country', 'Comments'
def main():
import stdwin
from WindowParent import WindowParent, MainLoop
from FormSplit import FormSplit
from Buttons import Label
from TextEdit import TextEdit
#
stdwin.setdefscrollbars(0, 0)
#
w = WindowParent().create('FormTest', (0, 0))
f = FormSplit().create(w)
#
h, v = 0, 0
for label in testlabels:
f.placenext(h, v)
lbl = Label().definetext(f, label)
f.placenext(h + 100, v)
txt = TextEdit().createboxed(f, (40, 2), (2, 2))
#txt = TextEdit().create(f, (40, 2))
v = v + 2*stdwin.lineheight() + 10
#
w.realize()
#
MainLoop()
main()
Contents of this directory:
FormTest.py Show how a form can be built to enter multiple fields
RadioGroups.py Show how to use multiple groups of radio buttons
TestCSplit.py Test CSplit widget (a clock-like split)
TestDirList.py Test DirList widget (lists directory contents)
TestFormSplit.py Test FormSplit widget (arbitrary grouping)
TestSched.py Test WindowSched widget (event scheduling)
TestTextEdit.py Test TextEdit widget (probably doen't work any more)
ibrowse/ An Emacs info file browser
clock.py A simple clock, with alarm
lpwin.py Watch line printer queues
microedit.py The smallest window editor
miniedit.py A small multi-window editor
python.py A window interface to the Python interpreter
wdiff.py A window-based directory diff
#! /usr/local/python
# radiogroups.py
#
# Demonstrate multiple groups of radio buttons
import stdwin
from Buttons import *
from WindowParent import WindowParent, MainLoop
from HVSplit import HSplit, VSplit
def main():
#
# Create the widget hierarchy, top-down
#
# 1. Create the window
#
window = WindowParent().create('Radio Groups', (0, 0))
#
# 2. Create a horizontal split to contain the groups
#
topsplit = HSplit().create(window)
#
# 3. Create vertical splits, one for each group
#
group1 = VSplit().create(topsplit)
group2 = VSplit().create(topsplit)
group3 = VSplit().create(topsplit)
#
# 4. Create individual radio buttons, each in their own split
#
b11 = RadioButton().definetext(group1, 'Group 1 button 1')
b12 = RadioButton().definetext(group1, 'Group 1 button 2')
b13 = RadioButton().definetext(group1, 'Group 1 button 3')
#
b21 = RadioButton().definetext(group2, 'Group 2 button 1')
b22 = RadioButton().definetext(group2, 'Group 2 button 2')
b23 = RadioButton().definetext(group2, 'Group 2 button 3')
#
b31 = RadioButton().definetext(group3, 'Group 3 button 1')
b32 = RadioButton().definetext(group3, 'Group 3 button 2')
b33 = RadioButton().definetext(group3, 'Group 3 button 3')
#
# 5. Define the grouping for the radio buttons.
# Note: this doesn't have to be the same as the
# grouping is splits (although it usually is).
# Also set the 'hook' procedure for each button
#
list1 = [b11, b12, b13]
list2 = [b21, b22, b23]
list3 = [b31, b32, b33]
#
for b in list1:
b.group = list1
b.on_hook = myhook
for b in list2:
b.group = list2
b.on_hook = myhook
for b in list3:
b.group = list3
b.on_hook = myhook
#
# 6. Select a default button in each group
#
b11.select(1)
b22.select(1)
b33.select(1)
#
# 6. Realize the window
#
window.realize()
#
# 7. Dispatch events until the window is closed
#
MainLoop()
#
# 8. Report final selections
#
print 'You selected the following choices:'
if b11.selected: print '1.1'
if b12.selected: print '1.2'
if b13.selected: print '1.3'
if b21.selected: print '2.1'
if b22.selected: print '2.2'
if b23.selected: print '2.3'
if b31.selected: print '3.1'
if b32.selected: print '3.2'
if b33.selected: print '3.3'
# My 'hook' procedure
# This is placed as 'hook' attribute on each button.
# The example just prints the title of the selected button.
#
def myhook(self):
print 'Selected:', self.text
main()
#! /usr/local/python
# TestCSplit
import stdwin
from WindowParent import WindowParent, MainLoop
from Buttons import PushButton
def main(n):
from CSplit import CSplit
#
stdwin.setdefscrollbars(0, 0)
#
the_window = WindowParent().create('TestCSplit', (0, 0))
the_csplit = CSplit().create(the_window)
#
for i in range(n):
the_child = PushButton().define(the_csplit)
the_child.settext(`(i+n-1)%n+1`)
#
the_window.realize()
#
MainLoop()
main(12)
#! /usr/local/python
# TestDirList
from DirList import DirListWindow
from WindowParent import MainLoop
def main():
import sys
args = sys.argv[1:]
if not args:
args = ['.']
# Mac: args = [':']
for arg in args:
w = DirListWindow().create(arg)
MainLoop()
main()
#! /usr/local/python
# TestFormSplit
import stdwin
from WindowParent import WindowParent, MainLoop
from Buttons import PushButton
def main(n):
from FormSplit import FormSplit
#
stdwin.setdefscrollbars(1, 1)
#
the_window = WindowParent().create('TestFormSplit', (0, 0))
the_form = FormSplit().create(the_window)
#
for i in range(n):
if i % 3 == 0:
the_form.placenext(i*40, 0)
the_child = PushButton().define(the_form)
the_child.settext('XXX-' + `i` + '-YYY')
#
the_window.realize()
#
MainLoop()
main(6)
#! /usr/local/python
# TestSched
import stdwin
from WindowParent import WindowParent, MainLoop
import WindowSched
from Buttons import PushButton
def my_ringer(child):
child.id = None
stdwin.fleep()
def my_hook(child):
# schedule for the bell to ring in N seconds; cancel previous
if child.my_id:
WindowSched.cancel(child.my_id)
child.my_id = \
WindowSched.enter(child.my_number*1000, 0, my_ringer, child)
def main(n):
from CSplit import CSplit
window = WindowParent().create('TestSched', (0, 0))
csplit = CSplit().create(window)
for i in range(n):
child = PushButton().define(csplit)
child.my_number = i
child.my_id = None
child.settext(`(i+n-1)%n+1`)
child.hook = my_hook
window.realize()
WindowSched.run()
main(12)
#! /usr/local/python
# Test TextEdit widgets
def main():
from TextEdit import TextEdit
from WindowParent import WindowParent, MainLoop
w = WindowParent().create('Test TextEdit', (0, 0))
t = TextEdit().create(w, (40, 4))
w.realize()
MainLoop()
main()
#! /usr/local/python
# 'clock' -- A simple alarm clock
# The alarm can be set at 5 minute intervals on a 12 hour basis.
# It is controlled with the mouse:
# - Click and drag around the circle to set the alarm.
# - Click far outside the circle to clear the alarm.
# - Click near the center to set the alarm at the last time set.
# The alarm time is indicated by a small triangle just outside the circle,
# and also by a digital time at the bottom.
# The indicators disappear when the alarm is not set.
# When the alarm goes off, it beeps every minute for five minutes,
# and the clock turns into inverse video.
# Click or activate the window to turn the ringing off.
import stdwin
from stdwinevents import WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP, \
WE_TIMER, WE_DRAW, WE_SIZE, WE_CLOSE, WE_ACTIVATE
import mainloop
import time
from math import sin, cos, atan2, pi, sqrt
DEFWIDTH, DEFHEIGHT = 200, 200
MOUSE_EVENTS = (WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP)
ORIGIN = 0, 0
FARAWAY = 2000, 2000
EVERYWHERE = ORIGIN, FARAWAY
# TZDIFF = 5*3600 # THINK C 3.0 returns UCT if local time is EST
TZDIFF = 0 # THINK C 4.0 always returns local time
def main():
win = makewindow()
del win
mainloop.mainloop()
def makewindow():
stdwin.setdefwinsize(DEFWIDTH, DEFHEIGHT + stdwin.lineheight())
win = stdwin.open('clock')
setdimensions(win)
win.set = 1 # True when alarm is set
win.time = 11*60 + 40 # Time when alarm must go off
win.ring = 0 # True when alarm is ringing
win.dispatch = cdispatch
mainloop.register(win)
settimer(win)
return win
def cdispatch(event):
type, win, detail = event
if type == WE_DRAW:
drawproc(win, detail)
elif type == WE_TIMER:
settimer(win)
drawproc(win, EVERYWHERE)
elif type in MOUSE_EVENTS:
mouseclick(win, type, detail)
elif type == WE_ACTIVATE:
if win.ring:
# Turn the ringing off
win.ring = 0
win.begindrawing().invert(win.mainarea)
elif type == WE_SIZE:
win.change(EVERYWHERE)
setdimensions(win)
elif type == WE_CLOSE:
mainloop.unregister(win)
def setdimensions(win):
width, height = win.getwinsize()
height = height - stdwin.lineheight()
if width < height: size = width
else: size = height
halfwidth = width/2
halfheight = height/2
win.center = halfwidth, halfheight
win.radius = size*45/100
win.width = width
win.height = height
win.corner = width, height
win.mainarea = ORIGIN, win.corner
win.lineheight = stdwin.lineheight()
win.farcorner = width, height + win.lineheight
win.statusarea = (0, height), win.farcorner
win.fullarea = ORIGIN, win.farcorner
def settimer(win):
now = getlocaltime()
win.times = calctime(now)
delay = 61 - now % 60
win.settimer(10 * delay)
minutes = (now/60) % 720
if win.ring:
# Is it time to stop the alarm ringing?
since = (minutes - win.time + 720) % 720
if since >= 5:
# Stop it now
win.ring = 0
else:
# Ring again, once every minute
stdwin.fleep()
elif win.set and minutes == win.time:
# Start the alarm ringing
win.ring = 1
stdwin.fleep()
def drawproc(win, area):
hours, minutes, seconds = win.times
d = win.begindrawing()
d.cliprect(area)
d.erase(EVERYWHERE)
d.circle(win.center, win.radius)
d.line(win.center, calcpoint(win, hours*30 + minutes/2, 0.6))
d.line(win.center, calcpoint(win, minutes*6, 1.0))
str = dd(hours) + ':' + dd(minutes)
p = (win.width - d.textwidth(str))/2, win.height * 3 / 4
d.text(p, str)
if win.set:
drawalarm(win, d)
drawalarmtime(win, d)
if win.ring:
d.invert(win.mainarea)
def mouseclick(win, type, detail):
d = win.begindrawing()
if win.ring:
# First turn the ringing off
win.ring = 0
d.invert(win.mainarea)
h, v = detail[0]
ch, cv = win.center
x, y = h-ch, cv-v
dist = sqrt(x*x + y*y) / float(win.radius)
if dist > 1.2:
if win.set:
drawalarm(win, d)
erasealarmtime(win, d)
win.set = 0
elif dist < 0.8:
if not win.set:
win.set = 1
drawalarm(win, d)
drawalarmtime(win, d)
else:
# Convert to half-degrees (range 0..720)
alpha = atan2(y, x)
hdeg = alpha*360.0/pi
hdeg = 180.0 - hdeg
hdeg = (hdeg + 720.0) % 720.0
atime = 5*int(hdeg/5.0 + 0.5)
if atime <> win.time or not win.set:
if win.set:
drawalarm(win, d)
erasealarmtime(win, d)
win.set = 1
win.time = atime
drawalarm(win, d)
drawalarmtime(win, d)
def drawalarm(win, d):
p1 = calcpoint(win, float(win.time)/2.0, 1.02)
p2 = calcpoint(win, float(win.time)/2.0 - 4.0, 1.1)
p3 = calcpoint(win, float(win.time)/2.0 + 4.0, 1.1)
d.xorline(p1, p2)
d.xorline(p2, p3)
d.xorline(p3, p1)
def erasealarmtime(win, d):
d.erase(win.statusarea)
def drawalarmtime(win, d):
# win.time is in the range 0..720 with origin at 12 o'clock
# Convert to hours (0..12) and minutes (12*(0..60))
hh = win.time/60
mm = win.time%60
str = 'Alarm@' + dd(hh) + ':' + dd(mm)
p1 = (win.width - d.textwidth(str))/2, win.height
d.text(p1, str)
def calcpoint(win, degrees, size):
alpha = pi/2.0 - float(degrees) * pi/180.0
x, y = cos(alpha), sin(alpha)
h, v = win.center
r = float(win.radius)
return h + int(x*size*r), v - int(y*size*r)
def calctime(now):
seconds = now % 60
minutes = (now/60) % 60
hours = (now/3600) % 12
return hours, minutes, seconds
def dd(n):
s = `n`
return '0'*(2-len(s)) + s
def getlocaltime():
return time.time() - TZDIFF
main()
This directory contains a browser written in Python for "Info files"
as used by the Emacs documentation system. The browser requires that
Python is built with the "stdwin" option and runs under X11 or the
Mac window system.
Now you can read Info files even if you can't spare the memory, time or
disk space to run Emacs. (I have used this extensively on a Macintosh
with 1 Megabyte main memory and a 20 Meg harddisk.)
You can give this to someone with great fear of complex computer
systems, as long as they can use a mouse.
Another reason to use this is to encourage the use of Info for on-line
documentation of software that is not related to Emacs or GNU.
(In particular, I plan to redo the Python and STDWIN documentation
in texinfo.)
The main program is in file "ib.py"; this accepts a file name and a
node name as optional command line arguments, i.e., its usage is
python ib.py [file [node]]
Configuration:
- The pathname of the directory (or directories) containing
the standard Info files should be set by editing the
value assigned to INFOPATH in module ifile.py.
- The default font should be set by editing the value of FONT
in this module (ibrowse.py).
- For fastest I/O, you may look at BLOCKSIZE and a few other
constants in ifile.py.
-*- Text -*-
This is the file .../ibrowse/dir, which contains the topmost node of the
Info hierarchy. The first time you invoke Ibrowse you start off
looking at that node, which is (dir)Top. (This is a copy of the Info
dir node, except that the reference to Info is replaced by one to Ibrowse.)

File: dir Node: Top This is the top of the INFO tree
This (the Directory node) gives a menu of major topics.
Typing "d" returns here, "q" exits, "?" lists all INFO commands, "h"
gives a primer for first-timers, "mTexinfo<Return>" visits Texinfo topic,
etc.
--- PLEASE ADD DOCUMENTATION TO THIS TREE. (See INFO topic first.) ---
* Menu: The list of major topics begins on the next line.
* Ibrowse: (ibrowse). Documentation browsing system.
* Emacs: (emacs). The extensible self-documenting text editor.
* VIP: (vip). A VI-emulation for Emacs.
* Texinfo: (texinfo).
With one source file, make either a printed manual
(through TeX) or an Info file (through texinfo).
Full documentation in this menu item.
* Termcap: (termcap).
The termcap library, which enables application programs
to handle all types of character-display terminals.
* Regex: (regex).
The GNU regular expression library.
* Bison: (bison.info).
The GNU yacc-compatible parser generator.
* GCC: (gcc.info).
The GNU C compiler.
* G++: (g-whiz).
The GNU C++ compiler.
* LibG++: (libg++).
The GNU C++ library.
* GDB: (gdb.info).
The GNU debugger.
* CPP: (cpp.info).
The GNU C preprocessor.
* Lispref: (lispref).
The GNU Emacs Lisp reference manual.
* Make: (make-info).
The GNU make program.
* M4: (m4).
The GNU m4 program.
* Gawk: (gawk-info).
GNU awk.
: ${ARCH}=`arch`
exec /ufs/guido/bin/$ARCH/python ib.py ${1+"$@"}
#! /usr/local/python
# Call ibrowse (the info file browser) under UNIX.
import sys
import ibrowse
if len(sys.argv) > 1:
file = sys.argv[1]
if len(sys.argv) > 2:
if len(sys.argv) > 3:
sys.stdout = sys.stderr
print 'usage:', sys.argv[0], '[file [node]]'
sys.exit(2)
else:
node = sys.argv[2]
else:
node = ''
ibrowse.start('(' + file + ')' + node)
else:
ibrowse.main()
This diff is collapsed.
This diff is collapsed.
# Cache management for info file processing.
# The function get_node() is the standard interface;
# its signature is the same as ifile.get_node() but it uses
# the cache and supports indirect tag tables.
import string
import ifile
from ifile import NoSuchNode, NoSuchFile
import itags
# Special hack to save the cache when using reload().
# This can just be "cache = {}" in a production version.
#
try:
dummy = cache
del dummy
except NameError:
cache = {}
# Clear the entire cache.
#
def resetcache():
for key in cache.keys():
del cache[key]
# Clear the node info from the cache (the most voluminous data).
#
def resetnodecache():
for key in cache.keys():
tags, nodes = cache[key]
cache[key] = tags, {}
# Get a node.
#
def get_node(curfile, ref):
file, node = ifile.parse_ref(curfile, ref)
file = string.lower(file)
node = string.lower(node)
if node == '*':
# Don't cache whole file references;
# reading the data is faster than displaying it anyway.
return ifile.get_whole_file(file) # May raise NoSuchFile
if not cache.has_key(file):
cache[file] = get_tags(file), {} # May raise NoSuchFile
tags, nodes = cache[file]
if not nodes.has_key(node):
if not tags.has_key(node):
raise NoSuchNode, ref
file1, offset, line = tags[node]
if not file1:
file1 = file
file1, node1, header, menu, footnotes, text = \
ifile.get_file_node(file1, offset, node)
nodes[node] = file, node1, header, menu, footnotes, text
return nodes[node]
# Get the tag table for a file.
# Either construct one or get the one found in the file.
# Raise NoSuchFile if the file isn't found.
#
def get_tags(file):
f = ifile.try_open(file) # May raise NoSuchFile
tags = itags.get_tags(f)
if not tags:
###print 'Scanning file...'
f.seek(0)
tags = ifile.make_tags(f)
return tags
# Tools for info file processing.
# XXX Need to be more careful with reading ahead searching for nodes.
import regexp
import string
# Exported exceptions.
#
NoSuchFile = 'no such file'
NoSuchNode = 'no such node'
# The search path for info files; this is site-specific.
# Directory names should end in a partname delimiter,
# so they can simply be concatenated to a relative pathname.
#
#INFOPATH = ['', ':Info.Ibrowse:', ':Info:'] # Mac
INFOPATH = ['', '/usr/local/emacs/info/'] # X11 on UNIX
# Tunable constants.
#
BLOCKSIZE = 512 # Qty to align reads to, if possible
FUZZ = 2*BLOCKSIZE # Qty to back-up before searching for a node
CHUNKSIZE = 4*BLOCKSIZE # Qty to read at once when reading lots of data
# Regular expressions used.
# Note that it is essential that Python leaves unrecognized backslash
# escapes in a string so they can be seen by regexp.compile!
#
findheader = regexp.compile('\037\014?\n(.*\n)').match
findescape = regexp.compile('\037').match
parseheader = regexp.compile('[nN]ode:[ \t]*([^\t,\n]*)').match
findfirstline = regexp.compile('^.*\n').match
findnode = regexp.compile('[nN]ode:[ \t]*([^\t,\n]*)').match
findprev = regexp.compile('[pP]rev[ious]*:[ \t]*([^\t,\n]*)').match
findnext = regexp.compile('[nN]ext:[ \t]*([^\t,\n]*)').match
findup = regexp.compile('[uU]p:[ \t]*([^\t,\n]*)').match
findmenu = regexp.compile('^\* [mM]enu:').match
findmenuitem = regexp.compile( \
'^\* ([^:]+):[ \t]*(:|\([^\t]*\)[^\t,\n.]*|[^:(][^\t,\n.]*)').match
findfootnote = regexp.compile( \
'\*[nN]ote ([^:]+):[ \t]*(:|[^:][^\t,\n.]*)').match
parsenoderef = regexp.compile('^\((.*)\)(.*)$').match
# Get a node and all information pertaining to it.
# This doesn't work if there is an indirect tag table,
# and in general you are better off using icache.get_node() instead.
# Functions get_whole_file() and get_file_node() provide part
# functionality used by icache.
# Raise NoSuchFile or NoSuchNode as appropriate.
#
def get_node(curfile, ref):
file, node = parse_ref(curfile, ref)
if node == '*':
return get_whole_file(file)
else:
return get_file_node(file, 0, node)
#
def get_whole_file(file):
f = try_open(file) # May raise NoSuchFile
text = f.read()
header, menu, footnotes = ('', '', ''), [], []
return file, '*', header, menu, footnotes, text
#
def get_file_node(file, offset, node):
f = try_open(file) # May raise NoSuchFile
text = find_node(f, offset, node) # May raise NoSuchNode
node, header, menu, footnotes = analyze_node(text)
return file, node, header, menu, footnotes, text
# Parse a node reference into a file (possibly default) and node name.
# Possible reference formats are: "NODE", "(FILE)", "(FILE)NODE".
# Default file is the curfile argument; default node is Top.
# A node value of '*' is a special case: the whole file should
# be interpreted (by the caller!) as a single node.
#
def parse_ref(curfile, ref):
match = parsenoderef(ref)
if not match:
file, node = curfile, ref
else:
(a, b), (a1, b1), (a2, b2) = match
file, node = ref[a1:b1], ref[a2:b2]
if not file:
file = curfile # (Is this necessary?)
if not node:
node = 'Top'
return file, node
# Extract node name, links, menu and footnotes from the node text.
#
def analyze_node(text):
#
# Get node name and links from the header line
#
match = findfirstline(text)
if match:
(a, b) = match[0]
line = text[a:b]
else:
line = ''
node = get_it(text, findnode)
prev = get_it(text, findprev)
next = get_it(text, findnext)
up = get_it(text, findup)
#
# Get the menu items, if there is a menu
#
menu = []
match = findmenu(text)
if match:
(a, b) = match[0]
while 1:
match = findmenuitem(text, b)
if not match:
break
(a, b), (a1, b1), (a2, b2) = match
topic, ref = text[a1:b1], text[a2:b2]
if ref == ':':
ref = topic
menu.append(topic, ref)
#
# Get the footnotes
#
footnotes = []
b = 0
while 1:
match = findfootnote(text, b)
if not match:
break
(a, b), (a1, b1), (a2, b2) = match
topic, ref = text[a1:b1], text[a2:b2]
if ref == ':':
ref = topic
footnotes.append(topic, ref)
#
return node, (prev, next, up), menu, footnotes
#
def get_it(line, matcher):
match = matcher(line)
if not match:
return ''
else:
(a, b), (a1, b1) = match
return line[a1:b1]
# Find a node in an open file.
# The offset (from the tags table) is a hint about the node's position.
# Pass zero if there is no tags table.
# Raise NoSuchNode if the node isn't found.
# NB: This seeks around in the file.
#
def find_node(f, offset, node):
node = string.lower(node) # Just to be sure
#
# Position a little before the given offset,
# so we may find the node even if it has moved around
# in the file a little.
#
offset = max(0, ((offset-FUZZ) / BLOCKSIZE) * BLOCKSIZE)
f.seek(offset)
#
# Loop, hunting for a matching node header.
#
while 1:
buf = f.read(CHUNKSIZE)
if not buf:
break
i = 0
while 1:
match = findheader(buf, i)
if match:
(a,b), (a1,b1) = match
start = a1
line = buf[a1:b1]
i = b
match = parseheader(line)
if match:
(a,b), (a1,b1) = match
key = string.lower(line[a1:b1])
if key == node:
# Got it! Now read the rest.
return read_node(f, buf[start:])
elif findescape(buf, i):
next = f.read(CHUNKSIZE)
if not next:
break
buf = buf + next
else:
break
#
# If we get here, we didn't find it. Too bad.
#
raise NoSuchNode, node
# Finish off getting a node (subroutine for find_node()).
# The node begins at the start of buf and may end in buf;
# if it doesn't end there, read additional data from f.
#
def read_node(f, buf):
i = 0
match = findescape(buf, i)
while not match:
next = f.read(CHUNKSIZE)
if not next:
end = len(buf)
break
i = len(buf)
buf = buf + next
match = findescape(buf, i)
else:
# Got a match
(a, b) = match[0]
end = a
# Strip trailing newlines
while end > 0 and buf[end-1] == '\n':
end = end-1
buf = buf[:end]
return buf
# Read reverse starting at offset until the beginning of a node is found.
# Then return a buffer containing the beginning of the node,
# with f positioned just after the buffer.
# The buffer will contain at least the full header line of the node;
# the caller should finish off with read_node() if it is the right node.
# (It is also possible that the buffer extends beyond the node!)
# Return an empty string if there is no node before the given offset.
#
def backup_node(f, offset):
start = max(0, ((offset-CHUNKSIZE) / BLOCKSIZE) * BLOCKSIZE)
end = offset
while start < end:
f.seek(start)
buf = f.read(end-start)
i = 0
hit = -1
while 1:
match = findheader(buf, i)
if match:
(a,b), (a1,b1) = match
hit = a1
i = b
elif end < offset and findescape(buf, i):
next = f.read(min(offset-end, BLOCKSIZE))
if not next:
break
buf = buf + next
end = end + len(next)
else:
break
if hit >= 0:
return buf[hit:]
end = start
start = max(0, end - CHUNKSIZE)
return ''
# Make a tag table for the given file by scanning the file.
# The file must be open for reading, and positioned at the beginning
# (or wherever the hunt for tags must begin; it is read till the end).
#
def make_tags(f):
tags = {}
while 1:
offset = f.tell()
buf = f.read(CHUNKSIZE)
if not buf:
break
i = 0
while 1:
match = findheader(buf, i)
if match:
(a,b), (a1,b1) = match
start = offset+a1
line = buf[a1:b1]
i = b
match = parseheader(line)
if match:
(a,b), (a1,b1) = match
key = string.lower(line[a1:b1])
if tags.has_key(key):
print 'Duplicate node:',
print key
tags[key] = '', start, line
elif findescape(buf, i):
next = f.read(CHUNKSIZE)
if not next:
break
buf = buf + next
else:
break
return tags
# Try to open a file, return a file object if succeeds.
# Raise NoSuchFile if the file can't be opened.
# Should treat absolute pathnames special.
#
def try_open(file):
for dir in INFOPATH:
try:
return open(dir + file, 'r')
except RuntimeError:
pass
raise NoSuchFile, file
# A little test for the speed of make_tags().
#
TESTFILE = 'texinfo-1'
def test_make_tags():
import time
f = try_open(TESTFILE)
t1 = time.millitimer()
tags = make_tags(f)
t2 = time.millitimer()
print 'Making tag table for', `TESTFILE`, 'took', t2-t1, 'msec.'
# Utility module for 'icache.py': interpret tag tables and indirect nodes.
# (This module is a bit chatty when confronted with the unexpected.)
import regexp
import string
import ifile
# Get the tag table of an open file, as a dictionary.
# Seeks around in the file; after reading, the position is undefined.
# Return an empty tag table if none is found.
#
def get_tags(f):
#
# First see if the last "node" is the end of tag table marker.
#
f.seek(0, 2) # Seek to EOF
end = f.tell()
buf = ifile.backup_node(f, end)
if not labelmatch(buf, 0, 'end tag table\n'):
return {} # No succes
#
# Next backup to the previous "node" -- the tag table itself.
#
###print 'Getting prebuilt tag table...'
end = f.tell() - len(buf)
buf = ifile.backup_node(f, end)
label = 'tag table:\n'
if not labelmatch(buf, 0, label):
print 'Weird: end tag table marker but no tag table?'
print 'Node begins:', `buf[:50]`
return {}
#
# Now read the whole tag table.
#
end = f.tell() - len(buf) # Do this first!
buf = ifile.read_node(f, buf)
#
# First check for an indirection table.
#
indirlist = []
if labelmatch(buf, len(label), '(indirect)\n'):
indirbuf = ifile.backup_node(f, end)
if not labelmatch(indirbuf, 0, 'indirect:\n'):
print 'Weird: promised indirection table not found'
print 'Node begins:', `indirbuf[:50]`
# Carry on. Things probably won't work though.
else:
indirbuf = ifile.read_node(f, indirbuf)
indirlist = parse_indirlist(indirbuf)
#
# Now parse the tag table.
#
findtag = regexp.compile('^(.*[nN]ode:[ \t]*(.*))\177([0-9]+)$').match
i = 0
tags = {}
while 1:
match = findtag(buf, i)
if not match:
break
(a,b), (a1,b1), (a2,b2), (a3,b3) = match
i = b
line = buf[a1:b1]
node = string.lower(buf[a2:b2])
offset = eval(buf[a3:b3]) # XXX What if it overflows?
if tags.has_key(node):
print 'Duplicate key in tag table:', `node`
file, offset = map_offset(offset, indirlist)
tags[node] = file, offset, line
#
return tags
# Return true if buf[i:] begins with a label, after lower case conversion.
# The label argument must be in lower case.
#
def labelmatch(buf, i, label):
return string.lower(buf[i:i+len(label)]) == label
# Parse the indirection list.
# Return a list of (filename, offset) pairs ready for use.
#
def parse_indirlist(buf):
list = []
findindir = regexp.compile('^(.+):[ \t]*([0-9]+)$').match
i = 0
while 1:
match = findindir(buf, i)
if not match:
break
(a,b), (a1,b1), (a2,b2) = match
file = buf[a1:b1]
offset = eval(buf[a2:b2]) # XXX What if this gets overflow?
list.append(file, offset)
i = b
return list
# Map an offset through the indirection list.
# Return (filename, new_offset).
# If the list is empty, return the given offset and an empty file name.
#
def map_offset(offset, indirlist):
if not indirlist:
return '', offset
#
# XXX This could be done more elegant.
#
filex, offx = indirlist[0]
for i in range(len(indirlist)):
file1, off1 = indirlist[i]
if i+1 >= len(indirlist):
file2, off2 = '', 0x7fffffff
else:
file2, off2 = indirlist[i+1]
if off1 <= offset < off2:
# Add offx+2 to compensate for extra header.
# No idea whether this is always correct.
return file1, offset-off1 + offx+2
#
# XXX Shouldn't get here.
#
print 'Oops, map_offset fell through'
return '', offset # Not likely to get good results
#! /usr/local/python
# Watch line printer queues (only works with BSD 4.3 lpq).
#
# This brings up a window containing one line per printer argument.
#
# Each line gives a small summary of the printer's status and queue.
# The status tries to give as much relevant information as possible,
# and gives extra info if you have jobs in the queue.
#
# The line's background color gives a hint at the status: navajo white
# for idle, green if your job is now printing, yellow/orange for
# small/large queue, red for errors.
#
# To reduce the duration of the unresponsive time while it is waiting
# for an lpq subprocess to finish, it polls one printer every
# delay/len(printers) seconds. A tiny dot indicates the last printer
# updated. Hit the mouse button in the window to update the next one.
#
# To do:
# - add an argument to override the default delay
# - add arguments to override the default colors
# - better heuristic for small/large queue (and more colors!)
# - mouse clicks should update the printer clicked in
# - better visual appearance, e.g., boxes around the lines?
import posix
import sys
import time
import string
import stdwin
from stdwinevents import *
import mainloop
# Default parameters
DEF_PRINTER = 'oce' # This is CWI specific!
DEF_DELAY = 10
# Color assignments
c_unknown = stdwin.fetchcolor('white')
c_idle = stdwin.fetchcolor('navajo white')
c_ontop = stdwin.fetchcolor('green')
c_smallqueue = stdwin.fetchcolor('yellow')
c_bigqueue = stdwin.fetchcolor('orange')
c_error = stdwin.fetchcolor('red')
def main():
delay = DEF_DELAY
#
try:
thisuser = posix.environ['LOGNAME']
except:
thisuser = posix.environ['USER']
#
printers = sys.argv[1:]
if printers:
# Strip '-P' from printer names just in case
# the user specified it...
for i in range(len(printers)):
if printers[i][:2] == '-P':
printers[i] = printers[i][2:]
else:
if posix.environ.has_key('PRINTER'):
printers = [posix.environ['PRINTER']]
else:
printers = [DEF_PRINTER]
#
width = stdwin.textwidth('in')*20
height = len(printers) * stdwin.lineheight() + 5
stdwin.setdefwinsize(width, height)
stdwin.setdefscrollbars(0, 0)
#
win = stdwin.open('lpwin')
#
win.printers = printers
win.colors = [c_unknown] * len(printers)
win.texts = printers[:]
win.next = 0
win.delay = DEF_DELAY
win.thisuser = thisuser
win.dispatch = lpdispatch
#
win.settimer(1)
#
mainloop.register(win)
mainloop.mainloop()
def lpdispatch(type, win, detail):
if type == WE_CLOSE or type == WE_CHAR and detail in ('q', 'Q'):
mainloop.unregister(win)
elif type == WE_DRAW:
drawproc(win)
elif type == WE_TIMER:
update(win)
win.change((0,0), (10000, 10000))
elif type == WE_MOUSE_UP:
win.settimer(1)
def drawproc(win):
d = win.begindrawing()
offset = d.textwidth('.')
h, v = 0, 0
for i in range(len(win.printers)):
text = win.texts[i]
color = win.colors[i]
d.setbgcolor(color)
d.erase((h, v), (h+10000, v+d.lineheight()))
if (i+1) % len(win.printers) == win.next and color <> c_unknown:
d.text((h, v), '.')
d.text((h+offset, v), text)
v = v + d.lineheight()
def update(win):
i = win.next
win.next = (i+1) % len(win.printers)
win.texts[i], win.colors[i] = makestatus(win.printers[i], win.thisuser)
win.settimer(int(win.delay * 10.0 / len(win.printers)))
def makestatus(name, thisuser):
pipe = posix.popen('lpq -P' + name + ' 2>&1', 'r')
lines = []
users = {}
aheadbytes = 0
aheadjobs = 0
userseen = 0
totalbytes = 0
totaljobs = 0
color = c_unknown
while 1:
line = pipe.readline()
if not line: break
fields = string.split(line)
n = len(fields)
if len(fields) >= 6 and fields[n-1] == 'bytes':
rank = fields[0]
user = fields[1]
job = fields[2]
files = fields[3:-2]
bytes = eval(fields[n-2])
if user == thisuser:
userseen = 1
if aheadjobs == 0:
color = c_ontop
elif not userseen:
aheadbytes = aheadbytes + bytes
aheadjobs = aheadjobs + 1
totalbytes = totalbytes + bytes
totaljobs = totaljobs + 1
if color == c_unknown:
color = c_smallqueue
elif color == c_smallqueue:
color = c_bigqueue
if users.has_key(user):
ujobs, ubytes = users[user]
else:
ujobs, ubytes = 0, 0
ujobs = ujobs + 1
ubytes = ubytes + bytes
users[user] = ujobs, ubytes
else:
if fields and fields[0] <> 'Rank':
line = string.strip(line)
if line == 'no entries':
line = name + ': idle'
if color == c_unknown:
color = c_idle
elif line[-22:] == ' is ready and printing':
line = line[:-22]
else:
line = name + ': ' + line
color = c_error
lines.append(line)
#
if totaljobs:
line = `(totalbytes+1023)/1024` + ' K'
if totaljobs <> len(users):
line = line + ' (' + `totaljobs` + ' jobs)'
if len(users) == 1:
line = line + ' for ' + users.keys()[0]
else:
line = line + ' for ' + `len(users)` + ' users'
if userseen:
if aheadjobs == 0:
line = line + ' (' + thisuser + ' first)'
else:
line = line + ' (' + `(aheadbytes+1023)/1024`
line = line + ' K before ' + thisuser + ')'
lines.append(line)
#
sts = pipe.close()
if sts:
lines.append('lpq exit status ' + `sts`)
color = c_error
return string.joinfields(lines, ': '), color
main()
#! /usr/local/python
# A minimal single-window text editor using STDWIN's text objects.
#
# Usage: microedit file
#
# This is not intended as a real application but as an introduction
# to STDWIN programming in Python, especially text objects.
# Once you understand microedit.py, study miniedit.py to learn
# about multiple windows and menus, cut and paste, etc.
import sys
import stdwin
from stdwinevents import *
# Main program
#
def main():
#
# Get the filename argument and read its contents as one very
# large string.
# An exception will terminate the program if there is no argument
# or if the file could not be read...
#
filename = sys.argv[1]
fp = open(filename, 'r')
contents = fp.read()
del fp # Close the file
#
# Create the window, using the filename as window title
#
window = stdwin.open(filename)
#
# Add a simple File menu to the window with two items
#
filemenu = window.menucreate('File')
filemenu.additem('Save', 'S') # Item 0 (shortcut Meta-S)
filemenu.additem('Save As...') # Item 1
#
# Create a text object occupying the entire window
# and fill it with the file's contents
#
corner = window.getwinsize() # (width, height)
area = (0, 0), corner # Rectangle as large as the window
text = window.textcreate(area)
text.settext(contents)
del contents # Get rid of contents object
fix_textsize(window, text) # Set document size accordingly
#
# Main event loop -- stop if a close request comes in.
#
# STDWIN applications should regularly call stdwin.getevent()
# otherwise the windows won't function as expected.
#
while 1:
#
# Get the next event
#
type, w, detail = e = stdwin.getevent()
#
# Event decoding switch
#
if type == WE_CLOSE:
break # Stop (no check for saved file!)
elif type == WE_SIZE:
#
# The window was resized --
# let the text object recompute the line breaks
# and change the document size accordingly,
# so scroll bars will work
#
fix_textsize(window, text)
elif type == WE_MENU:
#
# Execute a file menu request (our only menu)
#
menu, item = detail
if item == 0:
#
# "Save": save to the current filename
#
dummy = save_file(window, text, filename)
elif item == 1:
#
# "Save As": ask a new filename, save to it,
# and make it the current filename
#
# NB: askfile raises KeyboardInterrupt
# if the user cancels the dialog, hence
# the try statement
#
try:
newfile = stdwin.askfile( \
'Save as:', filename, 1)
except KeyboardInterrupt:
newfile = ''
if newfile:
if save_file(window, text, newfile):
filename = newfile
window.settitle(filename)
elif text.event(e):
#
# The text object has handled the event.
# Fix the document size if necessary.
# Note: this sometimes fixes the size
# unnecessarily, e.g., for arrow keys.
#
if type in (WE_CHAR, WE_COMMAND):
fix_docsize(window, text)
# Save the window's contents to the filename.
# If the open() fails, put up a warning message and return 0;
# if the save succeeds, return 1.
#
def save_file(window, text, filename):
#
# Open the file for writing, handling exceptions
#
try:
fp = open(filename, 'w')
except RuntimeError:
stdwin.message('Cannot create ' + filename)
return 0
#
# Get the contents of the text object as one very long string
#
contents = text.gettext()
#
# Write the contents to the file
#
fp.write(contents)
#
# The file is automatically closed when this routine returns
#
return 1
# Change the size of the text object to fit in the window,
# and then fix the window's document size to fit around the text object.
#
def fix_textsize(window, text):
#
# Compute a rectangle as large as the window
#
corner = window.getwinsize() # (width, height)
area = (0, 0), (corner)
#
# Move the text object to this rectangle.
# Note: text.move() ignores the bottom coordinate!
#
text.move(area)
#
# Now fix the document size accordingly
#
fix_docsize(window, text)
# Fix the document size, after the text has changed
#
def fix_docsize(window, text):
#
# Get the actual rectangle occupied by the text object.
# This has the same left, top and right, but a different bottom.
#
area = text.getrect()
#
# Compute the true height of the text object
#
origin, corner = area
width, height = corner
#
# Set the document height to the text object's height.
# The width is zero since we don't want a horizontal scroll bar.
#
window.setdocsize(0, height)
# Once all functions are defined, call main()
#
main()
#! /usr/local/python
# A miniature multi-window editor using STDWIN's text objects.
#
# Usage: miniedit [file] ...
#
# The user interface is similar to that of the miniedit demo application
# in C that comes with STDWIN.
#
# XXX need to comment the functions
# XXX Not yet implemented:
# disabling menu entries for inapplicable actions
# Find operations
import sys
import stdwin
from stdwinevents import *
# Constant: list of WE_COMMAND events that (may) change the text buffer
# so we can decide whether to set the 'changed' flag.
# Note that it is possible for such a command to fail (a backspace
# at the beginning of the buffer) but we'll set the changed flag anyway
# -- it's too complicated to check this condition right now.
#
changing = [WC_RETURN, WC_TAB, WC_BACKSPACE]
# The list of currently open windows;
# this is maintained so we can stop when there are no windows left
#
windows = []
# A note on window data attributes (set by open_window):
#
# w.textobject the window's text object
# w.changed true when the window's text is changed
# w.filename filename connected to the window; '' if none
# Main program
#
def main():
#
# Set a reasonable default window size.
# If we are using a fixed-width font this will open a 80x24 window;
# for variable-width fonts we approximate this based on an average
#
stdwin.setdefwinsize(40*stdwin.textwidth('in'), 24*stdwin.lineheight())
#
# Create global menus (as local variables)
#
filemenu = make_file_menu(stdwin)
editmenu = make_edit_menu(stdwin)
findmenu = make_find_menu(stdwin)
#
# Get the list of files from the command line (maybe none)
#
files = sys.argv[1:]
#
# Open any files -- errors will be reported but do won't stop us
#
for filename in files:
open_file(filename)
#
# If there were no files, or none of them could be opened,
# put up a dialog asking for a filename
#
if not windows:
try:
open_dialog(None)
except KeyboardInterrupt:
pass # User cancelled
#
# If the dialog was cancelled, create an empty new window
#
if not windows:
new_window(None)
#
# Main event loop -- stop when we have no open windows left
#
while windows:
#
# Get the next event -- ignore interrupts
#
try:
type, window, detail = event = stdwin.getevent()
except KeyboardInterrupt:
type, window, detail = event = WE_NONE, None, None
#
# Event decoding switch
#
if not window:
pass # Ignore such events
elif type == WE_MENU:
#
# Execute menu operation
#
menu, item = detail
try:
menu.actions[item](window)
except KeyboardInterrupt:
pass # User cancelled
elif type == WE_CLOSE:
#
# Close a window
#
try:
close_dialog(window)
except KeyboardInterrupt:
pass # User cancelled
elif type == WE_SIZE:
#
# A window was resized --
# let the text object recompute the line breaks
# and change the document size accordingly,
# so scroll bars will work
#
fix_textsize(window)
elif window.textobject.event(event):
#
# The event was eaten by the text object --
# set the changed flag if not already set
#
if type == WE_CHAR or \
type == WE_COMMAND and detail in changing:
window.changed = 1
fix_docsize(window)
#
# Delete all objects that may still reference the window
# in the event -- this is needed otherwise the window
# won't actually be closed and may receive further
# events, which will confuse the event decoder
#
del type, window, detail, event
def make_file_menu(object):
menu = object.menucreate('File')
menu.actions = []
additem(menu, 'New', 'N', new_window)
additem(menu, 'Open..', 'O', open_dialog)
additem(menu, '', '', None)
additem(menu, 'Save', 'S', save_dialog)
additem(menu, 'Save As..', '', save_as_dialog)
additem(menu, 'Save a Copy..', '', save_copy_dialog)
additem(menu, 'Revert', 'R', revert_dialog)
additem(menu, 'Quit', 'Q', quit_dialog)
return menu
def make_edit_menu(object):
menu = object.menucreate('Edit')
menu.actions = []
additem(menu, 'Cut', 'X', do_cut)
additem(menu, 'Copy', 'C', do_copy)
additem(menu, 'Paste', 'V', do_paste)
additem(menu, 'Clear', 'B', do_clear)
additem(menu, 'Select All', 'A', do_select_all)
return menu
def make_find_menu(object):
menu = object.menucreate('Find')
menu.actions = []
# XXX
return menu
def additem(menu, text, shortcut, function):
if shortcut:
menu.additem(text, shortcut)
else:
menu.additem(text)
menu.actions.append(function)
def open_dialog(current_ignored):
filename = stdwin.askfile('Open file:', '', 0)
open_file(filename)
def open_file(filename):
try:
fp = open(filename, 'r')
except RuntimeError:
stdwin.message(filename + ': cannot open')
return # Error, forget it
try:
contents = fp.read()
except RuntimeError:
stdwin.message(filename + ': read error')
return # Error, forget it
del fp # Close the file
open_window(filename, filename, contents)
def new_window(current_ignored):
open_window('', 'Untitled', '')
def open_window(filename, title, contents):
try:
window = stdwin.open(title)
except RuntimeError:
stdwin.message('cannot open new window')
return # Error, forget it
window.textobject = window.textcreate((0, 0), window.getwinsize())
window.textobject.settext(contents)
window.changed = 0
window.filename = filename
fix_textsize(window)
windows.append(window)
def quit_dialog(window):
for window in windows[:]:
close_dialog(window)
def close_dialog(window):
if window.changed:
prompt = 'Save changes to ' + window.gettitle() + ' ?'
if stdwin.askync(prompt, 1):
save_dialog(window)
if window.changed:
return # Save failed (not) cancelled
windows.remove(window)
del window.textobject
def save_dialog(window):
if not window.filename:
save_as_dialog(window)
return
if save_file(window, window.filename):
window.changed = 0
def save_as_dialog(window):
prompt = 'Save ' + window.gettitle() + ' as:'
filename = stdwin.askfile(prompt, window.filename, 1)
if save_file(window, filename):
window.filename = filename
window.settitle(filename)
window.changed = 0
def save_copy_dialog(window):
prompt = 'Save a copy of ' + window.gettitle() + ' as:'
filename = stdwin.askfile(prompt, window.filename, 1)
void = save_file(window, filename)
def save_file(window, filename):
try:
fp = open(filename, 'w')
except RuntimeError:
stdwin.message(filename + ': cannot create')
return 0
contents = window.textobject.gettext()
try:
fp.write(contents)
except RuntimeError:
stdwin.message(filename + ': write error')
return 0
return 1
def revert_dialog(window):
if not window.filename:
stdwin.message('This window has no file to revert from')
return
if window.changed:
prompt = 'Really read ' + window.filename + ' back from file?'
if not stdwin.askync(prompt, 1):
return
try:
fp = open(window.filename, 'r')
except RuntimeError:
stdwin.message(filename + ': cannot open')
return
contents = fp.read()
del fp # Close the file
window.textobject.settext(contents)
window.changed = 0
fix_docsize(window)
def fix_textsize(window):
corner = window.getwinsize()
area = (0, 0), (corner)
window.textobject.move(area)
fix_docsize(window)
def fix_docsize(window):
area = window.textobject.getrect()
origin, corner = area
width, height = corner
window.setdocsize(0, height)
def do_cut(window):
selection = window.textobject.getfocustext()
if not selection:
stdwin.fleep() # Nothing to cut
elif not window.setselection(WS_PRIMARY, selection):
stdwin.fleep() # Window manager glitch...
else:
stdwin.rotatecutbuffers(1)
stdwin.setcutbuffer(0, selection)
window.textobject.replace('')
window.changed = 1
fix_docsize(window)
def do_copy(window):
selection = window.textobject.getfocustext()
if not selection:
stdwin.fleep() # Nothing to cut
elif not window.setselection(WS_PRIMARY, selection):
stdwin.fleep() # Window manager glitch...
else:
stdwin.rotatecutbuffers(1)
stdwin.setcutbuffer(0, selection)
def do_paste(window):
selection = stdwin.getselection(WS_PRIMARY)
if not selection:
selection = stdwin.getcutbuffer(0)
if not selection:
stdwin.fleep() # Nothing to paste
else:
window.textobject.replace(selection)
window.changed = 1
fix_docsize(window)
def do_clear(window):
first, last = window.textobject.getfocus()
if first == last:
stdwin.fleep() # Nothing to clear
else:
window.textobject.replace('')
window.changed = 1
fix_docsize(window)
def do_select_all(window):
window.textobject.setfocus(0, 0x7fffffff) # XXX Smaller on the Mac!
main()
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment