python (3.12.0)
1 import sys
2 import unittest
3 import tkinter
4 from tkinter import ttk
5 from test.support import requires, gc_collect
6 from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest
7
8 requires('gui')
9
10 class ESC[4;38;5;81mLabeledScaleTest(ESC[4;38;5;149mAbstractTkTest, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
11
12 def tearDown(self):
13 self.root.update_idletasks()
14 super().tearDown()
15
16 def test_widget_destroy(self):
17 # automatically created variable
18 x = ttk.LabeledScale(self.root)
19 var = x._variable._name
20 x.destroy()
21 gc_collect() # For PyPy or other GCs.
22 self.assertRaises(tkinter.TclError, x.tk.globalgetvar, var)
23
24 # manually created variable
25 myvar = tkinter.DoubleVar(self.root)
26 name = myvar._name
27 x = ttk.LabeledScale(self.root, variable=myvar)
28 x.destroy()
29 if self.wantobjects:
30 self.assertEqual(x.tk.globalgetvar(name), myvar.get())
31 else:
32 self.assertEqual(float(x.tk.globalgetvar(name)), myvar.get())
33 del myvar
34 gc_collect() # For PyPy or other GCs.
35 self.assertRaises(tkinter.TclError, x.tk.globalgetvar, name)
36
37 # checking that the tracing callback is properly removed
38 myvar = tkinter.IntVar(self.root)
39 # LabeledScale will start tracing myvar
40 x = ttk.LabeledScale(self.root, variable=myvar)
41 x.destroy()
42 # Unless the tracing callback was removed, creating a new
43 # LabeledScale with the same var will cause an error now. This
44 # happens because the variable will be set to (possibly) a new
45 # value which causes the tracing callback to be called and then
46 # it tries calling instance attributes not yet defined.
47 ttk.LabeledScale(self.root, variable=myvar)
48 if hasattr(sys, 'last_exc'):
49 self.assertNotEqual(type(sys.last_exc), tkinter.TclError)
50 elif hasattr(sys, 'last_type'):
51 self.assertNotEqual(sys.last_type, tkinter.TclError)
52
53 def test_initialization(self):
54 # master passing
55 master = tkinter.Frame(self.root)
56 x = ttk.LabeledScale(master)
57 self.assertEqual(x.master, master)
58 x.destroy()
59
60 # variable initialization/passing
61 passed_expected = (('0', 0), (0, 0), (10, 10),
62 (-1, -1), (sys.maxsize + 1, sys.maxsize + 1),
63 (2.5, 2), ('2.5', 2))
64 for pair in passed_expected:
65 x = ttk.LabeledScale(self.root, from_=pair[0])
66 self.assertEqual(x.value, pair[1])
67 x.destroy()
68 x = ttk.LabeledScale(self.root, from_=None)
69 self.assertRaises((ValueError, tkinter.TclError), x._variable.get)
70 x.destroy()
71 # variable should have its default value set to the from_ value
72 myvar = tkinter.DoubleVar(self.root, value=20)
73 x = ttk.LabeledScale(self.root, variable=myvar)
74 self.assertEqual(x.value, 0)
75 x.destroy()
76 # check that it is really using a DoubleVar
77 x = ttk.LabeledScale(self.root, variable=myvar, from_=0.5)
78 self.assertEqual(x.value, 0.5)
79 self.assertEqual(x._variable._name, myvar._name)
80 x.destroy()
81
82 # widget positionment
83 def check_positions(scale, scale_pos, label, label_pos):
84 self.assertEqual(scale.pack_info()['side'], scale_pos)
85 self.assertEqual(label.place_info()['anchor'], label_pos)
86 x = ttk.LabeledScale(self.root, compound='top')
87 check_positions(x.scale, 'bottom', x.label, 'n')
88 x.destroy()
89 x = ttk.LabeledScale(self.root, compound='bottom')
90 check_positions(x.scale, 'top', x.label, 's')
91 x.destroy()
92 # invert default positions
93 x = ttk.LabeledScale(self.root, compound='unknown')
94 check_positions(x.scale, 'top', x.label, 's')
95 x.destroy()
96 x = ttk.LabeledScale(self.root) # take default positions
97 check_positions(x.scale, 'bottom', x.label, 'n')
98 x.destroy()
99
100 # extra, and invalid, kwargs
101 self.assertRaises(tkinter.TclError, ttk.LabeledScale, master, a='b')
102
103
104 def test_horizontal_range(self):
105 lscale = ttk.LabeledScale(self.root, from_=0, to=10)
106 lscale.pack()
107 lscale.update()
108
109 linfo_1 = lscale.label.place_info()
110 prev_xcoord = lscale.scale.coords()[0]
111 self.assertEqual(prev_xcoord, int(linfo_1['x']))
112 # change range to: from -5 to 5. This should change the x coord of
113 # the scale widget, since 0 is at the middle of the new
114 # range.
115 lscale.scale.configure(from_=-5, to=5)
116 # The following update is needed since the test doesn't use mainloop,
117 # at the same time this shouldn't affect test outcome
118 lscale.update()
119 curr_xcoord = lscale.scale.coords()[0]
120 self.assertNotEqual(prev_xcoord, curr_xcoord)
121 # the label widget should have been repositioned too
122 linfo_2 = lscale.label.place_info()
123 self.assertEqual(lscale.label['text'], 0 if self.wantobjects else '0')
124 self.assertEqual(curr_xcoord, int(linfo_2['x']))
125 # change the range back
126 lscale.scale.configure(from_=0, to=10)
127 self.assertNotEqual(prev_xcoord, curr_xcoord)
128 self.assertEqual(prev_xcoord, int(linfo_1['x']))
129
130 lscale.destroy()
131
132
133 def test_variable_change(self):
134 x = ttk.LabeledScale(self.root)
135 x.pack()
136 x.update()
137
138 curr_xcoord = x.scale.coords()[0]
139 newval = x.value + 1
140 x.value = newval
141 # The following update is needed since the test doesn't use mainloop,
142 # at the same time this shouldn't affect test outcome
143 x.update()
144 self.assertEqual(x.value, newval)
145 self.assertEqual(x.label['text'],
146 newval if self.wantobjects else str(newval))
147 self.assertEqual(float(x.scale.get()), newval)
148 self.assertGreater(x.scale.coords()[0], curr_xcoord)
149 self.assertEqual(x.scale.coords()[0],
150 int(x.label.place_info()['x']))
151
152 # value outside range
153 if self.wantobjects:
154 conv = lambda x: x
155 else:
156 conv = int
157 x.value = conv(x.scale['to']) + 1 # no changes shouldn't happen
158 x.update()
159 self.assertEqual(x.value, newval)
160 self.assertEqual(conv(x.label['text']), newval)
161 self.assertEqual(float(x.scale.get()), newval)
162 self.assertEqual(x.scale.coords()[0],
163 int(x.label.place_info()['x']))
164
165 # non-integer value
166 x.value = newval = newval + 1.5
167 x.update()
168 self.assertEqual(x.value, int(newval))
169 self.assertEqual(conv(x.label['text']), int(newval))
170 self.assertEqual(float(x.scale.get()), newval)
171
172 x.destroy()
173
174
175 def test_resize(self):
176 x = ttk.LabeledScale(self.root)
177 x.pack(expand=True, fill='both')
178 gc_collect() # For PyPy or other GCs.
179 x.update()
180
181 width, height = x.master.winfo_width(), x.master.winfo_height()
182 width_new, height_new = width * 2, height * 2
183
184 x.value = 3
185 x.update()
186 x.master.wm_geometry("%dx%d" % (width_new, height_new))
187 self.assertEqual(int(x.label.place_info()['x']),
188 x.scale.coords()[0])
189
190 # Reset geometry
191 x.master.wm_geometry("%dx%d" % (width, height))
192 x.destroy()
193
194
195 class ESC[4;38;5;81mOptionMenuTest(ESC[4;38;5;149mAbstractTkTest, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
196
197 def setUp(self):
198 super().setUp()
199 self.textvar = tkinter.StringVar(self.root)
200
201 def tearDown(self):
202 del self.textvar
203 super().tearDown()
204
205
206 def test_widget_destroy(self):
207 var = tkinter.StringVar(self.root)
208 optmenu = ttk.OptionMenu(self.root, var)
209 name = var._name
210 optmenu.update_idletasks()
211 optmenu.destroy()
212 self.assertEqual(optmenu.tk.globalgetvar(name), var.get())
213 del var
214 gc_collect() # For PyPy or other GCs.
215 self.assertRaises(tkinter.TclError, optmenu.tk.globalgetvar, name)
216
217
218 def test_initialization(self):
219 self.assertRaises(tkinter.TclError,
220 ttk.OptionMenu, self.root, self.textvar, invalid='thing')
221
222 optmenu = ttk.OptionMenu(self.root, self.textvar, 'b', 'a', 'b')
223 self.assertEqual(optmenu._variable.get(), 'b')
224
225 self.assertTrue(optmenu['menu'])
226 self.assertTrue(optmenu['textvariable'])
227
228 optmenu.destroy()
229
230
231 def test_menu(self):
232 items = ('a', 'b', 'c')
233 default = 'a'
234 optmenu = ttk.OptionMenu(self.root, self.textvar, default, *items)
235 found_default = False
236 for i in range(len(items)):
237 value = optmenu['menu'].entrycget(i, 'value')
238 self.assertEqual(value, items[i])
239 if value == default:
240 found_default = True
241 self.assertTrue(found_default)
242 optmenu.destroy()
243
244 # default shouldn't be in menu if it is not part of values
245 default = 'd'
246 optmenu = ttk.OptionMenu(self.root, self.textvar, default, *items)
247 curr = None
248 i = 0
249 while True:
250 last, curr = curr, optmenu['menu'].entryconfigure(i, 'value')
251 if last == curr:
252 # no more menu entries
253 break
254 self.assertNotEqual(curr, default)
255 i += 1
256 self.assertEqual(i, len(items))
257
258 # check that variable is updated correctly
259 optmenu.pack()
260 gc_collect() # For PyPy or other GCs.
261 optmenu['menu'].invoke(0)
262 self.assertEqual(optmenu._variable.get(), items[0])
263
264 # changing to an invalid index shouldn't change the variable
265 self.assertRaises(tkinter.TclError, optmenu['menu'].invoke, -1)
266 self.assertEqual(optmenu._variable.get(), items[0])
267
268 optmenu.destroy()
269
270 # specifying a callback
271 success = []
272 def cb_test(item):
273 self.assertEqual(item, items[1])
274 success.append(True)
275 optmenu = ttk.OptionMenu(self.root, self.textvar, 'a', command=cb_test,
276 *items)
277 optmenu['menu'].invoke(1)
278 if not success:
279 self.fail("Menu callback not invoked")
280
281 optmenu.destroy()
282
283 def test_unique_radiobuttons(self):
284 # check that radiobuttons are unique across instances (bpo25684)
285 items = ('a', 'b', 'c')
286 default = 'a'
287 optmenu = ttk.OptionMenu(self.root, self.textvar, default, *items)
288 textvar2 = tkinter.StringVar(self.root)
289 optmenu2 = ttk.OptionMenu(self.root, textvar2, default, *items)
290 optmenu.pack()
291 optmenu2.pack()
292 optmenu['menu'].invoke(1)
293 optmenu2['menu'].invoke(2)
294 optmenu_stringvar_name = optmenu['menu'].entrycget(0, 'variable')
295 optmenu2_stringvar_name = optmenu2['menu'].entrycget(0, 'variable')
296 self.assertNotEqual(optmenu_stringvar_name,
297 optmenu2_stringvar_name)
298 self.assertEqual(self.root.tk.globalgetvar(optmenu_stringvar_name),
299 items[1])
300 self.assertEqual(self.root.tk.globalgetvar(optmenu2_stringvar_name),
301 items[2])
302
303 optmenu.destroy()
304 optmenu2.destroy()
305
306 def test_trace_variable(self):
307 # prior to bpo45160, tracing a variable would cause the callback to be made twice
308 success = []
309 items = ('a', 'b', 'c')
310 textvar = tkinter.StringVar(self.root)
311 def cb_test(*args):
312 success.append(textvar.get())
313 optmenu = ttk.OptionMenu(self.root, textvar, "a", *items)
314 optmenu.pack()
315 cb_name = textvar.trace_add("write", cb_test)
316 optmenu['menu'].invoke(1)
317 self.assertEqual(success, ['b'])
318 self.assertEqual(textvar.get(), 'b')
319 textvar.trace_remove("write", cb_name)
320 optmenu.destroy()
321
322
323 class ESC[4;38;5;81mDefaultRootTest(ESC[4;38;5;149mAbstractDefaultRootTest, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
324
325 def test_labeledscale(self):
326 self._test_widget(ttk.LabeledScale)
327
328
329 if __name__ == "__main__":
330 unittest.main()