| #! /usr/local/bin/python |
| |
| # www13.py -- display the contents of a URL in a Text widget |
| # - set window title |
| # - make window resizable |
| # - update display while reading |
| # - vertical scroll bar |
| # - rewritten as class |
| # - editable url entry and reload button |
| # - error dialog |
| # - menu bar; added 'master' option to constructor |
| # - Added HTML parser |
| |
| import sys |
| import urllib |
| from Tkinter import * |
| import Dialog |
| import tkfmt |
| import htmllib |
| |
| def main(): |
| if len(sys.argv) != 2 or sys.argv[1][:1] == '-': |
| print "Usage:", sys.argv[0], "url" |
| sys.exit(2) |
| url = sys.argv[1] |
| tk = Tk() |
| tk.withdraw() |
| viewer = Viewer(tk) |
| viewer.load(url) |
| viewer.go() |
| |
| class Viewer: |
| |
| def __init__(self, master = None): |
| # Create root window |
| if master is None: |
| self.root = self.master = Tk() |
| else: |
| self.master = master |
| self.root = Toplevel(self.master) |
| self.root.minsize(1, 1) |
| |
| # Create menu bar |
| self.mbar = Frame(self.root, |
| {'relief': 'raised', |
| 'border': 2}) |
| self.mbar.pack({'fill': 'x'}) |
| |
| # Create File menu |
| self.filebutton = Menubutton(self.mbar, {'text': 'File'}) |
| self.filebutton.pack({'side': 'left'}) |
| |
| self.filemenu = Menu(self.filebutton) |
| self.filebutton['menu'] = self.filemenu |
| |
| # Create Edit menu |
| self.editbutton = Menubutton(self.mbar, {'text': 'Edit'}) |
| self.editbutton.pack({'side': 'left'}) |
| |
| self.editmenu = Menu(self.editbutton) |
| self.editbutton['menu'] = self.editmenu |
| |
| # Magic so you can swipe from one button to the next |
| self.mbar.tk_menuBar(self.filebutton, self.editbutton) |
| |
| # Populate File menu |
| self.filemenu.add('command', {'label': 'New', |
| 'command': self.new_command}) |
| self.filemenu.add('command', {'label': 'Open...', |
| 'command': self.open_command}) |
| self.filemenu.add('command', {'label': 'Clone', |
| 'command': self.clone_command}) |
| self.filemenu.add('separator') |
| self.filemenu.add('command', {'label': 'Close', |
| 'command': self.close_command}) |
| self.filemenu.add('command', {'label': 'Quit', |
| 'command': self.quit_command}) |
| |
| # Populate Edit menu |
| pass |
| |
| # Create topframe for the entry and button |
| self.topframe = Frame(self.root) |
| self.topframe.pack({'fill': 'x'}) |
| |
| # Create a label in front of the entry |
| self.urllabel = Label(self.topframe, {'text': 'URL:'}) |
| self.urllabel.pack({'side': 'left'}) |
| |
| # Create the entry containing the URL |
| self.entry = Entry(self.topframe, |
| {'relief': 'sunken', 'border': 2}) |
| self.entry.pack({'side': 'left', 'fill': 'x', 'expand': 1}) |
| self.entry.bind('<Return>', self.loadit) |
| |
| # Create the button |
| self.reload = Button(self.topframe, |
| {'text': 'Reload', |
| 'command': self.reload}) |
| self.reload.pack({'side': 'right'}) |
| |
| # Create botframe for the text and scrollbar |
| self.botframe = Frame(self.root) |
| self.botframe.pack({'fill': 'both', 'expand': 1}) |
| |
| # The Scrollbar *must* be created first |
| self.vbar = Scrollbar(self.botframe) |
| self.vbar.pack({'fill': 'y', 'side': 'right'}) |
| self.text = Text(self.botframe) |
| self.text.pack({'expand': 1, 'fill': 'both', 'side': 'left'}) |
| |
| # Link Text widget and Scrollbar |
| self.text['yscrollcommand'] = (self.vbar, 'set') |
| self.vbar['command'] = (self.text, 'yview') |
| |
| self.url = None |
| |
| def load(self, url): |
| # Load a new URL into the window |
| fp, url = self.urlopen(url) |
| if not fp: |
| return |
| |
| self.url = url |
| |
| self.root.title(url) |
| |
| self.entry.delete('0', 'end') |
| self.entry.insert('end', url) |
| |
| self.text.delete('0.0', 'end') |
| |
| f = tkfmt.TkFormatter(self.text) |
| p = htmllib.FormattingParser(f, htmllib.X11Stylesheet) |
| |
| while 1: |
| line = fp.readline() |
| if not line: break |
| if line[-2:] == '\r\n': line = line[:-2] + '\n' |
| p.feed(line) |
| self.root.update_idletasks() |
| |
| p.close() |
| |
| fp.close() |
| |
| def urlopen(self, url): |
| # Open a URL -- |
| # return (fp, url) if successful |
| # display dialog and return (None, url) for errors |
| try: |
| fp = urllib.urlopen(url) |
| except IOError, msg: |
| import types |
| if type(msg) == types.TupleType and len(msg) == 4: |
| if msg[1] == 302: |
| m = msg[3] |
| if m.has_key('location'): |
| url = m['location'] |
| return self.urlopen(url) |
| elif m.has_key('uri'): |
| url = m['uri'] |
| return self.urlopen(url) |
| self.errordialog(IOError, msg) |
| fp = None |
| return fp, url |
| |
| def errordialog(self, exc, msg): |
| # Display an error dialog -- return when the user clicks OK |
| Dialog.Dialog(self.root, { |
| 'text': str(msg), |
| 'title': exc, |
| 'bitmap': 'error', |
| 'default': 0, |
| 'strings': ('OK',), |
| }) |
| |
| def go(self): |
| # Start Tk main loop |
| self.root.mainloop() |
| |
| def reload(self, *args): |
| # Callback for Reload button |
| if self.url: |
| self.load(self.url) |
| |
| def loadit(self, *args): |
| # Callback for <Return> event in entry |
| self.load(self.entry.get()) |
| |
| def new_command(self): |
| # File/New... |
| Viewer(self.master) |
| |
| def clone_command(self): |
| # File/Clone |
| v = Viewer(self.master) |
| v.load(self.url) |
| |
| def open_command(self): |
| # File/Open... |
| print "File/Open...: Not implemented" |
| |
| def close_command(self): |
| # File/Close |
| self.destroy() |
| |
| def quit_command(self): |
| # File/Quit |
| self.root.quit() |
| |
| def destroy(self): |
| # Destroy this window |
| self.root.destroy() |
| if self.master is not self.root and not self.master.children: |
| self.master.quit() |
| |
| main() |