Coverage for searchbase.py: 18%

114 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-11 13:22 -0700

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 

5from tkinter.simpledialog import _setup_dialog 

6 

7 

8class SearchDialogBase: 

9 '''Create most of a 3 or 4 row, 3 column search dialog. 

10 

11 The left and wide middle column contain: 

12 1 or 2 labeled text entry lines (make_entry, create_entries); 

13 a row of standard Checkbuttons (make_frame, create_option_buttons), 

14 each of which corresponds to a search engine Variable; 

15 a row of dialog-specific Check/Radiobuttons (create_other_buttons). 

16 

17 The narrow right column contains command buttons 

18 (make_button, create_command_buttons). 

19 These are bound to functions that execute the command. 

20 

21 Except for command buttons, this base class is not limited to items 

22 common to all three subclasses. Rather, it is the Find dialog minus 

23 the "Find Next" command, its execution function, and the 

24 default_command attribute needed in create_widgets. The other 

25 dialogs override attributes and methods, the latter to replace and 

26 add widgets. 

27 ''' 

28 

29 title = "Search Dialog" # replace in subclasses 

30 icon = "Search" 

31 needwrapbutton = 1 # not in Find in Files 

32 

33 def __init__(self, root, engine): 

34 '''Initialize root, engine, and top attributes. 

35 

36 top (level widget): set in create_widgets() called from open(). 

37 frame: container for all widgets in dialog. 

38 text (Text searched): set in open(), only used in subclasses(). 

39 ent (ry): created in make_entry() called from create_entry(). 

40 row (of grid): 0 in create_widgets(), +1 in make_entry/frame(). 

41 default_command: set in subclasses, used in create_widgets(). 

42 

43 title (of dialog): class attribute, override in subclasses. 

44 icon (of dialog): ditto, use unclear if cannot minimize dialog. 

45 ''' 

46 self.root = root 

47 self.bell = root.bell 

48 self.engine = engine 

49 self.top = None 

50 

51 def open(self, text, searchphrase=None): 

52 "Make dialog visible on top of others and ready to use." 

53 self.text = text 

54 if not self.top: 

55 self.create_widgets() 

56 else: 

57 self.top.deiconify() 

58 self.top.tkraise() 

59 self.top.transient(text.winfo_toplevel()) 

60 if searchphrase: 

61 self.ent.delete(0,"end") 

62 self.ent.insert("end",searchphrase) 

63 self.ent.focus_set() 

64 self.ent.selection_range(0, "end") 

65 self.ent.icursor(0) 

66 self.top.grab_set() 

67 

68 def close(self, event=None): 

69 "Put dialog away for later use." 

70 if self.top: 

71 self.top.grab_release() 

72 self.top.transient('') 

73 self.top.withdraw() 

74 

75 def create_widgets(self): 

76 '''Create basic 3 row x 3 col search (find) dialog. 

77 

78 Other dialogs override subsidiary create_x methods as needed. 

79 Replace and Find-in-Files add another entry row. 

80 ''' 

81 top = Toplevel(self.root) 

82 top.bind("<Return>", self.default_command) 

83 top.bind("<Escape>", self.close) 

84 top.protocol("WM_DELETE_WINDOW", self.close) 

85 top.wm_title(self.title) 

86 top.wm_iconname(self.icon) 

87 _setup_dialog(top) 

88 self.top = top 

89 self.frame = Frame(top, padding="5px") 

90 self.frame.grid(sticky="nwes") 

91 top.grid_columnconfigure(0, weight=100) 

92 top.grid_rowconfigure(0, weight=100) 

93 

94 self.row = 0 

95 self.frame.grid_columnconfigure(0, pad=2, weight=0) 

96 self.frame.grid_columnconfigure(1, pad=2, minsize=100, weight=100) 

97 

98 self.create_entries() # row 0 (and maybe 1), cols 0, 1 

99 self.create_option_buttons() # next row, cols 0, 1 

100 self.create_other_buttons() # next row, cols 0, 1 

101 self.create_command_buttons() # col 2, all rows 

102 

103 def make_entry(self, label_text, var): 

104 '''Return (entry, label), . 

105 

106 entry - gridded labeled Entry for text entry. 

107 label - Label widget, returned for testing. 

108 ''' 

109 label = Label(self.frame, text=label_text) 

110 label.grid(row=self.row, column=0, sticky="nw") 

111 entry = Entry(self.frame, textvariable=var, exportselection=0) 

112 entry.grid(row=self.row, column=1, sticky="nwe") 

113 self.row = self.row + 1 

114 return entry, label 

115 

116 def create_entries(self): 

117 "Create one or more entry lines with make_entry." 

118 self.ent = self.make_entry("Find:", self.engine.patvar)[0] 

119 

120 def make_frame(self,labeltext=None): 

121 '''Return (frame, label). 

122 

123 frame - gridded labeled Frame for option or other buttons. 

124 label - Label widget, returned for testing. 

125 ''' 

126 if labeltext: 

127 label = Label(self.frame, text=labeltext) 

128 label.grid(row=self.row, column=0, sticky="nw") 

129 else: 

130 label = '' 

131 frame = Frame(self.frame) 

132 frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe") 

133 self.row = self.row + 1 

134 return frame, label 

135 

136 def create_option_buttons(self): 

137 '''Return (filled frame, options) for testing. 

138 

139 Options is a list of searchengine booleanvar, label pairs. 

140 A gridded frame from make_frame is filled with a Checkbutton 

141 for each pair, bound to the var, with the corresponding label. 

142 ''' 

143 frame = self.make_frame("Options")[0] 

144 engine = self.engine 

145 options = [(engine.revar, "Regular expression"), 

146 (engine.casevar, "Match case"), 

147 (engine.wordvar, "Whole word")] 

148 if self.needwrapbutton: 

149 options.append((engine.wrapvar, "Wrap around")) 

150 for var, label in options: 

151 btn = Checkbutton(frame, variable=var, text=label) 

152 btn.pack(side="left", fill="both") 

153 return frame, options 

154 

155 def create_other_buttons(self): 

156 '''Return (frame, others) for testing. 

157 

158 Others is a list of value, label pairs. 

159 A gridded frame from make_frame is filled with radio buttons. 

160 ''' 

161 frame = self.make_frame("Direction")[0] 

162 var = self.engine.backvar 

163 others = [(1, 'Up'), (0, 'Down')] 

164 for val, label in others: 

165 btn = Radiobutton(frame, variable=var, value=val, text=label) 

166 btn.pack(side="left", fill="both") 

167 return frame, others 

168 

169 def make_button(self, label, command, isdef=0): 

170 "Return command button gridded in command frame." 

171 b = Button(self.buttonframe, 

172 text=label, command=command, 

173 default=isdef and "active" or "normal") 

174 cols,rows=self.buttonframe.grid_size() 

175 b.grid(pady=1,row=rows,column=0,sticky="ew") 

176 self.buttonframe.grid(rowspan=rows+1) 

177 return b 

178 

179 def create_command_buttons(self): 

180 "Place buttons in vertical command frame gridded on right." 

181 f = self.buttonframe = Frame(self.frame) 

182 f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2) 

183 

184 b = self.make_button("Close", self.close) 

185 b.lower() 

186 

187 

188class _searchbase(SearchDialogBase): # htest # 

189 "Create auto-opening dialog with no text connection." 

190 

191 def __init__(self, parent): 

192 import re 

193 from idlelib import searchengine 

194 

195 self.root = parent 

196 self.engine = searchengine.get(parent) 

197 self.create_widgets() 

198 print(parent.geometry()) 

199 width,height, x,y = list(map(int, re.split('[x+]', parent.geometry()))) 

200 self.top.geometry("+%d+%d" % (x + 40, y + 175)) 

201 

202 def default_command(self, dummy): pass 202 ↛ exitline 202 didn't return from function 'default_command'

203 

204 

205if __name__ == '__main__': 205 ↛ 206line 205 didn't jump to line 206, because the condition on line 205 was never true

206 from unittest import main 

207 main('idlelib.idle_test.test_searchbase', verbosity=2, exit=False) 

208 

209 from idlelib.idle_test.htest import run 

210 run(_searchbase)