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