emayecs | 5656b2b | 2021-08-04 12:44:13 -0400 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
emayecs | 5966a53 | 2021-07-29 10:07:02 -0400 | [diff] [blame] | 2 | # |
| 3 | #-------------------------------------------------------- |
emayecs | 1474831 | 2021-08-05 14:21:26 -0400 | [diff] [blame] | 4 | # Help Window for the Project manager |
emayecs | 5966a53 | 2021-07-29 10:07:02 -0400 | [diff] [blame] | 5 | # |
| 6 | #-------------------------------------------------------- |
| 7 | # Written by Tim Edwards |
| 8 | # efabless, inc. |
| 9 | # September 12, 2016 |
| 10 | # Version 0.1 |
| 11 | #-------------------------------------------------------- |
| 12 | |
| 13 | import re |
| 14 | import tkinter |
| 15 | from tkinter import ttk |
| 16 | |
| 17 | class HelpWindow(tkinter.Toplevel): |
emayecs | 1474831 | 2021-08-05 14:21:26 -0400 | [diff] [blame] | 18 | """help window""" |
emayecs | 5966a53 | 2021-07-29 10:07:02 -0400 | [diff] [blame] | 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() |
emayecs | 1474831 | 2021-08-05 14:21:26 -0400 | [diff] [blame] | 29 | self.title('Help') |
emayecs | 5966a53 | 2021-07-29 10:07:02 -0400 | [diff] [blame] | 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() |