gh-59396: Use themed widgets in tkinter.filedialog (GH-152036) · python/cpython@3d9d6c8

GitHub

@@ -1,6 +1,8 @@

11importos

22importunittest

3+importtkinter

34fromtkinterimportfiledialog

5+fromtkinterimportttk

46fromtkinter.commondialogimportDialog

57fromtest.supportimportrequires, swap_attr

68fromtest.test_tkinter.supportimportsetUpModule# noqa: F401

@@ -101,6 +103,104 @@ def test_subclasses(self):

101103self.assertIsInstance(d, filedialog.FileDialog)

102104self.assertEqual(d.top.title(), cls.title)

103105106+# --- Themed widgets and keyboard (modernization) ---

107+108+defopen(self, **kw):

109+d=filedialog.FileDialog(self.root, **kw)

110+self.addCleanup(lambda: d.top.winfo_exists() andd.top.destroy())

111+d.top.deiconify() # __init__ leaves the dialog withdrawn until go()

112+d.top.update()

113+returnd

114+115+deftest_use_ttk(self):

116+# The dialog uses the themed (ttk) widgets by default.

117+d=self.open()

118+self.assertEqual(d.ok_button.winfo_class(), 'TButton')

119+self.assertEqual(d.selection.winfo_class(), 'TEntry')

120+121+deftest_use_classic(self):

122+# use_ttk=False uses the classic Tk widgets.

123+d=self.open(use_ttk=False)

124+self.assertEqual(d.ok_button.winfo_class(), 'Button')

125+self.assertEqual(d.selection.winfo_class(), 'Entry')

126+ifd.top._windowingsystem=='x11':

127+self.assertEqual(str(d.botframe.cget('relief')), 'raised')

128+129+deftest_background(self):

130+# The ttk dialog adopts the ttk background, even a customized one, while

131+# the classic dialog keeps the default Toplevel background.

132+style=ttk.Style(self.root)

133+old=style.lookup('.', 'background')

134+style.configure('.', background='#123456')

135+self.addCleanup(style.configure, '.', background=old)

136+d=self.open()

137+self.assertEqual(str(d.top.cget('background')), '#123456')

138+d=self.open(use_ttk=False)

139+ref=tkinter.Toplevel(self.root)

140+self.addCleanup(ref.destroy)

141+self.assertEqual(str(d.top.cget('background')),

142+str(ref.cget('background')))

143+144+deftest_button_accelerator(self):

145+# The buttons' "&" accelerators are parsed.

146+d=self.open()

147+self.assertEqual(str(d.ok_button.cget('text')), 'OK')

148+self.assertEqual(int(d.ok_button.cget('underline')), 0)

149+150+deftest_default_ring(self):

151+# The default ring follows the keyboard focus among the buttons.

152+d=self.open()

153+self.assertEqual(str(d.cancel_button.cget('default')), 'normal')

154+d.cancel_button.focus_force()

155+d.top.update()

156+self.assertEqual(str(d.cancel_button.cget('default')), 'active')

157+d.ok_button.focus_force()

158+d.top.update()

159+self.assertEqual(str(d.cancel_button.cget('default')), 'normal')

160+161+deftest_alt_key(self):

162+# Alt + the underlined letter invokes the matching button.

163+d=self.open()

164+invoked= []

165+d.cancel_button.configure(command=lambda: invoked.append(True))

166+d.top.focus_force()

167+d.top.update()

168+d.top.event_generate('<Alt-c>') # "&Cancel"

169+d.top.update()

170+self.assertTrue(invoked)

171+172+deftest_escape_cancels(self):

173+# The Escape key cancels the dialog.

174+d=self.open()

175+d.how='spam'

176+d.top.focus_force()

177+d.top.update()

178+d.top.event_generate('<Escape>')

179+d.top.update()

180+self.assertIsNone(d.how)

181+182+deftest_horizontal_scrollbars(self):

183+# Each list has a horizontal scrollbar besides the vertical one.

184+d=self.open()

185+self.assertEqual(str(d.dirshbar.cget('orient')), 'horizontal')

186+self.assertEqual(str(d.fileshbar.cget('orient')), 'horizontal')

187+self.assertTrue(d.dirs.cget('xscrollcommand'))

188+self.assertTrue(d.files.cget('xscrollcommand'))

189+190+deftest_type_ahead(self):

191+# Typing characters over a list jumps to a matching entry.

192+d=self.open()

193+d.directory=os.getcwd() # browsing the match fills the selection entry

194+d.files.delete(0, 'end')

195+fornamein ('alpha', 'bravo', 'charlie'):

196+d.files.insert('end', name)

197+d.files.focus_force()

198+d.top.update()

199+d.files.event_generate('<Key>', keysym='c')

200+d.top.update()

201+sel=d.files.curselection()

202+self.assertEqual([d.files.get(i) foriinsel], ['charlie'])

203+104204105205if__name__=="__main__":

106206unittest.main()