""" Text widget specialized to display the HTML formatted Python documentation as generated by Sphinx. The renderer is fragile enough that you won't likely get very far if you feed it anything but the IDLE help file. """ from tkinter import Tk, Text, Scrollbar, Toplevel from html.parser import HTMLParser import os class SphinxHTMLViewer(Text): class MyHTMLParser(HTMLParser): def __init__(self, t): HTMLParser.__init__(self) self.t = t # text widget we're rendering into self.tags = '' # current text tags to apply self.show = False # used so we exclude page navigation self.hdrlink = False # used so we don't show header links self.level = 0 # indentation level self.pre = False # displaying preformatted text self.hprefix = '' # strip e.g. '25.5' from headings self.nested_dl = False # if we're in a nested
def indent(self, amt=1): self.level += amt self.tags = '' if self.level == 0 else 'l'+str(self.level) def handle_starttag(self, tag, attrs): class_ = '' for a, v in attrs: if a == 'class': class_ = v s = '' if tag == 'div' and class_ == 'section': self.show = True # start of main content elif tag == 'div' and class_ == 'sphinxsidebar': self.show = False # end of main content elif tag == 'p' and class_ != 'first': s = '\n\n' elif tag == 'span' and class_ == 'pre': self.tags = 'pre' elif tag == 'span' and class_ == 'versionmodified': self.tags = 'em' elif tag == 'em': self.tags = 'em' elif tag in ['ul', 'ol']: self.indent() elif tag == 'dl': if self.level > 0: self.nested_dl = True elif tag == 'li': s = '\n\n* ' elif tag == 'dt': s = '\n\n' if not self.nested_dl else '\n' # avoid extra line self.nested_dl = False elif tag == 'dd': self.indent() s = '\n' elif tag == 'pre': self.pre = True self.tags = 'pre' s = '\n\n' elif tag == 'a' and class_ == 'headerlink': self.hdrlink = True elif tag in ['h1', 'h2', 'h3']: if self.show: self.t.insert('end', '\n\n\n') self.tags = tag if self.show: self.t.insert('end', s, self.tags) def handle_endtag(self, tag): if tag in ['h1', 'h2', 'h3', 'span', 'em']: self.indent(0) # clear tag, reset indent elif tag == 'a': self.hdrlink = False elif tag == 'pre': self.pre = False self.tags = '' elif tag in ['ul', 'dd', 'ol']: self.indent(amt=-1) def handle_data(self, data): if self.show and not self.hdrlink: d = data if self.pre else data.replace('\n', ' ') if self.tags == 'h1': self.hprefix = d[0:d.index(' ')] if self.tags in ['h1', 'h2', 'h3'] and self.hprefix != '': if d[0:len(self.hprefix)] == self.hprefix: d = d[len(self.hprefix):].strip() self.t.insert('end', d, self.tags) def __init__(self, parent, filename): Text.__init__(self, parent, wrap='word', font=('helvetica', 12), highlightthickness=0, padx=10, spacing1='2px', spacing2='2px') p = self.MyHTMLParser(self) with open(filename, encoding='utf-8') as f: contents = f.read() p.feed(contents) self.tag_configure('em', font=('helvetica', 12, 'italic')) self.tag_configure('h1', font=('helvetica', 24, 'bold')) self.tag_configure('h2', font=('helvetica', 20, 'bold')) self.tag_configure('h3', font=('helvetica', 15, 'bold')) self.tag_configure('pre', font=('courier', 12), lmargin1=25) self.tag_configure('l1', lmargin1=25, lmargin2=25) self.tag_configure('l2', lmargin1=50, lmargin2=50) self.tag_configure('l3', lmargin1=75, lmargin2=75) self.tag_configure('l4', lmargin1=100, lmargin2=100) self['state'] = 'disabled' class SphinxHTMLViewerWindow(Toplevel): def __init__(self, parent, filename, title): Toplevel.__init__(self, parent) self.wm_title(title) self.protocol("WM_DELETE_WINDOW", self.destroy) v = SphinxHTMLViewer(self, filename) s = Scrollbar(self, command=v.yview) v['yscrollcommand'] = s.set v.grid(column=0, row=0, sticky='nsew') s.grid(column=1, row=0, sticky='ns') self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) if __name__ == '__main__': root = Tk() w = SphinxHTMLViewerWindow(root, os.path.join(os.path.abspath(os.path.dirname(__file__)), 'idle.html'), 'IDLE Help') root.mainloop()