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