diff options
Diffstat (limited to 'Demo/tkinter/guido/manpage.py')
-rw-r--r-- | Demo/tkinter/guido/manpage.py | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/Demo/tkinter/guido/manpage.py b/Demo/tkinter/guido/manpage.py new file mode 100644 index 0000000..750c675 --- /dev/null +++ b/Demo/tkinter/guido/manpage.py @@ -0,0 +1,215 @@ +# Widget to display a man page + +import os +import re +import sys + +from tkinter import * +from tkinter.font import Font +from tkinter.scrolledtext import ScrolledText + +# XXX Recognizing footers is system dependent +# (This one works for IRIX 5.2 and Solaris 2.2) +footerprog = re.compile( + '^ Page [1-9][0-9]*[ \t]+\|^.*Last change:.*[1-9][0-9]*\n') +emptyprog = re.compile('^[ \t]*\n') +ulprog = re.compile('^[ \t]*[Xv!_][Xv!_ \t]*\n') + + +class EditableManPage(ScrolledText): + """Basic Man Page class -- does not disable editing.""" + + def __init__(self, master=None, **cnf): + ScrolledText.__init__(self, master, **cnf) + + bold = Font(font=self['font']).copy() + bold.config(weight='bold') + italic = Font(font=self['font']).copy() + italic.config(slant='italic') + + # Define tags for formatting styles + self.tag_config('X', underline=1) + self.tag_config('!', font=bold) + self.tag_config('_', font=italic) + + # Set state to idle + self.fp = None + self.lineno = 0 + + def busy(self): + """Test whether we are busy parsing a file.""" + return self.fp != None + + def kill(self): + """Ensure we're not busy.""" + if self.busy(): + self._endparser() + + def asyncparsefile(self, fp): + """Parse a file, in the background.""" + self._startparser(fp) + self.tk.createfilehandler(fp, READABLE, + self._filehandler) + + parsefile = asyncparsefile # Alias + + def _filehandler(self, fp, mask): + """I/O handler used by background parsing.""" + nextline = self.fp.readline() + if not nextline: + self._endparser() + return + self._parseline(nextline) + + def syncparsefile(self, fp): + """Parse a file, now (cannot be aborted).""" + self._startparser(fp) + while True: + nextline = fp.readline() + if not nextline: + break + self._parseline(nextline) + self._endparser() + + def _startparser(self, fp): + """Initialize parsing from a particular file -- must not be busy.""" + if self.busy(): + raise RuntimeError('startparser: still busy') + fp.fileno() # Test for file-ness + self.fp = fp + self.lineno = 0 + self.ok = 0 + self.empty = 0 + self.buffer = None + savestate = self['state'] + self['state'] = NORMAL + self.delete('1.0', END) + self['state'] = savestate + + def _endparser(self): + """End parsing -- must be busy, need not be at EOF.""" + if not self.busy(): + raise RuntimeError('endparser: not busy') + if self.buffer: + self._parseline('') + try: + self.tk.deletefilehandler(self.fp) + except TclError: + pass + self.fp.close() + self.fp = None + del self.ok, self.empty, self.buffer + + def _parseline(self, nextline): + """Parse a single line.""" + if not self.buffer: + # Save this line -- we need one line read-ahead + self.buffer = nextline + return + if emptyprog.match(self.buffer): + # Buffered line was empty -- set a flag + self.empty = 1 + self.buffer = nextline + return + textline = self.buffer + if ulprog.match(nextline): + # Next line is properties for buffered line + propline = nextline + self.buffer = None + else: + # Next line is read-ahead + propline = None + self.buffer = nextline + if not self.ok: + # First non blank line after footer must be header + # -- skip that too + self.ok = 1 + self.empty = 0 + return + if footerprog.match(textline): + # Footer -- start skipping until next non-blank line + self.ok = 0 + self.empty = 0 + return + savestate = self['state'] + self['state'] = NORMAL + if TkVersion >= 4.0: + self.mark_set('insert', 'end-1c') + else: + self.mark_set('insert', END) + if self.empty: + # One or more previous lines were empty + # -- insert one blank line in the text + self._insert_prop('\n') + self.lineno = self.lineno + 1 + self.empty = 0 + if not propline: + # No properties + self._insert_prop(textline) + else: + # Search for properties + p = '' + j = 0 + for i in range(min(len(propline), len(textline))): + if propline[i] != p: + if j < i: + self._insert_prop(textline[j:i], p) + j = i + p = propline[i] + self._insert_prop(textline[j:]) + self.lineno = self.lineno + 1 + self['state'] = savestate + + def _insert_prop(self, str, prop = ' '): + """Insert a string at the end, with at most one property (tag).""" + here = self.index(AtInsert()) + self.insert(AtInsert(), str) + if TkVersion <= 4.0: + tags = self.tag_names(here) + for tag in tags: + self.tag_remove(tag, here, AtInsert()) + if prop != ' ': + self.tag_add(prop, here, AtInsert()) + + +class ReadonlyManPage(EditableManPage): + """Readonly Man Page class -- disables editing, otherwise the same.""" + + def __init__(self, master=None, **cnf): + cnf['state'] = DISABLED + EditableManPage.__init__(self, master, **cnf) + +# Alias +ManPage = ReadonlyManPage + +# usage: ManPage [manpage]; or ManPage [-f] file +# -f means that the file is nroff -man output run through ul -i +def main(): + # XXX This directory may be different on your system + MANDIR = '' + DEFAULTPAGE = 'Tcl' + formatted = 0 + if sys.argv[1:] and sys.argv[1] == '-f': + formatted = 1 + del sys.argv[1] + if sys.argv[1:]: + name = sys.argv[1] + else: + name = DEFAULTPAGE + if not formatted: + if name[-2:-1] != '.': + name = name + '.n' + name = os.path.join(MANDIR, name) + root = Tk() + root.minsize(1, 1) + manpage = ManPage(root, relief=SUNKEN, borderwidth=2) + manpage.pack(expand=1, fill=BOTH) + if formatted: + fp = open(name, 'r') + else: + fp = os.popen('nroff -man -c %s | ul -i' % name, 'r') + manpage.parsefile(fp) + root.mainloop() + +if __name__ == '__main__': + main() |