1 import unittest 2 import Tkinter 3 import ttk 4 from test.test_support import requires, run_unittest 5 import sys 6 7 import support 8 from test_functions import MockTclObj, MockStateSpec 9 10 requires('gui') 11 12 class WidgetTest(unittest.TestCase): 13 """Tests methods available in every ttk widget.""" 14 15 def setUp(self): 16 support.root_deiconify() 17 self.widget = ttk.Button(width=0, text="Text") 18 self.widget.pack() 19 self.widget.wait_visibility() 20 21 def tearDown(self): 22 self.widget.destroy() 23 support.root_withdraw() 24 25 26 def test_identify(self): 27 self.widget.update_idletasks() 28 self.assertEqual(self.widget.identify( 29 int(self.widget.winfo_width() / 2), 30 int(self.widget.winfo_height() / 2) 31 ), "label") 32 self.assertEqual(self.widget.identify(-1, -1), "") 33 34 self.assertRaises(Tkinter.TclError, self.widget.identify, None, 5) 35 self.assertRaises(Tkinter.TclError, self.widget.identify, 5, None) 36 self.assertRaises(Tkinter.TclError, self.widget.identify, 5, '') 37 38 39 def test_widget_state(self): 40 # XXX not sure about the portability of all these tests 41 self.assertEqual(self.widget.state(), ()) 42 self.assertEqual(self.widget.instate(['!disabled']), True) 43 44 # changing from !disabled to disabled 45 self.assertEqual(self.widget.state(['disabled']), ('!disabled', )) 46 # no state change 47 self.assertEqual(self.widget.state(['disabled']), ()) 48 # change back to !disable but also active 49 self.assertEqual(self.widget.state(['!disabled', 'active']), 50 ('!active', 'disabled')) 51 # no state changes, again 52 self.assertEqual(self.widget.state(['!disabled', 'active']), ()) 53 self.assertEqual(self.widget.state(['active', '!disabled']), ()) 54 55 def test_cb(arg1, **kw): 56 return arg1, kw 57 self.assertEqual(self.widget.instate(['!disabled'], 58 test_cb, "hi", **{"msg": "there"}), 59 ('hi', {'msg': 'there'})) 60 61 # attempt to set invalid statespec 62 currstate = self.widget.state() 63 self.assertRaises(Tkinter.TclError, self.widget.instate, 64 ['badstate']) 65 self.assertRaises(Tkinter.TclError, self.widget.instate, 66 ['disabled', 'badstate']) 67 # verify that widget didn't change its state 68 self.assertEqual(currstate, self.widget.state()) 69 70 # ensuring that passing None as state doesn't modify current state 71 self.widget.state(['active', '!disabled']) 72 self.assertEqual(self.widget.state(), ('active', )) 73 74 75 class ButtonTest(unittest.TestCase): 76 77 def test_invoke(self): 78 success = [] 79 btn = ttk.Button(command=lambda: success.append(1)) 80 btn.invoke() 81 self.assertTrue(success) 82 83 84 class CheckbuttonTest(unittest.TestCase): 85 86 def test_invoke(self): 87 success = [] 88 def cb_test(): 89 success.append(1) 90 return "cb test called" 91 92 cbtn = ttk.Checkbutton(command=cb_test) 93 # the variable automatically created by ttk.Checkbutton is actually 94 # undefined till we invoke the Checkbutton 95 self.assertEqual(cbtn.state(), ('alternate', )) 96 self.assertRaises(Tkinter.TclError, cbtn.tk.globalgetvar, 97 cbtn['variable']) 98 99 res = cbtn.invoke() 100 self.assertEqual(res, "cb test called") 101 self.assertEqual(cbtn['onvalue'], 102 cbtn.tk.globalgetvar(cbtn['variable'])) 103 self.assertTrue(success) 104 105 cbtn['command'] = '' 106 res = cbtn.invoke() 107 self.assertEqual(res, '') 108 self.assertFalse(len(success) > 1) 109 self.assertEqual(cbtn['offvalue'], 110 cbtn.tk.globalgetvar(cbtn['variable'])) 111 112 113 class ComboboxTest(unittest.TestCase): 114 115 def setUp(self): 116 support.root_deiconify() 117 self.combo = ttk.Combobox() 118 119 def tearDown(self): 120 self.combo.destroy() 121 support.root_withdraw() 122 123 def _show_drop_down_listbox(self): 124 width = self.combo.winfo_width() 125 self.combo.event_generate('<ButtonPress-1>', x=width - 5, y=5) 126 self.combo.event_generate('<ButtonRelease-1>', x=width - 5, y=5) 127 self.combo.update_idletasks() 128 129 130 def test_virtual_event(self): 131 success = [] 132 133 self.combo['values'] = [1] 134 self.combo.bind('<<ComboboxSelected>>', 135 lambda evt: success.append(True)) 136 self.combo.pack() 137 self.combo.wait_visibility() 138 139 height = self.combo.winfo_height() 140 self._show_drop_down_listbox() 141 self.combo.update() 142 self.combo.event_generate('<Return>') 143 self.combo.update() 144 145 self.assertTrue(success) 146 147 148 def test_postcommand(self): 149 success = [] 150 151 self.combo['postcommand'] = lambda: success.append(True) 152 self.combo.pack() 153 self.combo.wait_visibility() 154 155 self._show_drop_down_listbox() 156 self.assertTrue(success) 157 158 # testing postcommand removal 159 self.combo['postcommand'] = '' 160 self._show_drop_down_listbox() 161 self.assertEqual(len(success), 1) 162 163 164 def test_values(self): 165 def check_get_current(getval, currval): 166 self.assertEqual(self.combo.get(), getval) 167 self.assertEqual(self.combo.current(), currval) 168 169 check_get_current('', -1) 170 171 self.combo['values'] = ['a', 1, 'c'] 172 173 self.combo.set('c') 174 check_get_current('c', 2) 175 176 self.combo.current(0) 177 check_get_current('a', 0) 178 179 self.combo.set('d') 180 check_get_current('d', -1) 181 182 # testing values with empty string 183 self.combo.set('') 184 self.combo['values'] = (1, 2, '', 3) 185 check_get_current('', 2) 186 187 # testing values with empty string set through configure 188 self.combo.configure(values=[1, '', 2]) 189 self.assertEqual(self.combo['values'], ('1', '', '2')) 190 191 # testing values with spaces 192 self.combo['values'] = ['a b', 'a\tb', 'a\nb'] 193 self.assertEqual(self.combo['values'], ('a b', 'a\tb', 'a\nb')) 194 195 # testing values with special characters 196 self.combo['values'] = [r'a\tb', '"a"', '} {'] 197 self.assertEqual(self.combo['values'], (r'a\tb', '"a"', '} {')) 198 199 # out of range 200 self.assertRaises(Tkinter.TclError, self.combo.current, 201 len(self.combo['values'])) 202 # it expects an integer (or something that can be converted to int) 203 self.assertRaises(Tkinter.TclError, self.combo.current, '') 204 205 # testing creating combobox with empty string in values 206 combo2 = ttk.Combobox(values=[1, 2, '']) 207 self.assertEqual(combo2['values'], ('1', '2', '')) 208 combo2.destroy() 209 210 211 class EntryTest(unittest.TestCase): 212 213 def setUp(self): 214 support.root_deiconify() 215 self.entry = ttk.Entry() 216 217 def tearDown(self): 218 self.entry.destroy() 219 support.root_withdraw() 220 221 222 def test_bbox(self): 223 self.assertEqual(len(self.entry.bbox(0)), 4) 224 for item in self.entry.bbox(0): 225 self.assertTrue(isinstance(item, int)) 226 227 self.assertRaises(Tkinter.TclError, self.entry.bbox, 'noindex') 228 self.assertRaises(Tkinter.TclError, self.entry.bbox, None) 229 230 231 def test_identify(self): 232 self.entry.pack() 233 self.entry.wait_visibility() 234 self.entry.update_idletasks() 235 236 self.assertEqual(self.entry.identify(5, 5), "textarea") 237 self.assertEqual(self.entry.identify(-1, -1), "") 238 239 self.assertRaises(Tkinter.TclError, self.entry.identify, None, 5) 240 self.assertRaises(Tkinter.TclError, self.entry.identify, 5, None) 241 self.assertRaises(Tkinter.TclError, self.entry.identify, 5, '') 242 243 244 def test_validation_options(self): 245 success = [] 246 test_invalid = lambda: success.append(True) 247 248 self.entry['validate'] = 'none' 249 self.entry['validatecommand'] = lambda: False 250 251 self.entry['invalidcommand'] = test_invalid 252 self.entry.validate() 253 self.assertTrue(success) 254 255 self.entry['invalidcommand'] = '' 256 self.entry.validate() 257 self.assertEqual(len(success), 1) 258 259 self.entry['invalidcommand'] = test_invalid 260 self.entry['validatecommand'] = lambda: True 261 self.entry.validate() 262 self.assertEqual(len(success), 1) 263 264 self.entry['validatecommand'] = '' 265 self.entry.validate() 266 self.assertEqual(len(success), 1) 267 268 self.entry['validatecommand'] = True 269 self.assertRaises(Tkinter.TclError, self.entry.validate) 270 271 272 def test_validation(self): 273 validation = [] 274 def validate(to_insert): 275 if not 'a' <= to_insert.lower() <= 'z': 276 validation.append(False) 277 return False 278 validation.append(True) 279 return True 280 281 self.entry['validate'] = 'key' 282 self.entry['validatecommand'] = self.entry.register(validate), '%S' 283 284 self.entry.insert('end', 1) 285 self.entry.insert('end', 'a') 286 self.assertEqual(validation, [False, True]) 287 self.assertEqual(self.entry.get(), 'a') 288 289 290 def test_revalidation(self): 291 def validate(content): 292 for letter in content: 293 if not 'a' <= letter.lower() <= 'z': 294 return False 295 return True 296 297 self.entry['validatecommand'] = self.entry.register(validate), '%P' 298 299 self.entry.insert('end', 'avocado') 300 self.assertEqual(self.entry.validate(), True) 301 self.assertEqual(self.entry.state(), ()) 302 303 self.entry.delete(0, 'end') 304 self.assertEqual(self.entry.get(), '') 305 306 self.entry.insert('end', 'a1b') 307 self.assertEqual(self.entry.validate(), False) 308 self.assertEqual(self.entry.state(), ('invalid', )) 309 310 self.entry.delete(1) 311 self.assertEqual(self.entry.validate(), True) 312 self.assertEqual(self.entry.state(), ()) 313 314 315 class PanedwindowTest(unittest.TestCase): 316 317 def setUp(self): 318 support.root_deiconify() 319 self.paned = ttk.Panedwindow() 320 321 def tearDown(self): 322 self.paned.destroy() 323 support.root_withdraw() 324 325 326 def test_add(self): 327 # attempt to add a child that is not a direct child of the paned window 328 label = ttk.Label(self.paned) 329 child = ttk.Label(label) 330 self.assertRaises(Tkinter.TclError, self.paned.add, child) 331 label.destroy() 332 child.destroy() 333 # another attempt 334 label = ttk.Label() 335 child = ttk.Label(label) 336 self.assertRaises(Tkinter.TclError, self.paned.add, child) 337 child.destroy() 338 label.destroy() 339 340 good_child = ttk.Label() 341 self.paned.add(good_child) 342 # re-adding a child is not accepted 343 self.assertRaises(Tkinter.TclError, self.paned.add, good_child) 344 345 other_child = ttk.Label(self.paned) 346 self.paned.add(other_child) 347 self.assertEqual(self.paned.pane(0), self.paned.pane(1)) 348 self.assertRaises(Tkinter.TclError, self.paned.pane, 2) 349 good_child.destroy() 350 other_child.destroy() 351 self.assertRaises(Tkinter.TclError, self.paned.pane, 0) 352 353 354 def test_forget(self): 355 self.assertRaises(Tkinter.TclError, self.paned.forget, None) 356 self.assertRaises(Tkinter.TclError, self.paned.forget, 0) 357 358 self.paned.add(ttk.Label()) 359 self.paned.forget(0) 360 self.assertRaises(Tkinter.TclError, self.paned.forget, 0) 361 362 363 def test_insert(self): 364 self.assertRaises(Tkinter.TclError, self.paned.insert, None, 0) 365 self.assertRaises(Tkinter.TclError, self.paned.insert, 0, None) 366 self.assertRaises(Tkinter.TclError, self.paned.insert, 0, 0) 367 368 child = ttk.Label() 369 child2 = ttk.Label() 370 child3 = ttk.Label() 371 372 self.assertRaises(Tkinter.TclError, self.paned.insert, 0, child) 373 374 self.paned.insert('end', child2) 375 self.paned.insert(0, child) 376 self.assertEqual(self.paned.panes(), (str(child), str(child2))) 377 378 self.paned.insert(0, child2) 379 self.assertEqual(self.paned.panes(), (str(child2), str(child))) 380 381 self.paned.insert('end', child3) 382 self.assertEqual(self.paned.panes(), 383 (str(child2), str(child), str(child3))) 384 385 # reinserting a child should move it to its current position 386 panes = self.paned.panes() 387 self.paned.insert('end', child3) 388 self.assertEqual(panes, self.paned.panes()) 389 390 # moving child3 to child2 position should result in child2 ending up 391 # in previous child position and child ending up in previous child3 392 # position 393 self.paned.insert(child2, child3) 394 self.assertEqual(self.paned.panes(), 395 (str(child3), str(child2), str(child))) 396 397 398 def test_pane(self): 399 self.assertRaises(Tkinter.TclError, self.paned.pane, 0) 400 401 child = ttk.Label() 402 self.paned.add(child) 403 self.assertTrue(isinstance(self.paned.pane(0), dict)) 404 self.assertEqual(self.paned.pane(0, weight=None), 0) 405 # newer form for querying a single option 406 self.assertEqual(self.paned.pane(0, 'weight'), 0) 407 self.assertEqual(self.paned.pane(0), self.paned.pane(str(child))) 408 409 self.assertRaises(Tkinter.TclError, self.paned.pane, 0, 410 badoption='somevalue') 411 412 413 def test_sashpos(self): 414 self.assertRaises(Tkinter.TclError, self.paned.sashpos, None) 415 self.assertRaises(Tkinter.TclError, self.paned.sashpos, '') 416 self.assertRaises(Tkinter.TclError, self.paned.sashpos, 0) 417 418 child = ttk.Label(self.paned, text='a') 419 self.paned.add(child, weight=1) 420 self.assertRaises(Tkinter.TclError, self.paned.sashpos, 0) 421 child2 = ttk.Label(self.paned, text='b') 422 self.paned.add(child2) 423 self.assertRaises(Tkinter.TclError, self.paned.sashpos, 1) 424 425 self.paned.pack(expand=True, fill='both') 426 self.paned.wait_visibility() 427 428 curr_pos = self.paned.sashpos(0) 429 self.paned.sashpos(0, 1000) 430 self.assertTrue(curr_pos != self.paned.sashpos(0)) 431 self.assertTrue(isinstance(self.paned.sashpos(0), int)) 432 433 434 class RadiobuttonTest(unittest.TestCase): 435 436 def test_invoke(self): 437 success = [] 438 def cb_test(): 439 success.append(1) 440 return "cb test called" 441 442 myvar = Tkinter.IntVar() 443 cbtn = ttk.Radiobutton(command=cb_test, variable=myvar, value=0) 444 cbtn2 = ttk.Radiobutton(command=cb_test, variable=myvar, value=1) 445 446 res = cbtn.invoke() 447 self.assertEqual(res, "cb test called") 448 self.assertEqual(cbtn['value'], myvar.get()) 449 self.assertEqual(myvar.get(), 450 cbtn.tk.globalgetvar(cbtn['variable'])) 451 self.assertTrue(success) 452 453 cbtn2['command'] = '' 454 res = cbtn2.invoke() 455 self.assertEqual(res, '') 456 self.assertFalse(len(success) > 1) 457 self.assertEqual(cbtn2['value'], myvar.get()) 458 self.assertEqual(myvar.get(), 459 cbtn.tk.globalgetvar(cbtn['variable'])) 460 461 self.assertEqual(str(cbtn['variable']), str(cbtn2['variable'])) 462 463 464 465 class ScaleTest(unittest.TestCase): 466 467 def setUp(self): 468 support.root_deiconify() 469 self.scale = ttk.Scale() 470 self.scale.pack() 471 self.scale.update() 472 473 def tearDown(self): 474 self.scale.destroy() 475 support.root_withdraw() 476 477 478 def test_custom_event(self): 479 failure = [1, 1, 1] # will need to be empty 480 481 funcid = self.scale.bind('<<RangeChanged>>', lambda evt: failure.pop()) 482 483 self.scale['from'] = 10 484 self.scale['from_'] = 10 485 self.scale['to'] = 3 486 487 self.assertFalse(failure) 488 489 failure = [1, 1, 1] 490 self.scale.configure(from_=2, to=5) 491 self.scale.configure(from_=0, to=-2) 492 self.scale.configure(to=10) 493 494 self.assertFalse(failure) 495 496 497 def test_get(self): 498 scale_width = self.scale.winfo_width() 499 self.assertEqual(self.scale.get(scale_width, 0), self.scale['to']) 500 501 self.assertEqual(self.scale.get(0, 0), self.scale['from']) 502 self.assertEqual(self.scale.get(), self.scale['value']) 503 self.scale['value'] = 30 504 self.assertEqual(self.scale.get(), self.scale['value']) 505 506 self.assertRaises(Tkinter.TclError, self.scale.get, '', 0) 507 self.assertRaises(Tkinter.TclError, self.scale.get, 0, '') 508 509 510 def test_set(self): 511 # set restricts the max/min values according to the current range 512 max = self.scale['to'] 513 new_max = max + 10 514 self.scale.set(new_max) 515 self.assertEqual(self.scale.get(), max) 516 min = self.scale['from'] 517 self.scale.set(min - 1) 518 self.assertEqual(self.scale.get(), min) 519 520 # changing directly the variable doesn't impose this limitation tho 521 var = Tkinter.DoubleVar() 522 self.scale['variable'] = var 523 var.set(max + 5) 524 self.assertEqual(self.scale.get(), var.get()) 525 self.assertEqual(self.scale.get(), max + 5) 526 del var 527 528 # the same happens with the value option 529 self.scale['value'] = max + 10 530 self.assertEqual(self.scale.get(), max + 10) 531 self.assertEqual(self.scale.get(), self.scale['value']) 532 533 # nevertheless, note that the max/min values we can get specifying 534 # x, y coords are the ones according to the current range 535 self.assertEqual(self.scale.get(0, 0), min) 536 self.assertEqual(self.scale.get(self.scale.winfo_width(), 0), max) 537 538 self.assertRaises(Tkinter.TclError, self.scale.set, None) 539 540 541 class NotebookTest(unittest.TestCase): 542 543 def setUp(self): 544 support.root_deiconify() 545 self.nb = ttk.Notebook(padding=0) 546 self.child1 = ttk.Label() 547 self.child2 = ttk.Label() 548 self.nb.add(self.child1, text='a') 549 self.nb.add(self.child2, text='b') 550 551 def tearDown(self): 552 self.child1.destroy() 553 self.child2.destroy() 554 self.nb.destroy() 555 support.root_withdraw() 556 557 558 def test_tab_identifiers(self): 559 self.nb.forget(0) 560 self.nb.hide(self.child2) 561 self.assertRaises(Tkinter.TclError, self.nb.tab, self.child1) 562 self.assertEqual(self.nb.index('end'), 1) 563 self.nb.add(self.child2) 564 self.assertEqual(self.nb.index('end'), 1) 565 self.nb.select(self.child2) 566 567 self.assertTrue(self.nb.tab('current')) 568 self.nb.add(self.child1, text='a') 569 570 self.nb.pack() 571 self.nb.wait_visibility() 572 if sys.platform == 'darwin': 573 tb_idx = "@20,5" 574 else: 575 tb_idx = "@5,5" 576 self.assertEqual(self.nb.tab(tb_idx), self.nb.tab('current')) 577 578 for i in range(5, 100, 5): 579 try: 580 if self.nb.tab('@%d, 5' % i, text=None) == 'a': 581 break 582 except Tkinter.TclError: 583 pass 584 585 else: 586 self.fail("Tab with text 'a' not found") 587 588 589 def test_add_and_hidden(self): 590 self.assertRaises(Tkinter.TclError, self.nb.hide, -1) 591 self.assertRaises(Tkinter.TclError, self.nb.hide, 'hi') 592 self.assertRaises(Tkinter.TclError, self.nb.hide, None) 593 self.assertRaises(Tkinter.TclError, self.nb.add, None) 594 self.assertRaises(Tkinter.TclError, self.nb.add, ttk.Label(), 595 unknown='option') 596 597 tabs = self.nb.tabs() 598 self.nb.hide(self.child1) 599 self.nb.add(self.child1) 600 self.assertEqual(self.nb.tabs(), tabs) 601 602 child = ttk.Label() 603 self.nb.add(child, text='c') 604 tabs = self.nb.tabs() 605 606 curr = self.nb.index('current') 607 # verify that the tab gets readded at its previous position 608 child2_index = self.nb.index(self.child2) 609 self.nb.hide(self.child2) 610 self.nb.add(self.child2) 611 self.assertEqual(self.nb.tabs(), tabs) 612 self.assertEqual(self.nb.index(self.child2), child2_index) 613 self.assertTrue(str(self.child2) == self.nb.tabs()[child2_index]) 614 # but the tab next to it (not hidden) is the one selected now 615 self.assertEqual(self.nb.index('current'), curr + 1) 616 617 618 def test_forget(self): 619 self.assertRaises(Tkinter.TclError, self.nb.forget, -1) 620 self.assertRaises(Tkinter.TclError, self.nb.forget, 'hi') 621 self.assertRaises(Tkinter.TclError, self.nb.forget, None) 622 623 tabs = self.nb.tabs() 624 child1_index = self.nb.index(self.child1) 625 self.nb.forget(self.child1) 626 self.assertFalse(str(self.child1) in self.nb.tabs()) 627 self.assertEqual(len(tabs) - 1, len(self.nb.tabs())) 628 629 self.nb.add(self.child1) 630 self.assertEqual(self.nb.index(self.child1), 1) 631 self.assertFalse(child1_index == self.nb.index(self.child1)) 632 633 634 def test_index(self): 635 self.assertRaises(Tkinter.TclError, self.nb.index, -1) 636 self.assertRaises(Tkinter.TclError, self.nb.index, None) 637 638 self.assertTrue(isinstance(self.nb.index('end'), int)) 639 self.assertEqual(self.nb.index(self.child1), 0) 640 self.assertEqual(self.nb.index(self.child2), 1) 641 self.assertEqual(self.nb.index('end'), 2) 642 643 644 def test_insert(self): 645 # moving tabs 646 tabs = self.nb.tabs() 647 self.nb.insert(1, tabs[0]) 648 self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0])) 649 self.nb.insert(self.child1, self.child2) 650 self.assertEqual(self.nb.tabs(), tabs) 651 self.nb.insert('end', self.child1) 652 self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0])) 653 self.nb.insert('end', 0) 654 self.assertEqual(self.nb.tabs(), tabs) 655 # bad moves 656 self.assertRaises(Tkinter.TclError, self.nb.insert, 2, tabs[0]) 657 self.assertRaises(Tkinter.TclError, self.nb.insert, -1, tabs[0]) 658 659 # new tab 660 child3 = ttk.Label() 661 self.nb.insert(1, child3) 662 self.assertEqual(self.nb.tabs(), (tabs[0], str(child3), tabs[1])) 663 self.nb.forget(child3) 664 self.assertEqual(self.nb.tabs(), tabs) 665 self.nb.insert(self.child1, child3) 666 self.assertEqual(self.nb.tabs(), (str(child3), ) + tabs) 667 self.nb.forget(child3) 668 self.assertRaises(Tkinter.TclError, self.nb.insert, 2, child3) 669 self.assertRaises(Tkinter.TclError, self.nb.insert, -1, child3) 670 671 # bad inserts 672 self.assertRaises(Tkinter.TclError, self.nb.insert, 'end', None) 673 self.assertRaises(Tkinter.TclError, self.nb.insert, None, 0) 674 self.assertRaises(Tkinter.TclError, self.nb.insert, None, None) 675 676 677 def test_select(self): 678 self.nb.pack() 679 self.nb.wait_visibility() 680 681 success = [] 682 tab_changed = [] 683 684 self.child1.bind('<Unmap>', lambda evt: success.append(True)) 685 self.nb.bind('<<NotebookTabChanged>>', 686 lambda evt: tab_changed.append(True)) 687 688 self.assertEqual(self.nb.select(), str(self.child1)) 689 self.nb.select(self.child2) 690 self.assertTrue(success) 691 self.assertEqual(self.nb.select(), str(self.child2)) 692 693 self.nb.update() 694 self.assertTrue(tab_changed) 695 696 697 def test_tab(self): 698 self.assertRaises(Tkinter.TclError, self.nb.tab, -1) 699 self.assertRaises(Tkinter.TclError, self.nb.tab, 'notab') 700 self.assertRaises(Tkinter.TclError, self.nb.tab, None) 701 702 self.assertTrue(isinstance(self.nb.tab(self.child1), dict)) 703 self.assertEqual(self.nb.tab(self.child1, text=None), 'a') 704 # newer form for querying a single option 705 self.assertEqual(self.nb.tab(self.child1, 'text'), 'a') 706 self.nb.tab(self.child1, text='abc') 707 self.assertEqual(self.nb.tab(self.child1, text=None), 'abc') 708 self.assertEqual(self.nb.tab(self.child1, 'text'), 'abc') 709 710 711 def test_tabs(self): 712 self.assertEqual(len(self.nb.tabs()), 2) 713 714 self.nb.forget(self.child1) 715 self.nb.forget(self.child2) 716 717 self.assertEqual(self.nb.tabs(), ()) 718 719 720 def test_traversal(self): 721 self.nb.pack() 722 self.nb.wait_visibility() 723 724 self.nb.select(0) 725 726 support.simulate_mouse_click(self.nb, 5, 5) 727 self.nb.focus_force() 728 self.nb.event_generate('<Control-Tab>') 729 self.assertEqual(self.nb.select(), str(self.child2)) 730 self.nb.focus_force() 731 self.nb.event_generate('<Shift-Control-Tab>') 732 self.assertEqual(self.nb.select(), str(self.child1)) 733 self.nb.focus_force() 734 self.nb.event_generate('<Shift-Control-Tab>') 735 self.assertEqual(self.nb.select(), str(self.child2)) 736 737 self.nb.tab(self.child1, text='a', underline=0) 738 self.nb.enable_traversal() 739 self.nb.focus_force() 740 support.simulate_mouse_click(self.nb, 5, 5) 741 if sys.platform == 'darwin': 742 self.nb.event_generate('<Option-a>') 743 else: 744 self.nb.event_generate('<Alt-a>') 745 self.assertEqual(self.nb.select(), str(self.child1)) 746 747 748 class TreeviewTest(unittest.TestCase): 749 750 def setUp(self): 751 support.root_deiconify() 752 self.tv = ttk.Treeview(padding=0) 753 754 def tearDown(self): 755 self.tv.destroy() 756 support.root_withdraw() 757 758 759 def test_bbox(self): 760 self.tv.pack() 761 self.assertEqual(self.tv.bbox(''), '') 762 self.tv.wait_visibility() 763 self.tv.update() 764 765 item_id = self.tv.insert('', 'end') 766 children = self.tv.get_children() 767 self.assertTrue(children) 768 769 bbox = self.tv.bbox(children[0]) 770 self.assertEqual(len(bbox), 4) 771 self.assertTrue(isinstance(bbox, tuple)) 772 for item in bbox: 773 if not isinstance(item, int): 774 self.fail("Invalid bounding box: %s" % bbox) 775 break 776 777 # compare width in bboxes 778 self.tv['columns'] = ['test'] 779 self.tv.column('test', width=50) 780 bbox_column0 = self.tv.bbox(children[0], 0) 781 root_width = self.tv.column('#0', width=None) 782 self.assertEqual(bbox_column0[0], bbox[0] + root_width) 783 784 # verify that bbox of a closed item is the empty string 785 child1 = self.tv.insert(item_id, 'end') 786 self.assertEqual(self.tv.bbox(child1), '') 787 788 789 def test_children(self): 790 # no children yet, should get an empty tuple 791 self.assertEqual(self.tv.get_children(), ()) 792 793 item_id = self.tv.insert('', 'end') 794 self.assertTrue(isinstance(self.tv.get_children(), tuple)) 795 self.assertEqual(self.tv.get_children()[0], item_id) 796 797 # add item_id and child3 as children of child2 798 child2 = self.tv.insert('', 'end') 799 child3 = self.tv.insert('', 'end') 800 self.tv.set_children(child2, item_id, child3) 801 self.assertEqual(self.tv.get_children(child2), (item_id, child3)) 802 803 # child3 has child2 as parent, thus trying to set child2 as a children 804 # of child3 should result in an error 805 self.assertRaises(Tkinter.TclError, 806 self.tv.set_children, child3, child2) 807 808 # remove child2 children 809 self.tv.set_children(child2) 810 self.assertEqual(self.tv.get_children(child2), ()) 811 812 # remove root's children 813 self.tv.set_children('') 814 self.assertEqual(self.tv.get_children(), ()) 815 816 817 def test_column(self): 818 # return a dict with all options/values 819 self.assertTrue(isinstance(self.tv.column('#0'), dict)) 820 # return a single value of the given option 821 self.assertTrue(isinstance(self.tv.column('#0', width=None), int)) 822 # set a new value for an option 823 self.tv.column('#0', width=10) 824 # testing new way to get option value 825 self.assertEqual(self.tv.column('#0', 'width'), 10) 826 self.assertEqual(self.tv.column('#0', width=None), 10) 827 # check read-only option 828 self.assertRaises(Tkinter.TclError, self.tv.column, '#0', id='X') 829 830 self.assertRaises(Tkinter.TclError, self.tv.column, 'invalid') 831 invalid_kws = [ 832 {'unknown_option': 'some value'}, {'stretch': 'wrong'}, 833 {'anchor': 'wrong'}, {'width': 'wrong'}, {'minwidth': 'wrong'} 834 ] 835 for kw in invalid_kws: 836 self.assertRaises(Tkinter.TclError, self.tv.column, '#0', 837 **kw) 838 839 840 def test_delete(self): 841 self.assertRaises(Tkinter.TclError, self.tv.delete, '#0') 842 843 item_id = self.tv.insert('', 'end') 844 item2 = self.tv.insert(item_id, 'end') 845 self.assertEqual(self.tv.get_children(), (item_id, )) 846 self.assertEqual(self.tv.get_children(item_id), (item2, )) 847 848 self.tv.delete(item_id) 849 self.assertFalse(self.tv.get_children()) 850 851 # reattach should fail 852 self.assertRaises(Tkinter.TclError, 853 self.tv.reattach, item_id, '', 'end') 854 855 # test multiple item delete 856 item1 = self.tv.insert('', 'end') 857 item2 = self.tv.insert('', 'end') 858 self.assertEqual(self.tv.get_children(), (item1, item2)) 859 860 self.tv.delete(item1, item2) 861 self.assertFalse(self.tv.get_children()) 862 863 864 def test_detach_reattach(self): 865 item_id = self.tv.insert('', 'end') 866 item2 = self.tv.insert(item_id, 'end') 867 868 # calling detach without items is valid, although it does nothing 869 prev = self.tv.get_children() 870 self.tv.detach() # this should do nothing 871 self.assertEqual(prev, self.tv.get_children()) 872 873 self.assertEqual(self.tv.get_children(), (item_id, )) 874 self.assertEqual(self.tv.get_children(item_id), (item2, )) 875 876 # detach item with children 877 self.tv.detach(item_id) 878 self.assertFalse(self.tv.get_children()) 879 880 # reattach item with children 881 self.tv.reattach(item_id, '', 'end') 882 self.assertEqual(self.tv.get_children(), (item_id, )) 883 self.assertEqual(self.tv.get_children(item_id), (item2, )) 884 885 # move a children to the root 886 self.tv.move(item2, '', 'end') 887 self.assertEqual(self.tv.get_children(), (item_id, item2)) 888 self.assertEqual(self.tv.get_children(item_id), ()) 889 890 # bad values 891 self.assertRaises(Tkinter.TclError, 892 self.tv.reattach, 'nonexistent', '', 'end') 893 self.assertRaises(Tkinter.TclError, 894 self.tv.detach, 'nonexistent') 895 self.assertRaises(Tkinter.TclError, 896 self.tv.reattach, item2, 'otherparent', 'end') 897 self.assertRaises(Tkinter.TclError, 898 self.tv.reattach, item2, '', 'invalid') 899 900 # multiple detach 901 self.tv.detach(item_id, item2) 902 self.assertEqual(self.tv.get_children(), ()) 903 self.assertEqual(self.tv.get_children(item_id), ()) 904 905 906 def test_exists(self): 907 self.assertEqual(self.tv.exists('something'), False) 908 self.assertEqual(self.tv.exists(''), True) 909 self.assertEqual(self.tv.exists({}), False) 910 911 # the following will make a tk.call equivalent to 912 # tk.call(treeview, "exists") which should result in an error 913 # in the tcl interpreter since tk requires an item. 914 self.assertRaises(Tkinter.TclError, self.tv.exists, None) 915 916 917 def test_focus(self): 918 # nothing is focused right now 919 self.assertEqual(self.tv.focus(), '') 920 921 item1 = self.tv.insert('', 'end') 922 self.tv.focus(item1) 923 self.assertEqual(self.tv.focus(), item1) 924 925 self.tv.delete(item1) 926 self.assertEqual(self.tv.focus(), '') 927 928 # try focusing inexistent item 929 self.assertRaises(Tkinter.TclError, self.tv.focus, 'hi') 930 931 932 def test_heading(self): 933 # check a dict is returned 934 self.assertTrue(isinstance(self.tv.heading('#0'), dict)) 935 936 # check a value is returned 937 self.tv.heading('#0', text='hi') 938 self.assertEqual(self.tv.heading('#0', 'text'), 'hi') 939 self.assertEqual(self.tv.heading('#0', text=None), 'hi') 940 941 # invalid option 942 self.assertRaises(Tkinter.TclError, self.tv.heading, '#0', 943 background=None) 944 # invalid value 945 self.assertRaises(Tkinter.TclError, self.tv.heading, '#0', 946 anchor=1) 947 948 # XXX skipping for now; should be fixed to work with newer ttk 949 @unittest.skip("skipping pending resolution of Issue #10734") 950 def test_heading_callback(self): 951 def simulate_heading_click(x, y): 952 support.simulate_mouse_click(self.tv, x, y) 953 self.tv.update_idletasks() 954 955 success = [] # no success for now 956 957 self.tv.pack() 958 self.tv.wait_visibility() 959 self.tv.heading('#0', command=lambda: success.append(True)) 960 self.tv.column('#0', width=100) 961 self.tv.update() 962 963 # assuming that the coords (5, 5) fall into heading #0 964 simulate_heading_click(5, 5) 965 if not success: 966 self.fail("The command associated to the treeview heading wasn't " 967 "invoked.") 968 969 success = [] 970 commands = self.tv.master._tclCommands 971 self.tv.heading('#0', command=str(self.tv.heading('#0', command=None))) 972 self.assertEqual(commands, self.tv.master._tclCommands) 973 simulate_heading_click(5, 5) 974 if not success: 975 self.fail("The command associated to the treeview heading wasn't " 976 "invoked.") 977 978 # XXX The following raises an error in a tcl interpreter, but not in 979 # Python 980 #self.tv.heading('#0', command='I dont exist') 981 #simulate_heading_click(5, 5) 982 983 984 def test_index(self): 985 # item 'what' doesn't exist 986 self.assertRaises(Tkinter.TclError, self.tv.index, 'what') 987 988 self.assertEqual(self.tv.index(''), 0) 989 990 item1 = self.tv.insert('', 'end') 991 item2 = self.tv.insert('', 'end') 992 c1 = self.tv.insert(item1, 'end') 993 c2 = self.tv.insert(item1, 'end') 994 self.assertEqual(self.tv.index(item1), 0) 995 self.assertEqual(self.tv.index(c1), 0) 996 self.assertEqual(self.tv.index(c2), 1) 997 self.assertEqual(self.tv.index(item2), 1) 998 999 self.tv.move(item2, '', 0) 1000 self.assertEqual(self.tv.index(item2), 0) 1001 self.assertEqual(self.tv.index(item1), 1) 1002 1003 # check that index still works even after its parent and siblings 1004 # have been detached 1005 self.tv.detach(item1) 1006 self.assertEqual(self.tv.index(c2), 1) 1007 self.tv.detach(c1) 1008 self.assertEqual(self.tv.index(c2), 0) 1009 1010 # but it fails after item has been deleted 1011 self.tv.delete(item1) 1012 self.assertRaises(Tkinter.TclError, self.tv.index, c2) 1013 1014 1015 def test_insert_item(self): 1016 # parent 'none' doesn't exist 1017 self.assertRaises(Tkinter.TclError, self.tv.insert, 'none', 'end') 1018 1019 # open values 1020 self.assertRaises(Tkinter.TclError, self.tv.insert, '', 'end', 1021 open='') 1022 self.assertRaises(Tkinter.TclError, self.tv.insert, '', 'end', 1023 open='please') 1024 self.assertFalse(self.tv.delete(self.tv.insert('', 'end', open=True))) 1025 self.assertFalse(self.tv.delete(self.tv.insert('', 'end', open=False))) 1026 1027 # invalid index 1028 self.assertRaises(Tkinter.TclError, self.tv.insert, '', 'middle') 1029 1030 # trying to duplicate item id is invalid 1031 itemid = self.tv.insert('', 'end', 'first-item') 1032 self.assertEqual(itemid, 'first-item') 1033 self.assertRaises(Tkinter.TclError, self.tv.insert, '', 'end', 1034 'first-item') 1035 self.assertRaises(Tkinter.TclError, self.tv.insert, '', 'end', 1036 MockTclObj('first-item')) 1037 1038 # unicode values 1039 value = u'\xe1ba' 1040 item = self.tv.insert('', 'end', values=(value, )) 1041 self.assertEqual(self.tv.item(item, 'values'), (value, )) 1042 self.assertEqual(self.tv.item(item, values=None), (value, )) 1043 1044 self.tv.item(item, values=list(self.tv.item(item, values=None))) 1045 self.assertEqual(self.tv.item(item, values=None), (value, )) 1046 1047 self.assertTrue(isinstance(self.tv.item(item), dict)) 1048 1049 # erase item values 1050 self.tv.item(item, values='') 1051 self.assertFalse(self.tv.item(item, values=None)) 1052 1053 # item tags 1054 item = self.tv.insert('', 'end', tags=[1, 2, value]) 1055 self.assertEqual(self.tv.item(item, tags=None), ('1', '2', value)) 1056 self.tv.item(item, tags=[]) 1057 self.assertFalse(self.tv.item(item, tags=None)) 1058 self.tv.item(item, tags=(1, 2)) 1059 self.assertEqual(self.tv.item(item, tags=None), ('1', '2')) 1060 1061 # values with spaces 1062 item = self.tv.insert('', 'end', values=('a b c', 1063 '%s %s' % (value, value))) 1064 self.assertEqual(self.tv.item(item, values=None), 1065 ('a b c', '%s %s' % (value, value))) 1066 1067 # text 1068 self.assertEqual(self.tv.item( 1069 self.tv.insert('', 'end', text="Label here"), text=None), 1070 "Label here") 1071 self.assertEqual(self.tv.item( 1072 self.tv.insert('', 'end', text=value), text=None), 1073 value) 1074 1075 1076 def test_set(self): 1077 self.tv['columns'] = ['A', 'B'] 1078 item = self.tv.insert('', 'end', values=['a', 'b']) 1079 self.assertEqual(self.tv.set(item), {'A': 'a', 'B': 'b'}) 1080 1081 self.tv.set(item, 'B', 'a') 1082 self.assertEqual(self.tv.item(item, values=None), ('a', 'a')) 1083 1084 self.tv['columns'] = ['B'] 1085 self.assertEqual(self.tv.set(item), {'B': 'a'}) 1086 1087 self.tv.set(item, 'B', 'b') 1088 self.assertEqual(self.tv.set(item, column='B'), 'b') 1089 self.assertEqual(self.tv.item(item, values=None), ('b', 'a')) 1090 1091 self.tv.set(item, 'B', 123) 1092 self.assertEqual(self.tv.set(item, 'B'), 123) 1093 self.assertEqual(self.tv.item(item, values=None), (123, 'a')) 1094 self.assertEqual(self.tv.set(item), {'B': 123}) 1095 1096 # inexistent column 1097 self.assertRaises(Tkinter.TclError, self.tv.set, item, 'A') 1098 self.assertRaises(Tkinter.TclError, self.tv.set, item, 'A', 'b') 1099 1100 # inexistent item 1101 self.assertRaises(Tkinter.TclError, self.tv.set, 'notme') 1102 1103 1104 def test_tag_bind(self): 1105 events = [] 1106 item1 = self.tv.insert('', 'end', tags=['call']) 1107 item2 = self.tv.insert('', 'end', tags=['call']) 1108 self.tv.tag_bind('call', '<ButtonPress-1>', 1109 lambda evt: events.append(1)) 1110 self.tv.tag_bind('call', '<ButtonRelease-1>', 1111 lambda evt: events.append(2)) 1112 1113 self.tv.pack() 1114 self.tv.wait_visibility() 1115 self.tv.update() 1116 1117 pos_y = set() 1118 found = set() 1119 for i in range(0, 100, 10): 1120 if len(found) == 2: # item1 and item2 already found 1121 break 1122 item_id = self.tv.identify_row(i) 1123 if item_id and item_id not in found: 1124 pos_y.add(i) 1125 found.add(item_id) 1126 1127 self.assertEqual(len(pos_y), 2) # item1 and item2 y pos 1128 for y in pos_y: 1129 support.simulate_mouse_click(self.tv, 0, y) 1130 1131 # by now there should be 4 things in the events list, since each 1132 # item had a bind for two events that were simulated above 1133 self.assertEqual(len(events), 4) 1134 for evt in zip(events[::2], events[1::2]): 1135 self.assertEqual(evt, (1, 2)) 1136 1137 1138 def test_tag_configure(self): 1139 # Just testing parameter passing for now 1140 self.assertRaises(TypeError, self.tv.tag_configure) 1141 self.assertRaises(Tkinter.TclError, self.tv.tag_configure, 1142 'test', sky='blue') 1143 self.tv.tag_configure('test', foreground='blue') 1144 self.assertEqual(str(self.tv.tag_configure('test', 'foreground')), 1145 'blue') 1146 self.assertEqual(str(self.tv.tag_configure('test', foreground=None)), 1147 'blue') 1148 self.assertTrue(isinstance(self.tv.tag_configure('test'), dict)) 1149 1150 1151 tests_gui = ( 1152 WidgetTest, ButtonTest, CheckbuttonTest, RadiobuttonTest, 1153 ComboboxTest, EntryTest, PanedwindowTest, ScaleTest, NotebookTest, 1154 TreeviewTest 1155 ) 1156 1157 if __name__ == "__main__": 1158 run_unittest(*tests_gui) 1159