tags after a closed tag. # Avoid extra lines, e.g. after
tags. lastline = self.text.get('end-1c linestart', 'end-1c') s = '\n\n' if lastline and not lastline.isspace() else '\n' elif tag == 'span' and class_ == 'pre': self.chartags = 'pre' elif tag == 'span' and class_ == 'versionmodified': self.chartags = 'em' elif tag == 'em': self.chartags = '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.tags = tag elif tag in ['h2', 'h3']: if self.show: self.header = '' self.text.insert('end', '\n\n') self.tags = tag if self.show: self.text.insert('end', s, (self.tags, self.chartags)) self.prevtag = (True, tag) def handle_endtag(self, tag): "Handle endtags in help.html." if tag in ['h1', 'h2', 'h3']: assert self.level == 0 if self.show: indent = (' ' if tag == 'h3' else ' ' if tag == 'h2' else '') self.toc.append((indent+self.header, self.text.index('insert'))) self.tags = '' elif tag in ['span', 'em']: self.chartags = '' elif tag == 'a': self.hdrlink = False elif tag == 'pre': self.pre = False self.tags = '' elif tag in ['ul', 'dd', 'ol']: self.indent(-1) self.prevtag = (False, tag) def handle_data(self, data): "Handle date segments in help.html." if self.show and not self.hdrlink: d = data if self.pre else data.replace('\n', ' ') if self.tags == 'h1': try: self.hprefix = d[0:d.index(' ')] except ValueError: self.hprefix = '' if self.tags in ['h1', 'h2', 'h3']: if (self.hprefix != '' and d[0:len(self.hprefix)] == self.hprefix): d = d[len(self.hprefix):] self.header += d.strip() self.text.insert('end', d, (self.tags, self.chartags)) class HelpText(Text): "Display help.html." def __init__(self, parent, filename): "Configure tags and feed file to parser." uwide = idleConf.GetOption('main', 'EditorWindow', 'width', type='int') uhigh = idleConf.GetOption('main', 'EditorWindow', 'height', type='int') uhigh = 3 * uhigh // 4 # Lines average 4/3 of editor line height. Text.__init__(self, parent, wrap='word', highlightthickness=0, padx=5, borderwidth=0, width=uwide, height=uhigh) 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), background='#f6f6ff') 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): "Display html text, scrollbar, and toc." def __init__(self, parent, filename): Frame.__init__(self, parent) self.text = text = HelpText(self, filename) self['background'] = text['background'] self.toc = toc = self.toc_menu(text) self.scroll = scroll = Scrollbar(self, command=text.yview) text['yscrollcommand'] = scroll.set self.rowconfigure(0, weight=1) self.columnconfigure(1, weight=1) # Only expand the text widget. toc.grid(row=0, column=0, sticky='nw') text.grid(row=0, column=1, sticky='nsew') scroll.grid(row=0, column=2, sticky='ns') def toc_menu(self, text): "Create table of contents as drop-down menu." toc = Menubutton(self, text='TOC') drop = Menu(toc, tearoff=False) for lbl, dex in text.parser.toc: drop.add_command(label=lbl, command=lambda dex=dex:text.yview(dex)) toc['menu'] = drop return toc class HelpWindow(Toplevel): "Display frame with rendered html." 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 copy_strip(): """Copy idle.html to idlelib/help.html, stripping trailing whitespace. Files with trailing whitespace cannot be pushed to the git cpython repository. For 3.x (on Windows), help.html is generated, after editing idle.rst on the master branch, with sphinx-build -bhtml . build/html python_d.exe -c "from idlelib.help import copy_strip; copy_strip()" Check build/html/library/idle.html, the help.html diff, and the text displayed by Help => IDLE Help. Add a blurb and create a PR. It can be worthwhile to occasionally generate help.html without touching idle.rst. Changes to the master version and to the doc build system may result in changes that should not changed the displayed text, but might break HelpParser. As long as master and maintenance versions of idle.rst remain the same, help.html can be backported. The internal Python version number is not displayed. If maintenance idle.rst diverges from the master version, then instead of backporting help.html from master, repeat the procedure above to generate a maintenance version. """ src = join(abspath(dirname(dirname(dirname(__file__)))), 'Doc', 'build', 'html', 'library', 'idle.html') dst = join(abspath(dirname(__file__)), 'help.html') with open(src, 'rb') as inn,\ open(dst, 'wb') as out: for line in inn: out.write(line.rstrip() + b'\n') print(f'{src} copied to {dst}') def show_idlehelp(parent): "Create HelpWindow; called from Idle Help event handler." filename = join(abspath(dirname(__file__)), 'help.html') if not isfile(filename): # Try copy_strip, present message. return HelpWindow(parent, filename, 'IDLE Help (%s)' % python_version()) if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_help', verbosity=2, exit=False) from idlelib.idle_test.htest import run run(show_idlehelp)