"""
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()