17db96d56Sopenharmony_ci'''Define SearchDialogBase used by Search, Replace, and Grep dialogs.''' 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_cifrom tkinter import Toplevel 47db96d56Sopenharmony_cifrom tkinter.ttk import Frame, Entry, Label, Button, Checkbutton, Radiobutton 57db96d56Sopenharmony_cifrom tkinter.simpledialog import _setup_dialog 67db96d56Sopenharmony_ci 77db96d56Sopenharmony_ci 87db96d56Sopenharmony_ciclass SearchDialogBase: 97db96d56Sopenharmony_ci '''Create most of a 3 or 4 row, 3 column search dialog. 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_ci The left and wide middle column contain: 127db96d56Sopenharmony_ci 1 or 2 labeled text entry lines (make_entry, create_entries); 137db96d56Sopenharmony_ci a row of standard Checkbuttons (make_frame, create_option_buttons), 147db96d56Sopenharmony_ci each of which corresponds to a search engine Variable; 157db96d56Sopenharmony_ci a row of dialog-specific Check/Radiobuttons (create_other_buttons). 167db96d56Sopenharmony_ci 177db96d56Sopenharmony_ci The narrow right column contains command buttons 187db96d56Sopenharmony_ci (make_button, create_command_buttons). 197db96d56Sopenharmony_ci These are bound to functions that execute the command. 207db96d56Sopenharmony_ci 217db96d56Sopenharmony_ci Except for command buttons, this base class is not limited to items 227db96d56Sopenharmony_ci common to all three subclasses. Rather, it is the Find dialog minus 237db96d56Sopenharmony_ci the "Find Next" command, its execution function, and the 247db96d56Sopenharmony_ci default_command attribute needed in create_widgets. The other 257db96d56Sopenharmony_ci dialogs override attributes and methods, the latter to replace and 267db96d56Sopenharmony_ci add widgets. 277db96d56Sopenharmony_ci ''' 287db96d56Sopenharmony_ci 297db96d56Sopenharmony_ci title = "Search Dialog" # replace in subclasses 307db96d56Sopenharmony_ci icon = "Search" 317db96d56Sopenharmony_ci needwrapbutton = 1 # not in Find in Files 327db96d56Sopenharmony_ci 337db96d56Sopenharmony_ci def __init__(self, root, engine): 347db96d56Sopenharmony_ci '''Initialize root, engine, and top attributes. 357db96d56Sopenharmony_ci 367db96d56Sopenharmony_ci top (level widget): set in create_widgets() called from open(). 377db96d56Sopenharmony_ci frame: container for all widgets in dialog. 387db96d56Sopenharmony_ci text (Text searched): set in open(), only used in subclasses(). 397db96d56Sopenharmony_ci ent (ry): created in make_entry() called from create_entry(). 407db96d56Sopenharmony_ci row (of grid): 0 in create_widgets(), +1 in make_entry/frame(). 417db96d56Sopenharmony_ci default_command: set in subclasses, used in create_widgets(). 427db96d56Sopenharmony_ci 437db96d56Sopenharmony_ci title (of dialog): class attribute, override in subclasses. 447db96d56Sopenharmony_ci icon (of dialog): ditto, use unclear if cannot minimize dialog. 457db96d56Sopenharmony_ci ''' 467db96d56Sopenharmony_ci self.root = root 477db96d56Sopenharmony_ci self.bell = root.bell 487db96d56Sopenharmony_ci self.engine = engine 497db96d56Sopenharmony_ci self.top = None 507db96d56Sopenharmony_ci 517db96d56Sopenharmony_ci def open(self, text, searchphrase=None): 527db96d56Sopenharmony_ci "Make dialog visible on top of others and ready to use." 537db96d56Sopenharmony_ci self.text = text 547db96d56Sopenharmony_ci if not self.top: 557db96d56Sopenharmony_ci self.create_widgets() 567db96d56Sopenharmony_ci else: 577db96d56Sopenharmony_ci self.top.deiconify() 587db96d56Sopenharmony_ci self.top.tkraise() 597db96d56Sopenharmony_ci self.top.transient(text.winfo_toplevel()) 607db96d56Sopenharmony_ci if searchphrase: 617db96d56Sopenharmony_ci self.ent.delete(0,"end") 627db96d56Sopenharmony_ci self.ent.insert("end",searchphrase) 637db96d56Sopenharmony_ci self.ent.focus_set() 647db96d56Sopenharmony_ci self.ent.selection_range(0, "end") 657db96d56Sopenharmony_ci self.ent.icursor(0) 667db96d56Sopenharmony_ci self.top.grab_set() 677db96d56Sopenharmony_ci 687db96d56Sopenharmony_ci def close(self, event=None): 697db96d56Sopenharmony_ci "Put dialog away for later use." 707db96d56Sopenharmony_ci if self.top: 717db96d56Sopenharmony_ci self.top.grab_release() 727db96d56Sopenharmony_ci self.top.transient('') 737db96d56Sopenharmony_ci self.top.withdraw() 747db96d56Sopenharmony_ci 757db96d56Sopenharmony_ci def create_widgets(self): 767db96d56Sopenharmony_ci '''Create basic 3 row x 3 col search (find) dialog. 777db96d56Sopenharmony_ci 787db96d56Sopenharmony_ci Other dialogs override subsidiary create_x methods as needed. 797db96d56Sopenharmony_ci Replace and Find-in-Files add another entry row. 807db96d56Sopenharmony_ci ''' 817db96d56Sopenharmony_ci top = Toplevel(self.root) 827db96d56Sopenharmony_ci top.bind("<Return>", self.default_command) 837db96d56Sopenharmony_ci top.bind("<Escape>", self.close) 847db96d56Sopenharmony_ci top.protocol("WM_DELETE_WINDOW", self.close) 857db96d56Sopenharmony_ci top.wm_title(self.title) 867db96d56Sopenharmony_ci top.wm_iconname(self.icon) 877db96d56Sopenharmony_ci _setup_dialog(top) 887db96d56Sopenharmony_ci self.top = top 897db96d56Sopenharmony_ci self.frame = Frame(top, padding="5px") 907db96d56Sopenharmony_ci self.frame.grid(sticky="nwes") 917db96d56Sopenharmony_ci top.grid_columnconfigure(0, weight=100) 927db96d56Sopenharmony_ci top.grid_rowconfigure(0, weight=100) 937db96d56Sopenharmony_ci 947db96d56Sopenharmony_ci self.row = 0 957db96d56Sopenharmony_ci self.frame.grid_columnconfigure(0, pad=2, weight=0) 967db96d56Sopenharmony_ci self.frame.grid_columnconfigure(1, pad=2, minsize=100, weight=100) 977db96d56Sopenharmony_ci 987db96d56Sopenharmony_ci self.create_entries() # row 0 (and maybe 1), cols 0, 1 997db96d56Sopenharmony_ci self.create_option_buttons() # next row, cols 0, 1 1007db96d56Sopenharmony_ci self.create_other_buttons() # next row, cols 0, 1 1017db96d56Sopenharmony_ci self.create_command_buttons() # col 2, all rows 1027db96d56Sopenharmony_ci 1037db96d56Sopenharmony_ci def make_entry(self, label_text, var): 1047db96d56Sopenharmony_ci '''Return (entry, label), . 1057db96d56Sopenharmony_ci 1067db96d56Sopenharmony_ci entry - gridded labeled Entry for text entry. 1077db96d56Sopenharmony_ci label - Label widget, returned for testing. 1087db96d56Sopenharmony_ci ''' 1097db96d56Sopenharmony_ci label = Label(self.frame, text=label_text) 1107db96d56Sopenharmony_ci label.grid(row=self.row, column=0, sticky="nw") 1117db96d56Sopenharmony_ci entry = Entry(self.frame, textvariable=var, exportselection=0) 1127db96d56Sopenharmony_ci entry.grid(row=self.row, column=1, sticky="nwe") 1137db96d56Sopenharmony_ci self.row = self.row + 1 1147db96d56Sopenharmony_ci return entry, label 1157db96d56Sopenharmony_ci 1167db96d56Sopenharmony_ci def create_entries(self): 1177db96d56Sopenharmony_ci "Create one or more entry lines with make_entry." 1187db96d56Sopenharmony_ci self.ent = self.make_entry("Find:", self.engine.patvar)[0] 1197db96d56Sopenharmony_ci 1207db96d56Sopenharmony_ci def make_frame(self,labeltext=None): 1217db96d56Sopenharmony_ci '''Return (frame, label). 1227db96d56Sopenharmony_ci 1237db96d56Sopenharmony_ci frame - gridded labeled Frame for option or other buttons. 1247db96d56Sopenharmony_ci label - Label widget, returned for testing. 1257db96d56Sopenharmony_ci ''' 1267db96d56Sopenharmony_ci if labeltext: 1277db96d56Sopenharmony_ci label = Label(self.frame, text=labeltext) 1287db96d56Sopenharmony_ci label.grid(row=self.row, column=0, sticky="nw") 1297db96d56Sopenharmony_ci else: 1307db96d56Sopenharmony_ci label = '' 1317db96d56Sopenharmony_ci frame = Frame(self.frame) 1327db96d56Sopenharmony_ci frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe") 1337db96d56Sopenharmony_ci self.row = self.row + 1 1347db96d56Sopenharmony_ci return frame, label 1357db96d56Sopenharmony_ci 1367db96d56Sopenharmony_ci def create_option_buttons(self): 1377db96d56Sopenharmony_ci '''Return (filled frame, options) for testing. 1387db96d56Sopenharmony_ci 1397db96d56Sopenharmony_ci Options is a list of searchengine booleanvar, label pairs. 1407db96d56Sopenharmony_ci A gridded frame from make_frame is filled with a Checkbutton 1417db96d56Sopenharmony_ci for each pair, bound to the var, with the corresponding label. 1427db96d56Sopenharmony_ci ''' 1437db96d56Sopenharmony_ci frame = self.make_frame("Options")[0] 1447db96d56Sopenharmony_ci engine = self.engine 1457db96d56Sopenharmony_ci options = [(engine.revar, "Regular expression"), 1467db96d56Sopenharmony_ci (engine.casevar, "Match case"), 1477db96d56Sopenharmony_ci (engine.wordvar, "Whole word")] 1487db96d56Sopenharmony_ci if self.needwrapbutton: 1497db96d56Sopenharmony_ci options.append((engine.wrapvar, "Wrap around")) 1507db96d56Sopenharmony_ci for var, label in options: 1517db96d56Sopenharmony_ci btn = Checkbutton(frame, variable=var, text=label) 1527db96d56Sopenharmony_ci btn.pack(side="left", fill="both") 1537db96d56Sopenharmony_ci return frame, options 1547db96d56Sopenharmony_ci 1557db96d56Sopenharmony_ci def create_other_buttons(self): 1567db96d56Sopenharmony_ci '''Return (frame, others) for testing. 1577db96d56Sopenharmony_ci 1587db96d56Sopenharmony_ci Others is a list of value, label pairs. 1597db96d56Sopenharmony_ci A gridded frame from make_frame is filled with radio buttons. 1607db96d56Sopenharmony_ci ''' 1617db96d56Sopenharmony_ci frame = self.make_frame("Direction")[0] 1627db96d56Sopenharmony_ci var = self.engine.backvar 1637db96d56Sopenharmony_ci others = [(1, 'Up'), (0, 'Down')] 1647db96d56Sopenharmony_ci for val, label in others: 1657db96d56Sopenharmony_ci btn = Radiobutton(frame, variable=var, value=val, text=label) 1667db96d56Sopenharmony_ci btn.pack(side="left", fill="both") 1677db96d56Sopenharmony_ci return frame, others 1687db96d56Sopenharmony_ci 1697db96d56Sopenharmony_ci def make_button(self, label, command, isdef=0): 1707db96d56Sopenharmony_ci "Return command button gridded in command frame." 1717db96d56Sopenharmony_ci b = Button(self.buttonframe, 1727db96d56Sopenharmony_ci text=label, command=command, 1737db96d56Sopenharmony_ci default=isdef and "active" or "normal") 1747db96d56Sopenharmony_ci cols,rows=self.buttonframe.grid_size() 1757db96d56Sopenharmony_ci b.grid(pady=1,row=rows,column=0,sticky="ew") 1767db96d56Sopenharmony_ci self.buttonframe.grid(rowspan=rows+1) 1777db96d56Sopenharmony_ci return b 1787db96d56Sopenharmony_ci 1797db96d56Sopenharmony_ci def create_command_buttons(self): 1807db96d56Sopenharmony_ci "Place buttons in vertical command frame gridded on right." 1817db96d56Sopenharmony_ci f = self.buttonframe = Frame(self.frame) 1827db96d56Sopenharmony_ci f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2) 1837db96d56Sopenharmony_ci 1847db96d56Sopenharmony_ci b = self.make_button("Close", self.close) 1857db96d56Sopenharmony_ci b.lower() 1867db96d56Sopenharmony_ci 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_ciclass _searchbase(SearchDialogBase): # htest # 1897db96d56Sopenharmony_ci "Create auto-opening dialog with no text connection." 1907db96d56Sopenharmony_ci 1917db96d56Sopenharmony_ci def __init__(self, parent): 1927db96d56Sopenharmony_ci import re 1937db96d56Sopenharmony_ci from idlelib import searchengine 1947db96d56Sopenharmony_ci 1957db96d56Sopenharmony_ci self.root = parent 1967db96d56Sopenharmony_ci self.engine = searchengine.get(parent) 1977db96d56Sopenharmony_ci self.create_widgets() 1987db96d56Sopenharmony_ci print(parent.geometry()) 1997db96d56Sopenharmony_ci width,height, x,y = list(map(int, re.split('[x+]', parent.geometry()))) 2007db96d56Sopenharmony_ci self.top.geometry("+%d+%d" % (x + 40, y + 175)) 2017db96d56Sopenharmony_ci 2027db96d56Sopenharmony_ci def default_command(self, dummy): pass 2037db96d56Sopenharmony_ci 2047db96d56Sopenharmony_ci 2057db96d56Sopenharmony_ciif __name__ == '__main__': 2067db96d56Sopenharmony_ci from unittest import main 2077db96d56Sopenharmony_ci main('idlelib.idle_test.test_searchbase', verbosity=2, exit=False) 2087db96d56Sopenharmony_ci 2097db96d56Sopenharmony_ci from idlelib.idle_test.htest import run 2107db96d56Sopenharmony_ci run(_searchbase) 211