1'''Define SearchDialogBase used by Search, Replace, and Grep dialogs.''' 2 3from tkinter import Toplevel 4from tkinter.ttk import Frame, Entry, Label, Button, Checkbutton, Radiobutton 5 6 7class SearchDialogBase: 8 '''Create most of a 3 or 4 row, 3 column search dialog. 9 10 The left and wide middle column contain: 11 1 or 2 labeled text entry lines (make_entry, create_entries); 12 a row of standard Checkbuttons (make_frame, create_option_buttons), 13 each of which corresponds to a search engine Variable; 14 a row of dialog-specific Check/Radiobuttons (create_other_buttons). 15 16 The narrow right column contains command buttons 17 (make_button, create_command_buttons). 18 These are bound to functions that execute the command. 19 20 Except for command buttons, this base class is not limited to items 21 common to all three subclasses. Rather, it is the Find dialog minus 22 the "Find Next" command, its execution function, and the 23 default_command attribute needed in create_widgets. The other 24 dialogs override attributes and methods, the latter to replace and 25 add widgets. 26 ''' 27 28 title = "Search Dialog" # replace in subclasses 29 icon = "Search" 30 needwrapbutton = 1 # not in Find in Files 31 32 def __init__(self, root, engine): 33 '''Initialize root, engine, and top attributes. 34 35 top (level widget): set in create_widgets() called from open(). 36 frame: container for all widgets in dialog. 37 text (Text searched): set in open(), only used in subclasses(). 38 ent (ry): created in make_entry() called from create_entry(). 39 row (of grid): 0 in create_widgets(), +1 in make_entry/frame(). 40 default_command: set in subclasses, used in create_widgets(). 41 42 title (of dialog): class attribute, override in subclasses. 43 icon (of dialog): ditto, use unclear if cannot minimize dialog. 44 ''' 45 self.root = root 46 self.bell = root.bell 47 self.engine = engine 48 self.top = None 49 50 def open(self, text, searchphrase=None): 51 "Make dialog visible on top of others and ready to use." 52 self.text = text 53 if not self.top: 54 self.create_widgets() 55 else: 56 self.top.deiconify() 57 self.top.tkraise() 58 self.top.transient(text.winfo_toplevel()) 59 if searchphrase: 60 self.ent.delete(0,"end") 61 self.ent.insert("end",searchphrase) 62 self.ent.focus_set() 63 self.ent.selection_range(0, "end") 64 self.ent.icursor(0) 65 self.top.grab_set() 66 67 def close(self, event=None): 68 "Put dialog away for later use." 69 if self.top: 70 self.top.grab_release() 71 self.top.transient('') 72 self.top.withdraw() 73 74 def create_widgets(self): 75 '''Create basic 3 row x 3 col search (find) dialog. 76 77 Other dialogs override subsidiary create_x methods as needed. 78 Replace and Find-in-Files add another entry row. 79 ''' 80 top = Toplevel(self.root) 81 top.bind("<Return>", self.default_command) 82 top.bind("<Escape>", self.close) 83 top.protocol("WM_DELETE_WINDOW", self.close) 84 top.wm_title(self.title) 85 top.wm_iconname(self.icon) 86 self.top = top 87 self.frame = Frame(top, padding="5px") 88 self.frame.grid(sticky="nwes") 89 top.grid_columnconfigure(0, weight=100) 90 top.grid_rowconfigure(0, weight=100) 91 92 self.row = 0 93 self.frame.grid_columnconfigure(0, pad=2, weight=0) 94 self.frame.grid_columnconfigure(1, pad=2, minsize=100, weight=100) 95 96 self.create_entries() # row 0 (and maybe 1), cols 0, 1 97 self.create_option_buttons() # next row, cols 0, 1 98 self.create_other_buttons() # next row, cols 0, 1 99 self.create_command_buttons() # col 2, all rows 100 101 def make_entry(self, label_text, var): 102 '''Return (entry, label), . 103 104 entry - gridded labeled Entry for text entry. 105 label - Label widget, returned for testing. 106 ''' 107 label = Label(self.frame, text=label_text) 108 label.grid(row=self.row, column=0, sticky="nw") 109 entry = Entry(self.frame, textvariable=var, exportselection=0) 110 entry.grid(row=self.row, column=1, sticky="nwe") 111 self.row = self.row + 1 112 return entry, label 113 114 def create_entries(self): 115 "Create one or more entry lines with make_entry." 116 self.ent = self.make_entry("Find:", self.engine.patvar)[0] 117 118 def make_frame(self,labeltext=None): 119 '''Return (frame, label). 120 121 frame - gridded labeled Frame for option or other buttons. 122 label - Label widget, returned for testing. 123 ''' 124 if labeltext: 125 label = Label(self.frame, text=labeltext) 126 label.grid(row=self.row, column=0, sticky="nw") 127 else: 128 label = '' 129 frame = Frame(self.frame) 130 frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe") 131 self.row = self.row + 1 132 return frame, label 133 134 def create_option_buttons(self): 135 '''Return (filled frame, options) for testing. 136 137 Options is a list of searchengine booleanvar, label pairs. 138 A gridded frame from make_frame is filled with a Checkbutton 139 for each pair, bound to the var, with the corresponding label. 140 ''' 141 frame = self.make_frame("Options")[0] 142 engine = self.engine 143 options = [(engine.revar, "Regular expression"), 144 (engine.casevar, "Match case"), 145 (engine.wordvar, "Whole word")] 146 if self.needwrapbutton: 147 options.append((engine.wrapvar, "Wrap around")) 148 for var, label in options: 149 btn = Checkbutton(frame, variable=var, text=label) 150 btn.pack(side="left", fill="both") 151 return frame, options 152 153 def create_other_buttons(self): 154 '''Return (frame, others) for testing. 155 156 Others is a list of value, label pairs. 157 A gridded frame from make_frame is filled with radio buttons. 158 ''' 159 frame = self.make_frame("Direction")[0] 160 var = self.engine.backvar 161 others = [(1, 'Up'), (0, 'Down')] 162 for val, label in others: 163 btn = Radiobutton(frame, variable=var, value=val, text=label) 164 btn.pack(side="left", fill="both") 165 return frame, others 166 167 def make_button(self, label, command, isdef=0): 168 "Return command button gridded in command frame." 169 b = Button(self.buttonframe, 170 text=label, command=command, 171 default=isdef and "active" or "normal") 172 cols,rows=self.buttonframe.grid_size() 173 b.grid(pady=1,row=rows,column=0,sticky="ew") 174 self.buttonframe.grid(rowspan=rows+1) 175 return b 176 177 def create_command_buttons(self): 178 "Place buttons in vertical command frame gridded on right." 179 f = self.buttonframe = Frame(self.frame) 180 f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2) 181 182 b = self.make_button("Close", self.close) 183 b.lower() 184 185 186class _searchbase(SearchDialogBase): # htest # 187 "Create auto-opening dialog with no text connection." 188 189 def __init__(self, parent): 190 import re 191 from idlelib import searchengine 192 193 self.root = parent 194 self.engine = searchengine.get(parent) 195 self.create_widgets() 196 print(parent.geometry()) 197 width,height, x,y = list(map(int, re.split('[x+]', parent.geometry()))) 198 self.top.geometry("+%d+%d" % (x + 40, y + 175)) 199 200 def default_command(self, dummy): pass 201 202 203if __name__ == '__main__': 204 from unittest import main 205 main('idlelib.idle_test.test_searchbase', verbosity=2, exit=False) 206 207 from idlelib.idle_test.htest import run 208 run(_searchbase) 209