Coverage for src/tkinterquickhelper/funcwin/frame_params.py: 18%

163 statements  

« prev     ^ index     » next       coverage.py v7.1.0, created at 2024-04-25 05:39 +0200

1# -*- coding: utf-8 -*- 

2""" 

3@file 

4 

5@brief Defines @see cl FrameParams. 

6""" 

7import sys 

8import os 

9import tkinter 

10from .tk_window import create_tk 

11from .function_helper import private_adjust_parameters 

12from .storing_functions import _private_restore, _private_store, interpret_parameter 

13 

14 

15class FrameParams(tkinter.Frame): 

16 """ 

17 Creates a Frame window for a list of parameters. 

18 """ 

19 

20 def __init__(self, parent, restore=True, width=100, raise_exception=False, 

21 params=None, help="", key_save="", command_leave=None): # pylint: disable=W0622 

22 """ 

23 @param parent window parent 

24 @param restore if True, check if existing saved parameters are present 

25 @param width number of characters in every Entry field 

26 @param raise_exception raise an exception instead of catching it 

27 @param params parameters to overwrite 

28 @param help help to display 

29 @param key_save to make unique the file storing and restoring the parameters 

30 @param command_leave if not None, this function will be called when clicking on Cancel or Leave 

31 """ 

32 if params is None: 

33 params = {} 

34 

35 tkinter.Frame.__init__(self, parent) 

36 self.fdoc = tkinter.Frame(self) 

37 self.fpar = tkinter.Frame(self) 

38 self.fbut = tkinter.Frame(self) 

39 self.fpar.pack() 

40 self.fbut.pack() 

41 self.fdoc.pack() 

42 self.restore = restore 

43 self.parent = parent 

44 self.input = {} 

45 self.types = {} 

46 self.raise_exception = raise_exception 

47 self._added = {} 

48 self.key_save = key_save 

49 self.command_leave = command_leave 

50 

51 # retrieve previous answers 

52 self._history = [] 

53 self._hpos = -1 

54 

55 self.info = {"name": "FrameParams", "param": params, 

56 "help": help, "key_save": key_save} 

57 

58 objs = [] 

59 

60 if restore: 

61 self._history = _private_restore( 

62 ".".join([self.info["name"], self.info["key_save"]])) 

63 if len(self._history) > 0: 

64 self.info["param"].update(self._history[-1]) 

65 self._hpos = len(self._history) - 1 

66 

67 for k in self.info["param"]: 

68 self.types[k] = self.info["param"][k].__class__ 

69 if self.types[k] in [None, None.__class__]: 

70 self.types[k] = str 

71 

72 # documentation 

73 tlab = tkinter.Label(self.fdoc, text="Help") 

74 tlab.pack(side=tkinter.LEFT) 

75 lab = tkinter.Text(self.fdoc, width=width, height=7) 

76 lab.pack(side=tkinter.LEFT) 

77 lab.insert("0.0", self.info["help"]) 

78 objs.append(lab) 

79 objs.append(tlab) 

80 

81 scroll = tkinter.Scrollbar(self.fdoc) 

82 scroll.pack(side=tkinter.RIGHT, fill=tkinter.Y) 

83 scroll.config(command=lab.yview, width=5) 

84 lab.config(yscrollcommand=scroll.set) 

85 

86 # next 

87 line = 0 

88 for k in sorted(self.info["param"]): 

89 if k in self._added: 

90 continue 

91 lab = tkinter.Label(self.fpar, text=k) 

92 lab.grid(row=line, column=0) 

93 

94 if k in ["password", "password1", "password2", "password3"]: 

95 lab = tkinter.Entry(self.fpar, width=width, show="*") 

96 else: 

97 lab = tkinter.Entry(self.fpar, width=width) 

98 

99 lab.grid(row=line, column=1) 

100 if self.info["param"][k] is not None: 

101 lab.insert("0", str(self.info["param"][k])) 

102 self.input[k] = lab 

103 objs.append(lab) 

104 line += 1 

105 

106 # optional 

107 for k in sorted(self.info["param"]): 

108 if k not in self._added: 

109 continue 

110 lab = tkinter.Label(self.fpar, text=k) 

111 lab.grid(row=line, column=0) 

112 

113 if k in ["password", "password1", "password2", "password3"]: 

114 lab = tkinter.Entry(self.fpar, width=width, show="*") 

115 else: 

116 lab = tkinter.Entry(self.fpar, width=width) 

117 

118 lab.grid(row=line, column=1) 

119 if self.info["param"][k] is not None: 

120 lab.insert("0", str(self.info["param"][k])) 

121 self.input[k] = lab 

122 objs.append(lab) 

123 line += 1 

124 

125 # next: button 

126 self.cancel = tkinter.Button(self.fbut, text="cancel or leave") 

127 self.run = tkinter.Button(self.fbut, text=" ok ") 

128 self.cancel.pack(side=tkinter.LEFT) 

129 self.run.pack(side=tkinter.LEFT) 

130 self.run.bind('<Return>', self.run_function) 

131 self.run.bind('<Escape>', self.run_cancel) 

132 

133 self.cancel.config(command=self.run_cancel) 

134 self.run.config(command=self.run_function) 

135 private_adjust_parameters(self.info["param"]) 

136 self._already = False 

137 

138 # up, down 

139 self.bup = tkinter.Button(self.fbut, text="up") 

140 self.bdown = tkinter.Button(self.fbut, text="down") 

141 self.bup.pack(side=tkinter.LEFT) 

142 self.bdown.pack(side=tkinter.LEFT) 

143 self.bup.config(command=self.history_up) 

144 self.bdown.config(command=self.history_down) 

145 

146 # keys 

147 for obj in objs + \ 

148 [parent, self, self.bup, self.bdown, self.run, self.cancel, self.fdoc]: 

149 obj.bind("<Up>", self.history_up) 

150 obj.bind("<Down>", self.history_down) 

151 obj.bind("<Return>", self.run_function) 

152 obj.bind("<Escape>", self.run_cancel) 

153 

154 def update(self): 

155 """ 

156 update the parameters (ie ``self.info``) 

157 """ 

158 for k in self.input: # pylint: disable=C0206 

159 self.input[k].delete(0, tkinter.END) 

160 self.input[k].insert("0", str(self.info["param"].get(k, ""))) 

161 

162 def history_up(self, *args): 

163 """ 

164 look back in the history (log of used parameters) 

165 and update the parameters 

166 """ 

167 if len(self._history) > 0: 

168 self._hpos = (self._hpos + 1) % len(self._history) 

169 self.info["param"].update(self._history[self._hpos]) 

170 self.update() 

171 

172 def history_down(self, *args): 

173 """ 

174 look forward in the history (log of used parameters) 

175 and update the parameters 

176 """ 

177 if len(self._history) > 0: 

178 self._hpos = ( 

179 self._hpos + len(self._history) - 1) % len(self._history) 

180 self.info["param"].update(self._history[self._hpos]) 

181 self.update() 

182 

183 def run_cancel(self, *args): 

184 """ 

185 what to do when Cancel is pressed 

186 """ 

187 self.info["param"]["__cancel__"] = True 

188 if self.command_leave is not None: 

189 self.command_leave() 

190 else: 

191 self.parent.destroy() 

192 

193 def get_parameters(self): 

194 """ 

195 returns the parameters 

196 

197 @return dictionary 

198 """ 

199 res = {} 

200 for k, v in self.input.items(): 

201 s = v.get() 

202 s = s.strip() 

203 if len(s) == 0: 

204 s = None 

205 ty = self.types[k] 

206 res[k] = interpret_parameter(ty, s) 

207 return res 

208 

209 def get_title(self): 

210 """ 

211 Returns the title. 

212 

213 @return ``self.info ["name"]`` 

214 """ 

215 return self.info["name"] 

216 

217 def refresh(self): 

218 """ 

219 Refreshes the screen. 

220 """ 

221 if self._already: 

222 self.after(1000, self.refresh) 

223 else: 

224 self.run.config(state=tkinter.NORMAL) 

225 self.parent.destroy() 

226 

227 def run_function(self, *args): 

228 """ 

229 Runs the function. 

230 """ 

231 self.parent.withdraw() 

232 # self.run.config(state=tkinter.DISABLED) 

233 self._already = True 

234 

235 res = self.get_parameters() 

236 if self.restore: 

237 _private_store( 

238 ".".join([self.info["name"], self.info["key_save"]]), res) 

239 

240 self.info["param"].update(res) 

241 self.parent.destroy() 

242 

243 @staticmethod 

244 def open_window(params, 

245 help_string="", 

246 title="", 

247 top_level_window=None, 

248 key_save="", 

249 do_not_open=False): 

250 """ 

251 Opens a :epkg:`tkinter` window to set up parameters. 

252 It adds entries for the parameters, 

253 it displays the help given to this function. 

254 It also memorizes the latest values used (stored in ``<user>/TEMP folder``). 

255 

256 @param help_string help to de displayed 

257 @param top_level_window if you want this window to depend on a top level window from tkinter 

258 @param params if not None, overwrite values for some parameters, 

259 it will be updated by the function (= returned value) 

260 @param key_save parameters are saved and restore from a file, key_save will make this file unique 

261 @param title title of the window 

262 @param do_not_open do not open the window, let you do it 

263 @return new parameters (or a the Windows object if *do_not_open* is True) 

264 

265 @warning If the string "__cancel__" is present in the results, it means the users clicked on cancel. 

266 

267 The window looks like: 

268 @image images/open_window_params.png 

269 

270 Example:: 

271 

272 params = {"velib_key": "", "contract":"Paris"} 

273 newparams = FrameParams.open_window (params, "fetch data from Velib website") 

274 

275 .. versionchanged:: 1.0 

276 Parameter *do_not_open* was added. 

277 """ 

278 param = params if params is not None else {} 

279 

280 root = top_level_window if top_level_window is not None else create_tk() 

281 ico = os.path.realpath( 

282 os.path.join(os.path.split(__file__)[0], "project_ico.ico")) 

283 fr = FrameParams( 

284 root, params=param, help=help_string, key_save=key_save) 

285 fr.pack() 

286 root.title(title) 

287 if ico is not None and top_level_window is None and sys.platform.startswith( 

288 "win"): 

289 root.wm_iconbitmap(ico) 

290 if top_level_window is None: 

291 fr.focus_set() 

292 root.focus_set() 

293 

294 if do_not_open: 

295 return fr 

296 else: 

297 fr.mainloop() 

298 return param 

299 

300 

301def open_window_params(params, help_string="", title="", top_level_window=None, 

302 key_save="", do_not_open=False): 

303 """ 

304 Open a :epkg:`tkinter` window to set up parameters. 

305 It adds entries for the parameters, 

306 it displays the help given to this function. 

307 It also memorizes the latest values used 

308 (stored in `<user>/TEMP` folder). 

309 

310 @param help_string help to de displayed 

311 @param top_level_window if you want this window to depend on a top level window from tkinter 

312 @param params if not None, overwrite values for some parameters, 

313 it will be updated by the function (= returned value) 

314 @param key_save parameters are saved and restore from a file, key_save will make this file unique 

315 @param title title of the window 

316 @param do_not_open do not open the window, let you do it 

317 @return new parameters (or a the Windows object if *do_not_open* is True) 

318 

319 @warning If the string "__cancel__" is present in the results, it means the users clicked on cancel. 

320 

321 The window looks like: 

322 @image images/open_window_params.png 

323 

324 .. exref:: 

325 :title: Open a tkinter window to ask parameters to a user 

326 

327 :: 

328 

329 params = { "user": os.environ.get("USERNAME", os.environ["USER"]), 

330 "password":"" } 

331 newparams = open_window_params(params, title="try the password *", 

332 help_string="unit test", key_save="my_key") 

333 

334 The program opens a window like the following one: 

335 

336 @image images/open_params.png 

337 

338 The parameters ``key_save`` can be ignored but if you use this function 

339 with different parameters, they should all appear after a couple of runs. 

340 That is because the function uses ``key_save`` ot unique the file uses 

341 to store the values for the parameters used in previous execution. 

342 

343 Password are not stored in a text file. You must type them again next time. 

344 """ 

345 return FrameParams.open_window(params=params, help_string=help_string, title=title, 

346 top_level_window=top_level_window, key_save=key_save, 

347 do_not_open=do_not_open)