# -*-mode: tcl; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- # # $Id$ # """TkHtml Wrapper for the TkHtml widget from http://www.hwaci.com. """ import Tkinter __require__ = 1 __tk_call__ = None # This function is not implemented yet. token_append_implemented = 0 def __setup_globals(master): global __require__, __tk_call__ if __require__: master.tk.call("package","require","Tkhtml") __tk_call__ = master.tk.call __require__ = None def reformat(from_, to, text): "Reformat a string to another representation: plain, http, url, html" if __require__: __setup_globals(Tkinter._default_root) return __tk_call__("html", "reformat", from_, to, text) def uri_join(scheme, authority, path, query, fragment): "Join the five components of the URI together." if __require__: __setup_globals(Tkinter._default_root) return __tk_call__("html", "urljoin", scheme, authority, path, query, fragment) def uri_split(uri): "Takes an URI and splits it into it's five main components." if __require__: __setup_globals(Tkinter._default_root) return __tk_call__("html", "urlsplit", uri) class Html(Tkinter.Widget): """Html widget""" def __init__(self, master=None, cfg={}, **kw): master.tk.call("package", "require", "Tkhtml") Tkinter.Widget.__init__(self, master, 'html', cfg, kw) def parse(self, text): """Add the given HTML-TEXT to the end of any text previously received through the parse command and parses as much of the text as possible into tokens. Afterwards, the display is updated to show the new tokens, if they're visible. """ self.tk.call(self._w, "parse", text) def clear(self): "Clear the widget." self.tk.call(self._w, "clear") def href(self, x, y): "Returns the href at coordinates X, Y" return self.tk.call(self._w, "href", x, y) def index(self, index, count=None, units=None): "Translates 'index' into it's cannonical form." cmd = (index,) if count and units: cmd = cmd + (count,units) return self.tk.call(self._w, "index", *cmd) def insert(self, index): """Causes insertion point to be position immediately after the character at 'index'.""" return self.tk.call(self._w, "insert", index) def names(self): """This command causes the widget to scan the entire text of the document looking for tags of the form: It returns a list of values of the 'name=...' fields. The vertical position of the widget can be moved to any of these names using the 'WIDGET yview NAME' command described below. """ return self.tk.call(self._w, "names") def resolver(self, *args): """The resolver specified by the -resolvercommand option is called with the base URI of the document followed by the remaining arguments to this command. The result of this command is the result of the -resolvercommand script. """ return self.tk.call(self._w, "resolver", *args) def selection(self, subcommand, *args): """The selection() widget command is used to control the selection. WIDGET selection clear Clear the current selection. WIDGET selection set START END Change the selection to the text between the START and END indices. """ return self.tk.call(self._w, "selection", *args) def selection_clear(self): "Clear the current selection." return self.selection("clear") def selection_set(self, start, end): "Set the selection to the text between start and end indices." return self.selection("set", start, end) def text(self, *args): """There are several token commands. They all have the common property that they directly manipulate the text which is currently displayed. This could be used to build a WYSIWYG html editor. """ return self.tk.call(self._w, "text", *args) def text_ascii(self, index1, index2): """Returns plain ascii text that represents all characters between index1 and index2. Formatting tags are omitted. The index1 character is included but index2 is excluded. """ return self.text("ascii", index1, index2) def text_delete(index1, index2): """All text from index1 up to --but not including-- index2 is removed and the display is updated accordingly. """ return self.text("delete", index1, index2) def text_html(index1, index2): """Returns HTML text that represents all characters and formatting tags between index1 and index2, excluding the character at index2. """ return self.text("html", index1, index2) def text_insert(self, index, text): """Inserts one or more characters immediately before the character whose index is given. """ return self.text("insert", index, text) def token(self, subcommand, *args): """There are several token commands. They all have the common property that they involve the list of tokens into which the HTML text is parsed. Some of the following subcommands make use of indices. The character number of these indices is ignored, since these commands only deal with tokens. """ return self.tk.call(self._w, "token", subcommand, *args) def token_append(self, tag, *args): "The token is appended to the current list of tokens." return self.token("append", tag.upper(), *args) def token_delete(self, index1, index2=None): """The token at the given index is deleted; if the second index is specified, then the range of tokens is deleted (second index inclusive). """ if index2: return self.token("delete", index1, index2) return self.token("delete", index1) def token_find(self, tag): """Locats all tokens with the given tag and returns a list of them all. Each element of the list contains the index of the token and the token's arguments. """ return self.token("find", tag) def token_get(self, index1, index2=None): """Returns a list of tokens in the range index1 through index2; each element of the list consists of the token tag followed by it's arguments. """ if index2: return self.token("get", index1, index2) return self.token("get", index1) def token_handler(self, tag, handler=None): """This command allows special processing to occur for selected tokens in the HTML input stream. The tag argument is either "Text" or "Space", or the name of an HTML tag ("H3", "/A", etc). If a none-None function is specified for the handler, then when instances of that tag are encountered by the parser, the parser calls the corresponding function instead of appending the token to the end of the token list. Before calling the function, two arguments are appended: 1. The tag (ex: "H3", "/TABLE", etc) 2. A list of name/value pairs describing all tag arguments. An empty handler causes the default processing to occur for the tag. If the function argument is ommitted altogether, then the current value for the token handler for the given tag is returned. Only one handler for each token type may be defined. If a new handler for a given tag is set, then the previous handler is replaced. """ if not handler: return self.token("handler", tag) if handler in ("", "{}"): # Delete the current handler for this tag. return self.token("handler", tag, handler) elif callable(handler): name = self.register(handler) self.token("handler", tag, name) else: raise ValueError("handler must be None, '' or callable.") def token_handler_delete(self, tag): "Deletes a handler for a token type." return self.token("handler", tag, "{}") def token_insert(self, index, tag, *arguments): """Inserts a single token given by tag and arguments into the token list immediately before the token given by index. """ return self.token("insert", index, *arguments) def xview(self, *args): "Used to control horizontal scrolling." if args: return self.tk.call(self._w, "xview", *args) coords = map(float, self.tk.call(self._w, "xview").split()) return tuple(coords) def xview_moveto(self, fraction): """Adjusts horizontal position of the widget so that fraction of the horizontal span of the document is off-screen to the left. """ return self.xview("moveto", fraction) def xview_scroll(self, number, what): """Shifts the view in the window according to number and what; number is an integer, and what is either 'units' or 'pages'. """ return self.xview("scroll", number, what) def yview(self, *args): "Used to control the vertical position of the document." if args: return self.tk.call(self._w, "yview", *args) coords = map(float, self.tk.call(self._w, "yview").split()) return tuple(coords) def yview_name(self, name): """Adjust the vertical position of the document so that the tag is visible and preferably near the top of the window. """ return self.yview(name) def yview_moveto(self, fraction): """Adjust the vertical position of the document so that fraction of the document is off-screen above the visible region. """ return self.yview("moveto", fraction) def yview_scroll(self, number, what): """Shifts the view in the window up or down, according to number and what. 'number' is an integer, and 'what' is either 'units' or 'pages'. """ return self.yview("scroll", number, what) ## ## FINIS ## class TestApp: def __init__(self): import Tkinter, tkFileDialog, TkHtml root = self._root = Tkinter.Tk() root.title("Html widget test") root["menu"] = mb = self._mb = Tkinter.Menu(root) file = self._file = Tkinter.Menu(mb) mb.add_cascade(menu=file, label="File", underline=0) html = self._html = TkHtml.Html(root) vsb = self._vsb = Tkinter.Scrollbar(root, orient="v", command=html.yview) html.configure(yscrollcommand=vsb.set) vsb.pack(side="right", fill="y", expand="yes") html.pack(side="left", fill="both", expand="yes") self._dialog = tkFileDialog.Open(root) # Bind some menu options. file.add_command(label="Default", underline=0, command=self.default) file.add_command(label="Open", underline=0, command=self.open) file.add_command(label="Clear", underline=0, command=html.clear) file.add_separator() file.add_command(label="Quit", underline=0, command=root.destroy) # Create some image placeholders. biggray = Tkinter.PhotoImage(data="R0lGODdhPAA+APAAALi4uAAAACwAAA" "AAPAA+AAACQISPqcvtD6OctNqLs968+w+G4kiW5omm6sq27g" "vH8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNFg" "sAO///") smallgray = Tkinter.PhotoImage(data="R0lGODdhOAAYAPAAALi4uAAAACwA" "AAAAOAAYAAACI4SPqcvtD6OctNqLs968+w+G4kiW5omm6sq2" "7gvH8kzX9m0VADv/") nogifbig = Tkinter.PhotoImage(data="R0lGODdhJAAkAPEAAACQkADQ0PgAA" "AAAACwAAAAAJAAkAAACmISPqcsQD6OcdJqKM71PeK15AsSJH" "0iZY1CqqKSurfsGsex08XuTuU7L9HywHWZILAaVJssvgoREk" "5PolFo1XrHZ29IZ8oo0HKEYVDYbyc/jFhz2otvdcyZdF68qe" "Kh2DZd3AtS0QWcDSDgWKJXY+MXS9qY4+JA2+Vho+YPpFzSji" "TIEWslDQ1rDhPOY2sXVOgeb2kBbu1AAADv/") nogifsm = Tkinter.PhotoImage(data="R0lGODdhEAAQAPEAAACQkADQ0PgAAA" "AAACwAAAAAEAAQAAACNISPacHtD4IQz80QJ60as25d3idKZd" "R0IIOm2ta0Lhw/Lz2S1JqvK8ozbTKlEIVYceWSjwIAO///") tl = Tkinter.Toplevel(root) for img in (biggray, smallgray, nogifbig, nogifsm): Tkinter.Label(tl, image=img).pack() def default(self): self._html.clear() self._html.parse(open(r"tkhtml-spec.html").read()) def open(self): file = self._dialog.show() if file: self._html.clear() self._html.parse(open(file).read()) if __name__ == "__main__": test = TestApp()