1#!/usr/local/bin/python3.8 2# 3#-------------------------------------------------------- 4# Help Window for the Qflow project manager 5# 6#-------------------------------------------------------- 7# Written by Tim Edwards 8# efabless, inc. 9# September 12, 2016 10# Version 0.1 11#-------------------------------------------------------- 12 13import re 14import tkinter 15from tkinter import ttk 16 17class HelpWindow(tkinter.Toplevel): 18 """Qflow help window.""" 19 20 def __init__(self, parent=None, fontsize = 11, *args, **kwargs): 21 '''See the __init__ for Tkinter.Toplevel.''' 22 tkinter.Toplevel.__init__(self, parent, *args, **kwargs) 23 24 s = ttk.Style() 25 s.configure('normal.TButton', font=('Helvetica', fontsize), border = 3, relief = 'raised') 26 self.protocol("WM_DELETE_WINDOW", self.close) 27 28 self.withdraw() 29 self.title('Qflow Help') 30 31 self.helptitle = ttk.Label(self, style='title.TLabel', text = '(no text)') 32 self.helptitle.grid(column = 0, row = 0, sticky = "news") 33 self.helpbar = ttk.Separator(self, orient='horizontal') 34 self.helpbar.grid(column = 0, row = 1, sticky = "news") 35 36 self.hframe = tkinter.Frame(self) 37 self.hframe.grid(column = 0, row = 2, sticky = "news") 38 self.hframe.helpdisplay = ttk.Frame(self.hframe) 39 self.hframe.helpdisplay.pack(side = 'left', fill = 'both', expand = 'true') 40 41 self.hframe.helpdisplay.helptext = tkinter.Text(self.hframe.helpdisplay, wrap='word') 42 self.hframe.helpdisplay.helptext.pack(side = 'top', fill = 'both', expand = 'true') 43 # Add scrollbar to help window 44 self.hframe.scrollbar = ttk.Scrollbar(self.hframe) 45 self.hframe.scrollbar.pack(side='right', fill='y') 46 # attach help window to scrollbar 47 self.hframe.helpdisplay.helptext.config(yscrollcommand = self.hframe.scrollbar.set) 48 self.hframe.scrollbar.config(command = self.hframe.helpdisplay.helptext.yview) 49 50 self.hframe.toc = ttk.Treeview(self.hframe, selectmode='browse') 51 self.hframe.toc.bind('<<TreeviewSelect>>', self.toc_to_page) 52 self.hframe.toc.bind('<<TreeviewOpen>>', self.toc_toggle) 53 self.hframe.toc.bind('<<TreeviewClose>>', self.toc_toggle) 54 self.hframe.toc.tag_configure('title', font=('Helvetica', fontsize, 'bold italic'), 55 foreground = 'brown', anchor = 'center') 56 self.hframe.toc.heading('#0', text = "Table of Contents") 57 58 self.bbar = ttk.Frame(self) 59 self.bbar.grid(column = 0, row = 3, sticky = "news") 60 self.bbar.close_button = ttk.Button(self.bbar, text='Close', 61 command=self.close, style = 'normal.TButton') 62 self.bbar.close_button.grid(column=0, row=0, padx = 5) 63 64 self.bbar.prev_button = ttk.Button(self.bbar, text='Prev', 65 command=self.prevpage, style = 'normal.TButton') 66 self.bbar.prev_button.grid(column=1, row=0, padx = 5) 67 68 self.bbar.next_button = ttk.Button(self.bbar, text='Next', 69 command=self.nextpage, style = 'normal.TButton') 70 self.bbar.next_button.grid(column=2, row=0, padx = 5) 71 72 self.bbar.contents_button = ttk.Button(self.bbar, text='Table of Contents', 73 command=self.page_to_toc, style = 'normal.TButton') 74 self.bbar.contents_button.grid(column=3, row=0, padx = 5) 75 76 self.rowconfigure(0, weight=0) 77 self.rowconfigure(1, weight=0) 78 self.rowconfigure(2, weight=1) 79 self.rowconfigure(3, weight=0) 80 self.columnconfigure(0, weight=1) 81 82 # Help pages 83 self.pages = [] 84 self.pageno = -1 # No page 85 self.toggle = False 86 87 def grid_configure(self, padx, pady): 88 pass 89 90 def redisplay(self): 91 # remove contents 92 if self.pageno >= 0 and self.pageno < len(self.pages): 93 self.hframe.helpdisplay.helptext.delete('1.0', 'end') 94 self.hframe.helpdisplay.helptext.insert('end', self.pages[self.pageno]['text']) 95 self.helptitle.configure(text = self.pages[self.pageno]['title']) 96 97 def toc_toggle(self, event): 98 self.toggle = True 99 100 def toc_to_page(self, event): 101 treeview = event.widget 102 selection = treeview.item(treeview.selection()) 103 104 # Make sure any open/close callback is handled first! 105 self.update_idletasks() 106 if self.toggle: 107 # Item was opened or closed, so consider this a 'false select' and 108 # do not go to the page. 109 self.toggle = False 110 return 111 112 if 'values' in selection: 113 pagenum = selection['values'][0] 114 else: 115 print('Unknown page selected.') 116 pagenum = 0 117 118 # Display a page after displaying the table of contents 119 self.hframe.toc.pack_forget() 120 self.hframe.scrollbar.pack_forget() 121 self.hframe.helpdisplay.pack(side='left', fill='both', expand = 'true') 122 self.hframe.scrollbar.pack(side='right', fill='y') 123 self.hframe.scrollbar.config(command = self.hframe.helpdisplay.helptext.yview) 124 # Enable Prev and Next buttons 125 self.bbar.prev_button.configure(state='enabled') 126 self.bbar.next_button.configure(state='enabled') 127 # Redisplay 128 self.page(pagenum) 129 130 def page_to_toc(self): 131 # Display the table of contents after displaying a page 132 self.hframe.scrollbar.pack_forget() 133 self.hframe.helpdisplay.pack_forget() 134 self.hframe.toc.pack(side='left', fill='both', expand = 'true') 135 self.hframe.scrollbar.pack(side='right', fill='y') 136 self.hframe.scrollbar.config(command = self.hframe.toc.yview) 137 # Disable Prev and Next buttons 138 self.bbar.prev_button.configure(state='disabled') 139 self.bbar.next_button.configure(state='disabled') 140 141 # Simple add page with a single block of plain text 142 def add_page(self, toc_text, text_block): 143 newdict = {} 144 newdict['text'] = text_block 145 newdict['title'] = toc_text 146 self.pages.append(newdict) 147 newpageno = len(self.pages) 148 self.hframe.toc.insert('', 'end', text=str(newpageno) + '. ' + toc_text, 149 tag='title', value = newpageno - 1) 150 if self.pageno < 0: 151 self.pageno = 0 # First page 152 153 # Fill the help text from a file. The format of the file is: 154 # <page_num> 155 # <title> 156 # <text> 157 # '.' 158 # Text is multi-line and ends when '.' is encountered by itself 159 160 def add_pages_from_file(self, filename): 161 endpagerex = re.compile('^\.$') 162 newpagerex = re.compile('^[0-9\.]+$') 163 commentrex = re.compile('^[\-]+$') 164 hierarchy = '' 165 print('Loading help text from file ' + filename) 166 with open(filename, 'r') as f: 167 toc_text = [] 168 page_text = [] 169 for line in f: 170 if newpagerex.match(line) or endpagerex.match(line): 171 if toc_text and page_text: 172 newdict = {} 173 self.pages.append(newdict) 174 newpageno = len(self.pages) 175 if '.' in hierarchy: 176 pageinfo = hierarchy.rsplit('.', 1) 177 if pageinfo[1] == '': 178 parentid = '' 179 pageid = pageinfo[0] 180 else: 181 parentid = pageinfo[0] 182 pageid = pageinfo[1] 183 else: 184 parentid = '' 185 pageid = hierarchy 186 if parentid: 187 pageid = parentid + '.' + pageid 188 newdict['text'] = page_text 189 newdict['title'] = pageid + '. ' + toc_text 190 self.hframe.toc.insert(parentid, 'end', 191 text=newdict['title'], tag='title', 192 value = newpageno - 1, iid = pageid) 193 if newpagerex.match(line): 194 hierarchy = line.rstrip() 195 toc_text = [] 196 elif not toc_text: 197 toc_text = line.rstrip() 198 page_text = [] 199 elif not commentrex.match(line): 200 if not page_text: 201 page_text = line 202 else: 203 page_text += line 204 205 def nextpage(self): 206 # Go to next page 207 if self.pageno < len(self.pages) - 1: 208 self.pageno += 1 209 self.redisplay() 210 211 def prevpage(self): 212 # Go to previous page 213 if self.pageno > 0: 214 self.pageno -= 1 215 self.redisplay() 216 217 def page(self, pagenum): 218 # Go to indicated page 219 if pagenum >= 0 and pagenum < len(self.pages): 220 self.pageno = pagenum 221 self.redisplay() 222 223 def close(self): 224 # pop down help window 225 self.withdraw() 226 227 def open(self): 228 # pop up help window 229 self.deiconify() 230 self.lift() 231