summaryrefslogtreecommitdiffstats
path: root/Lib/idlelib/help.py
diff options
context:
space:
mode:
authorTerry Jan Reedy <tjreedy@udel.edu>2015-09-20 23:57:13 (GMT)
committerTerry Jan Reedy <tjreedy@udel.edu>2015-09-20 23:57:13 (GMT)
commit5d46ab1274232d51060ba2e45e6c345ef4e5b970 (patch)
tree7398216e5fd4a31147d682c84b2b052f39b831d2 /Lib/idlelib/help.py
parent16ad5cfad391a8e7c124ccaa984a56ad123d0ab2 (diff)
downloadcpython-5d46ab1274232d51060ba2e45e6c345ef4e5b970.zip
cpython-5d46ab1274232d51060ba2e45e6c345ef4e5b970.tar.gz
cpython-5d46ab1274232d51060ba2e45e6c345ef4e5b970.tar.bz2
Issue #16893: Replace help.txt with idle.html for Idle doc display.
The new idlelib/idle.html is copied from Doc/build/html/idle.html. It looks better than help.txt and will better document Idle as released. The tkinter html viewer that works for this file was written by Rose Roseman. The new code is in idlelib/help.py, a new file for help menu classes. The now unused EditorWindow.HelpDialog class and helt.txt file are deprecated.
Diffstat (limited to 'Lib/idlelib/help.py')
-rw-r--r--Lib/idlelib/help.py233
1 files changed, 233 insertions, 0 deletions
diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py
new file mode 100644
index 0000000..b1047c8
--- /dev/null
+++ b/Lib/idlelib/help.py
@@ -0,0 +1,233 @@
+"""
+help.py implements the Idle help menu and is subject to change.
+
+The contents are subject to revision at any time, without notice.
+
+Help => About IDLE: diplay About Idle dialog
+
+<to be moved here from aboutDialog.py>
+
+Help => IDLE Help: display idle.html with proper formatting
+
+HelpParser - Parses idle.html generated from idle.rst by Sphinx
+and renders to tk Text.
+
+HelpText - Displays formatted idle.html.
+
+HelpFrame - Contains text, scrollbar, and table-of-contents.
+(This will be needed for display in a future tabbed window.)
+
+HelpWindow - Display idleframe in a standalone window.
+
+show_idlehelp - Create HelpWindow. Called in EditorWindow.help_dialog.
+"""
+from html.parser import HTMLParser
+from os.path import abspath, dirname, isdir, isfile, join
+from tkinter import Tk, Toplevel, Frame, Text, Scrollbar, Menu, Menubutton
+from tkinter import font as tkfont
+
+use_ttk = False # until available to import
+if use_ttk:
+ from tkinter.ttk import Menubutton
+
+## About IDLE ##
+
+
+## IDLE Help ##
+
+class HelpParser(HTMLParser):
+ """Render idle.html generated by Sphinx from idle.rst.
+
+ The overridden handle_xyz methods handle a subset of html tags.
+ The supplied text should have the needed tag configurations.
+ The behavior for unsupported tags, such as table, is undefined.
+ """
+ def __init__(self, text):
+ HTMLParser.__init__(self, convert_charrefs=True)
+ self.text = text # 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 <dl>
+ self.simplelist = False # simple list (no double spacing)
+ self.tocid = 1 # id for table of contents entries
+ self.contents = [] # map toc ids to section titles
+ self.data = '' # to record data within header tags for toc
+
+ 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):
+ "Handle starttags in idle.html."
+ 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']:
+ if class_.find('simple') != -1:
+ s = '\n'
+ self.simplelist = True
+ else:
+ self.simplelist = False
+ self.indent()
+ elif tag == 'dl':
+ if self.level > 0:
+ self.nested_dl = True
+ elif tag == 'li':
+ s = '\n* ' if self.simplelist else '\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
+ if self.show:
+ self.text.insert('end', '\n\n')
+ self.tags = 'preblock'
+ elif tag == 'a' and class_ == 'headerlink':
+ self.hdrlink = True
+ elif tag == 'h1':
+ self.text.mark_set('toc'+str(self.tocid),
+ self.text.index('end-1line'))
+ self.tags = tag
+ elif tag in ['h2', 'h3']:
+ if self.show:
+ self.data = ''
+ self.text.mark_set('toc'+str(self.tocid),
+ self.text.index('end-1line'))
+ self.text.insert('end', '\n\n')
+ self.tags = tag
+ if self.show:
+ self.text.insert('end', s, self.tags)
+
+ def handle_endtag(self, tag):
+ "Handle endtags in idle.html."
+ if tag in ['h1', 'h2', 'h3', 'span', 'em']:
+ self.indent(0) # clear tag, reset indent
+ if self.show and tag in ['h1', 'h2', 'h3']:
+ title = self.data
+ self.contents.append(('toc'+str(self.tocid), title))
+ self.tocid += 1
+ 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):
+ "Handle date segments in idle.html."
+ 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.data += d
+ self.text.insert('end', d, self.tags)
+
+
+class HelpText(Text):
+ "Display idle.html."
+ def __init__(self, parent, filename):
+ "Configure tags and feed file to parser."
+ Text.__init__(self, parent, wrap='word', highlightthickness=0,
+ padx=5, borderwidth=0)
+
+ normalfont = self.findfont(['TkDefaultFont', 'arial', 'helvetica'])
+ fixedfont = self.findfont(['TkFixedFont', 'monaco', 'courier'])
+ self['font'] = (normalfont, 12)
+ self.tag_configure('em', font=(normalfont, 12, 'italic'))
+ self.tag_configure('h1', font=(normalfont, 20, 'bold'))
+ self.tag_configure('h2', font=(normalfont, 18, 'bold'))
+ self.tag_configure('h3', font=(normalfont, 15, 'bold'))
+ self.tag_configure('pre', font=(fixedfont, 12))
+ self.tag_configure('preblock', font=(fixedfont, 10), lmargin1=25,
+ borderwidth=1, relief='solid', background='#eeffcc')
+ 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.parser = HelpParser(self)
+ with open(filename, encoding='utf-8') as f:
+ contents = f.read()
+ self.parser.feed(contents)
+ self['state'] = 'disabled'
+
+ def findfont(self, names):
+ "Return name of first font family derived from names."
+ for name in names:
+ if name.lower() in (x.lower() for x in tkfont.names(root=self)):
+ font = tkfont.Font(name=name, exists=True, root=self)
+ return font.actual()['family']
+ elif name.lower() in (x.lower()
+ for x in tkfont.families(root=self)):
+ return name
+
+
+class HelpFrame(Frame):
+ def __init__(self, parent, filename):
+ Frame.__init__(self, parent)
+ text = HelpText(self, filename)
+ self['background'] = text['background']
+ scroll = Scrollbar(self, command=text.yview)
+ text['yscrollcommand'] = scroll.set
+ text.grid(column=1, row=0, sticky='nsew')
+ scroll.grid(column=2, row=0, sticky='ns')
+ self.grid_columnconfigure(1, weight=1)
+ self.grid_rowconfigure(0, weight=1)
+ toc = self.contents_widget(text)
+ toc.grid(column=0, row=0, sticky='nw')
+
+ def contents_widget(self, text):
+ toc = Menubutton(self, text='TOC')
+ drop = Menu(toc, tearoff=False)
+ for tag, lbl in text.parser.contents:
+ drop.add_command(label=lbl, command=lambda mark=tag:text.see(mark))
+ toc['menu'] = drop
+ return toc
+
+
+class HelpWindow(Toplevel):
+
+ def __init__(self, parent, filename, title):
+ Toplevel.__init__(self, parent)
+ self.wm_title(title)
+ self.protocol("WM_DELETE_WINDOW", self.destroy)
+ HelpFrame(self, filename).grid(column=0, row=0, sticky='nsew')
+ self.grid_columnconfigure(0, weight=1)
+ self.grid_rowconfigure(0, weight=1)
+
+
+def show_idlehelp(parent):
+ filename = join(abspath(dirname(__file__)), 'idle.html')
+ if not isfile(filename):
+ dirpath = join(abspath(dirname(dirname(dirname(__file__)))),
+ 'Doc', 'build', 'html', 'library')
+ HelpWindow(parent, filename, 'IDLE Help')
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(show_idlehelp)