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