1 import unittest 2 import tkinter 3 from tkinter import ttk, TclError 4 from test.support import requires 5 import sys 6 7 from tkinter.test.test_ttk.test_functions import MockTclObj 8 from tkinter.test.support import (AbstractTkTest, tcl_version, get_tk_patchlevel, 9 simulate_mouse_click) 10 from tkinter.test.widget_tests import (add_standard_options, noconv, 11 AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests, 12 setUpModule) 13 14 requires('gui') 15 16 17 class StandardTtkOptionsTests(StandardOptionsTests): 18 19 def test_class(self): 20 widget = self.create() 21 self.assertEqual(widget['class'], '') 22 errmsg='attempt to change read-only option' 23 if get_tk_patchlevel() < (8, 6, 0, 'beta', 3): 24 errmsg='Attempt to change read-only option' 25 self.checkInvalidParam(widget, 'class', 'Foo', errmsg=errmsg) 26 widget2 = self.create(class_='Foo') 27 self.assertEqual(widget2['class'], 'Foo') 28 29 def test_padding(self): 30 widget = self.create() 31 self.checkParam(widget, 'padding', 0, expected=('0',)) 32 self.checkParam(widget, 'padding', 5, expected=('5',)) 33 self.checkParam(widget, 'padding', (5, 6), expected=('5', '6')) 34 self.checkParam(widget, 'padding', (5, 6, 7), 35 expected=('5', '6', '7')) 36 self.checkParam(widget, 'padding', (5, 6, 7, 8), 37 expected=('5', '6', '7', '8')) 38 self.checkParam(widget, 'padding', ('5p', '6p', '7p', '8p')) 39 self.checkParam(widget, 'padding', (), expected='') 40 41 def test_style(self): 42 widget = self.create() 43 self.assertEqual(widget['style'], '') 44 errmsg = 'Layout Foo not found' 45 if hasattr(self, 'default_orient'): 46 errmsg = ('Layout %s.Foo not found' % 47 getattr(self, 'default_orient').title()) 48 self.checkInvalidParam(widget, 'style', 'Foo', 49 errmsg=errmsg) 50 widget2 = self.create(class_='Foo') 51 self.assertEqual(widget2['class'], 'Foo') 52 # XXX 53 pass 54 55 56 class WidgetTest(AbstractTkTest, unittest.TestCase): 57 """Tests methods available in every ttk widget.""" 58 59 def setUp(self): 60 super().setUp() 61 self.widget = ttk.Button(self.root, width=0, text="Text") 62 self.widget.pack() 63 self.widget.wait_visibility() 64 65 66 def test_identify(self): 67 self.widget.update_idletasks() 68 self.assertEqual(self.widget.identify( 69 int(self.widget.winfo_width() / 2), 70 int(self.widget.winfo_height() / 2) 71 ), "label") 72 self.assertEqual(self.widget.identify(-1, -1), "") 73 74 self.assertRaises(tkinter.TclError, self.widget.identify, None, 5) 75 self.assertRaises(tkinter.TclError, self.widget.identify, 5, None) 76 self.assertRaises(tkinter.TclError, self.widget.identify, 5, '') 77 78 79 def test_widget_state(self): 80 # XXX not sure about the portability of all these tests 81 self.assertEqual(self.widget.state(), ()) 82 self.assertEqual(self.widget.instate(['!disabled']), True) 83 84 # changing from !disabled to disabled 85 self.assertEqual(self.widget.state(['disabled']), ('!disabled', )) 86 # no state change 87 self.assertEqual(self.widget.state(['disabled']), ()) 88 # change back to !disable but also active 89 self.assertEqual(self.widget.state(['!disabled', 'active']), 90 ('!active', 'disabled')) 91 # no state changes, again 92 self.assertEqual(self.widget.state(['!disabled', 'active']), ()) 93 self.assertEqual(self.widget.state(['active', '!disabled']), ()) 94 95 def test_cb(arg1, **kw): 96 return arg1, kw 97 self.assertEqual(self.widget.instate(['!disabled'], 98 test_cb, "hi", **{"msg": "there"}), 99 ('hi', {'msg': 'there'})) 100 101 # attempt to set invalid statespec 102 currstate = self.widget.state() 103 self.assertRaises(tkinter.TclError, self.widget.instate, 104 ['badstate']) 105 self.assertRaises(tkinter.TclError, self.widget.instate, 106 ['disabled', 'badstate']) 107 # verify that widget didn't change its state 108 self.assertEqual(currstate, self.widget.state()) 109 110 # ensuring that passing None as state doesn't modify current state 111 self.widget.state(['active', '!disabled']) 112 self.assertEqual(self.widget.state(), ('active', )) 113 114 115 class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): 116 _conv_pixels = noconv 117 118 119 @add_standard_options(StandardTtkOptionsTests) 120 class FrameTest(AbstractToplevelTest, unittest.TestCase): 121 OPTIONS = ( 122 'borderwidth', 'class', 'cursor', 'height', 123 'padding', 'relief', 'style', 'takefocus', 124 'width', 125 ) 126 127 def create(self, **kwargs): 128 return ttk.Frame(self.root, **kwargs) 129 130 131 @add_standard_options(StandardTtkOptionsTests) 132 class LabelFrameTest(AbstractToplevelTest, unittest.TestCase): 133 OPTIONS = ( 134 'borderwidth', 'class', 'cursor', 'height', 135 'labelanchor', 'labelwidget', 136 'padding', 'relief', 'style', 'takefocus', 137 'text', 'underline', 'width', 138 ) 139 140 def create(self, **kwargs): 141 return ttk.LabelFrame(self.root, **kwargs) 142 143 def test_labelanchor(self): 144 widget = self.create() 145 self.checkEnumParam(widget, 'labelanchor', 146 'e', 'en', 'es', 'n', 'ne', 'nw', 's', 'se', 'sw', 'w', 'wn', 'ws', 147 errmsg='Bad label anchor specification {}') 148 self.checkInvalidParam(widget, 'labelanchor', 'center') 149 150 def test_labelwidget(self): 151 widget = self.create() 152 label = ttk.Label(self.root, text='Mupp', name='foo') 153 self.checkParam(widget, 'labelwidget', label, expected='.foo') 154 label.destroy() 155 156 157 class AbstractLabelTest(AbstractWidgetTest): 158 159 def checkImageParam(self, widget, name): 160 image = tkinter.PhotoImage(master=self.root, name='image1') 161 image2 = tkinter.PhotoImage(master=self.root, name='image2') 162 self.checkParam(widget, name, image, expected=('image1',)) 163 self.checkParam(widget, name, 'image1', expected=('image1',)) 164 self.checkParam(widget, name, (image,), expected=('image1',)) 165 self.checkParam(widget, name, (image, 'active', image2), 166 expected=('image1', 'active', 'image2')) 167 self.checkParam(widget, name, 'image1 active image2', 168 expected=('image1', 'active', 'image2')) 169 self.checkInvalidParam(widget, name, 'spam', 170 errmsg='image "spam" doesn\'t exist') 171 172 def test_compound(self): 173 widget = self.create() 174 self.checkEnumParam(widget, 'compound', 175 'none', 'text', 'image', 'center', 176 'top', 'bottom', 'left', 'right') 177 178 def test_state(self): 179 widget = self.create() 180 self.checkParams(widget, 'state', 'active', 'disabled', 'normal') 181 182 def test_width(self): 183 widget = self.create() 184 self.checkParams(widget, 'width', 402, -402, 0) 185 186 187 @add_standard_options(StandardTtkOptionsTests) 188 class LabelTest(AbstractLabelTest, unittest.TestCase): 189 OPTIONS = ( 190 'anchor', 'background', 'borderwidth', 191 'class', 'compound', 'cursor', 'font', 'foreground', 192 'image', 'justify', 'padding', 'relief', 'state', 'style', 193 'takefocus', 'text', 'textvariable', 194 'underline', 'width', 'wraplength', 195 ) 196 _conv_pixels = noconv 197 198 def create(self, **kwargs): 199 return ttk.Label(self.root, **kwargs) 200 201 def test_font(self): 202 widget = self.create() 203 self.checkParam(widget, 'font', 204 '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') 205 206 207 @add_standard_options(StandardTtkOptionsTests) 208 class ButtonTest(AbstractLabelTest, unittest.TestCase): 209 OPTIONS = ( 210 'class', 'command', 'compound', 'cursor', 'default', 211 'image', 'padding', 'state', 'style', 212 'takefocus', 'text', 'textvariable', 213 'underline', 'width', 214 ) 215 216 def create(self, **kwargs): 217 return ttk.Button(self.root, **kwargs) 218 219 def test_default(self): 220 widget = self.create() 221 self.checkEnumParam(widget, 'default', 'normal', 'active', 'disabled') 222 223 def test_invoke(self): 224 success = [] 225 btn = ttk.Button(self.root, command=lambda: success.append(1)) 226 btn.invoke() 227 self.assertTrue(success) 228 229 230 @add_standard_options(StandardTtkOptionsTests) 231 class CheckbuttonTest(AbstractLabelTest, unittest.TestCase): 232 OPTIONS = ( 233 'class', 'command', 'compound', 'cursor', 234 'image', 235 'offvalue', 'onvalue', 236 'padding', 'state', 'style', 237 'takefocus', 'text', 'textvariable', 238 'underline', 'variable', 'width', 239 ) 240 241 def create(self, **kwargs): 242 return ttk.Checkbutton(self.root, **kwargs) 243 244 def test_offvalue(self): 245 widget = self.create() 246 self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string') 247 248 def test_onvalue(self): 249 widget = self.create() 250 self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string') 251 252 def test_invoke(self): 253 success = [] 254 def cb_test(): 255 success.append(1) 256 return "cb test called" 257 258 cbtn = ttk.Checkbutton(self.root, command=cb_test) 259 # the variable automatically created by ttk.Checkbutton is actually 260 # undefined till we invoke the Checkbutton 261 self.assertEqual(cbtn.state(), ('alternate', )) 262 self.assertRaises(tkinter.TclError, cbtn.tk.globalgetvar, 263 cbtn['variable']) 264 265 res = cbtn.invoke() 266 self.assertEqual(res, "cb test called") 267 self.assertEqual(cbtn['onvalue'], 268 cbtn.tk.globalgetvar(cbtn['variable'])) 269 self.assertTrue(success) 270 271 cbtn['command'] = '' 272 res = cbtn.invoke() 273 self.assertFalse(str(res)) 274 self.assertLessEqual(len(success), 1) 275 self.assertEqual(cbtn['offvalue'], 276 cbtn.tk.globalgetvar(cbtn['variable'])) 277 278 279 @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) 280 class EntryTest(AbstractWidgetTest, unittest.TestCase): 281 OPTIONS = ( 282 'background', 'class', 'cursor', 283 'exportselection', 'font', 'foreground', 284 'invalidcommand', 'justify', 285 'show', 'state', 'style', 'takefocus', 'textvariable', 286 'validate', 'validatecommand', 'width', 'xscrollcommand', 287 ) 288 289 def setUp(self): 290 super().setUp() 291 self.entry = self.create() 292 293 def create(self, **kwargs): 294 return ttk.Entry(self.root, **kwargs) 295 296 def test_invalidcommand(self): 297 widget = self.create() 298 self.checkCommandParam(widget, 'invalidcommand') 299 300 def test_show(self): 301 widget = self.create() 302 self.checkParam(widget, 'show', '*') 303 self.checkParam(widget, 'show', '') 304 self.checkParam(widget, 'show', ' ') 305 306 def test_state(self): 307 widget = self.create() 308 self.checkParams(widget, 'state', 309 'disabled', 'normal', 'readonly') 310 311 def test_validate(self): 312 widget = self.create() 313 self.checkEnumParam(widget, 'validate', 314 'all', 'key', 'focus', 'focusin', 'focusout', 'none') 315 316 def test_validatecommand(self): 317 widget = self.create() 318 self.checkCommandParam(widget, 'validatecommand') 319 320 321 def test_bbox(self): 322 self.assertIsBoundingBox(self.entry.bbox(0)) 323 self.assertRaises(tkinter.TclError, self.entry.bbox, 'noindex') 324 self.assertRaises(tkinter.TclError, self.entry.bbox, None) 325 326 327 def test_identify(self): 328 self.entry.pack() 329 self.entry.wait_visibility() 330 self.entry.update_idletasks() 331 332 self.assertEqual(self.entry.identify(5, 5), "textarea") 333 self.assertEqual(self.entry.identify(-1, -1), "") 334 335 self.assertRaises(tkinter.TclError, self.entry.identify, None, 5) 336 self.assertRaises(tkinter.TclError, self.entry.identify, 5, None) 337 self.assertRaises(tkinter.TclError, self.entry.identify, 5, '') 338 339 340 def test_validation_options(self): 341 success = [] 342 test_invalid = lambda: success.append(True) 343 344 self.entry['validate'] = 'none' 345 self.entry['validatecommand'] = lambda: False 346 347 self.entry['invalidcommand'] = test_invalid 348 self.entry.validate() 349 self.assertTrue(success) 350 351 self.entry['invalidcommand'] = '' 352 self.entry.validate() 353 self.assertEqual(len(success), 1) 354 355 self.entry['invalidcommand'] = test_invalid 356 self.entry['validatecommand'] = lambda: True 357 self.entry.validate() 358 self.assertEqual(len(success), 1) 359 360 self.entry['validatecommand'] = '' 361 self.entry.validate() 362 self.assertEqual(len(success), 1) 363 364 self.entry['validatecommand'] = True 365 self.assertRaises(tkinter.TclError, self.entry.validate) 366 367 368 def test_validation(self): 369 validation = [] 370 def validate(to_insert): 371 if not 'a' <= to_insert.lower() <= 'z': 372 validation.append(False) 373 return False 374 validation.append(True) 375 return True 376 377 self.entry['validate'] = 'key' 378 self.entry['validatecommand'] = self.entry.register(validate), '%S' 379 380 self.entry.insert('end', 1) 381 self.entry.insert('end', 'a') 382 self.assertEqual(validation, [False, True]) 383 self.assertEqual(self.entry.get(), 'a') 384 385 386 def test_revalidation(self): 387 def validate(content): 388 for letter in content: 389 if not 'a' <= letter.lower() <= 'z': 390 return False 391 return True 392 393 self.entry['validatecommand'] = self.entry.register(validate), '%P' 394 395 self.entry.insert('end', 'avocado') 396 self.assertEqual(self.entry.validate(), True) 397 self.assertEqual(self.entry.state(), ()) 398 399 self.entry.delete(0, 'end') 400 self.assertEqual(self.entry.get(), '') 401 402 self.entry.insert('end', 'a1b') 403 self.assertEqual(self.entry.validate(), False) 404 self.assertEqual(self.entry.state(), ('invalid', )) 405 406 self.entry.delete(1) 407 self.assertEqual(self.entry.validate(), True) 408 self.assertEqual(self.entry.state(), ()) 409 410 411 @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) 412 class ComboboxTest(EntryTest, unittest.TestCase): 413 OPTIONS = ( 414 'background', 'class', 'cursor', 'exportselection', 415 'font', 'foreground', 'height', 'invalidcommand', 416 'justify', 'postcommand', 'show', 'state', 'style', 417 'takefocus', 'textvariable', 418 'validate', 'validatecommand', 'values', 419 'width', 'xscrollcommand', 420 ) 421 422 def setUp(self): 423 super().setUp() 424 self.combo = self.create() 425 426 def create(self, **kwargs): 427 return ttk.Combobox(self.root, **kwargs) 428 429 def test_height(self): 430 widget = self.create() 431 self.checkParams(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i') 432 433 def _show_drop_down_listbox(self): 434 width = self.combo.winfo_width() 435 self.combo.event_generate('<ButtonPress-1>', x=width - 5, y=5) 436 self.combo.event_generate('<ButtonRelease-1>', x=width - 5, y=5) 437 self.combo.update_idletasks() 438 439 440 def test_virtual_event(self): 441 success = [] 442 443 self.combo['values'] = [1] 444 self.combo.bind('<<ComboboxSelected>>', 445 lambda evt: success.append(True)) 446 self.combo.pack() 447 self.combo.wait_visibility() 448 449 height = self.combo.winfo_height() 450 self._show_drop_down_listbox() 451 self.combo.update() 452 self.combo.event_generate('<Return>') 453 self.combo.update() 454 455 self.assertTrue(success) 456 457 458 def test_postcommand(self): 459 success = [] 460 461 self.combo['postcommand'] = lambda: success.append(True) 462 self.combo.pack() 463 self.combo.wait_visibility() 464 465 self._show_drop_down_listbox() 466 self.assertTrue(success) 467 468 # testing postcommand removal 469 self.combo['postcommand'] = '' 470 self._show_drop_down_listbox() 471 self.assertEqual(len(success), 1) 472 473 474 def test_values(self): 475 def check_get_current(getval, currval): 476 self.assertEqual(self.combo.get(), getval) 477 self.assertEqual(self.combo.current(), currval) 478 479 self.assertEqual(self.combo['values'], 480 () if tcl_version < (8, 5) else '') 481 check_get_current('', -1) 482 483 self.checkParam(self.combo, 'values', 'mon tue wed thur', 484 expected=('mon', 'tue', 'wed', 'thur')) 485 self.checkParam(self.combo, 'values', ('mon', 'tue', 'wed', 'thur')) 486 self.checkParam(self.combo, 'values', (42, 3.14, '', 'any string')) 487 self.checkParam(self.combo, 'values', '', 488 expected='' if get_tk_patchlevel() < (8, 5, 10) else ()) 489 490 self.combo['values'] = ['a', 1, 'c'] 491 492 self.combo.set('c') 493 check_get_current('c', 2) 494 495 self.combo.current(0) 496 check_get_current('a', 0) 497 498 self.combo.set('d') 499 check_get_current('d', -1) 500 501 # testing values with empty string 502 self.combo.set('') 503 self.combo['values'] = (1, 2, '', 3) 504 check_get_current('', 2) 505 506 # testing values with empty string set through configure 507 self.combo.configure(values=[1, '', 2]) 508 self.assertEqual(self.combo['values'], 509 ('1', '', '2') if self.wantobjects else 510 '1 {} 2') 511 512 # testing values with spaces 513 self.combo['values'] = ['a b', 'a\tb', 'a\nb'] 514 self.assertEqual(self.combo['values'], 515 ('a b', 'a\tb', 'a\nb') if self.wantobjects else 516 '{a b} {a\tb} {a\nb}') 517 518 # testing values with special characters 519 self.combo['values'] = [r'a\tb', '"a"', '} {'] 520 self.assertEqual(self.combo['values'], 521 (r'a\tb', '"a"', '} {') if self.wantobjects else 522 r'a\\tb {"a"} \}\ \{') 523 524 # out of range 525 self.assertRaises(tkinter.TclError, self.combo.current, 526 len(self.combo['values'])) 527 # it expects an integer (or something that can be converted to int) 528 self.assertRaises(tkinter.TclError, self.combo.current, '') 529 530 # testing creating combobox with empty string in values 531 combo2 = ttk.Combobox(self.root, values=[1, 2, '']) 532 self.assertEqual(combo2['values'], 533 ('1', '2', '') if self.wantobjects else '1 2 {}') 534 combo2.destroy() 535 536 537 @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) 538 class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): 539 OPTIONS = ( 540 'class', 'cursor', 'height', 541 'orient', 'style', 'takefocus', 'width', 542 ) 543 544 def setUp(self): 545 super().setUp() 546 self.paned = self.create() 547 548 def create(self, **kwargs): 549 return ttk.PanedWindow(self.root, **kwargs) 550 551 def test_orient(self): 552 widget = self.create() 553 self.assertEqual(str(widget['orient']), 'vertical') 554 errmsg='attempt to change read-only option' 555 if get_tk_patchlevel() < (8, 6, 0, 'beta', 3): 556 errmsg='Attempt to change read-only option' 557 self.checkInvalidParam(widget, 'orient', 'horizontal', 558 errmsg=errmsg) 559 widget2 = self.create(orient='horizontal') 560 self.assertEqual(str(widget2['orient']), 'horizontal') 561 562 def test_add(self): 563 # attempt to add a child that is not a direct child of the paned window 564 label = ttk.Label(self.paned) 565 child = ttk.Label(label) 566 self.assertRaises(tkinter.TclError, self.paned.add, child) 567 label.destroy() 568 child.destroy() 569 # another attempt 570 label = ttk.Label(self.root) 571 child = ttk.Label(label) 572 self.assertRaises(tkinter.TclError, self.paned.add, child) 573 child.destroy() 574 label.destroy() 575 576 good_child = ttk.Label(self.root) 577 self.paned.add(good_child) 578 # re-adding a child is not accepted 579 self.assertRaises(tkinter.TclError, self.paned.add, good_child) 580 581 other_child = ttk.Label(self.paned) 582 self.paned.add(other_child) 583 self.assertEqual(self.paned.pane(0), self.paned.pane(1)) 584 self.assertRaises(tkinter.TclError, self.paned.pane, 2) 585 good_child.destroy() 586 other_child.destroy() 587 self.assertRaises(tkinter.TclError, self.paned.pane, 0) 588 589 590 def test_forget(self): 591 self.assertRaises(tkinter.TclError, self.paned.forget, None) 592 self.assertRaises(tkinter.TclError, self.paned.forget, 0) 593 594 self.paned.add(ttk.Label(self.root)) 595 self.paned.forget(0) 596 self.assertRaises(tkinter.TclError, self.paned.forget, 0) 597 598 599 def test_insert(self): 600 self.assertRaises(tkinter.TclError, self.paned.insert, None, 0) 601 self.assertRaises(tkinter.TclError, self.paned.insert, 0, None) 602 self.assertRaises(tkinter.TclError, self.paned.insert, 0, 0) 603 604 child = ttk.Label(self.root) 605 child2 = ttk.Label(self.root) 606 child3 = ttk.Label(self.root) 607 608 self.assertRaises(tkinter.TclError, self.paned.insert, 0, child) 609 610 self.paned.insert('end', child2) 611 self.paned.insert(0, child) 612 self.assertEqual(self.paned.panes(), (str(child), str(child2))) 613 614 self.paned.insert(0, child2) 615 self.assertEqual(self.paned.panes(), (str(child2), str(child))) 616 617 self.paned.insert('end', child3) 618 self.assertEqual(self.paned.panes(), 619 (str(child2), str(child), str(child3))) 620 621 # reinserting a child should move it to its current position 622 panes = self.paned.panes() 623 self.paned.insert('end', child3) 624 self.assertEqual(panes, self.paned.panes()) 625 626 # moving child3 to child2 position should result in child2 ending up 627 # in previous child position and child ending up in previous child3 628 # position 629 self.paned.insert(child2, child3) 630 self.assertEqual(self.paned.panes(), 631 (str(child3), str(child2), str(child))) 632 633 634 def test_pane(self): 635 self.assertRaises(tkinter.TclError, self.paned.pane, 0) 636 637 child = ttk.Label(self.root) 638 self.paned.add(child) 639 self.assertIsInstance(self.paned.pane(0), dict) 640 self.assertEqual(self.paned.pane(0, weight=None), 641 0 if self.wantobjects else '0') 642 # newer form for querying a single option 643 self.assertEqual(self.paned.pane(0, 'weight'), 644 0 if self.wantobjects else '0') 645 self.assertEqual(self.paned.pane(0), self.paned.pane(str(child))) 646 647 self.assertRaises(tkinter.TclError, self.paned.pane, 0, 648 badoption='somevalue') 649 650 651 def test_sashpos(self): 652 self.assertRaises(tkinter.TclError, self.paned.sashpos, None) 653 self.assertRaises(tkinter.TclError, self.paned.sashpos, '') 654 self.assertRaises(tkinter.TclError, self.paned.sashpos, 0) 655 656 child = ttk.Label(self.paned, text='a') 657 self.paned.add(child, weight=1) 658 self.assertRaises(tkinter.TclError, self.paned.sashpos, 0) 659 child2 = ttk.Label(self.paned, text='b') 660 self.paned.add(child2) 661 self.assertRaises(tkinter.TclError, self.paned.sashpos, 1) 662 663 self.paned.pack(expand=True, fill='both') 664 self.paned.wait_visibility() 665 666 curr_pos = self.paned.sashpos(0) 667 self.paned.sashpos(0, 1000) 668 self.assertNotEqual(curr_pos, self.paned.sashpos(0)) 669 self.assertIsInstance(self.paned.sashpos(0), int) 670 671 672 @add_standard_options(StandardTtkOptionsTests) 673 class RadiobuttonTest(AbstractLabelTest, unittest.TestCase): 674 OPTIONS = ( 675 'class', 'command', 'compound', 'cursor', 676 'image', 677 'padding', 'state', 'style', 678 'takefocus', 'text', 'textvariable', 679 'underline', 'value', 'variable', 'width', 680 ) 681 682 def create(self, **kwargs): 683 return ttk.Radiobutton(self.root, **kwargs) 684 685 def test_value(self): 686 widget = self.create() 687 self.checkParams(widget, 'value', 1, 2.3, '', 'any string') 688 689 def test_invoke(self): 690 success = [] 691 def cb_test(): 692 success.append(1) 693 return "cb test called" 694 695 myvar = tkinter.IntVar(self.root) 696 cbtn = ttk.Radiobutton(self.root, command=cb_test, 697 variable=myvar, value=0) 698 cbtn2 = ttk.Radiobutton(self.root, command=cb_test, 699 variable=myvar, value=1) 700 701 if self.wantobjects: 702 conv = lambda x: x 703 else: 704 conv = int 705 706 res = cbtn.invoke() 707 self.assertEqual(res, "cb test called") 708 self.assertEqual(conv(cbtn['value']), myvar.get()) 709 self.assertEqual(myvar.get(), 710 conv(cbtn.tk.globalgetvar(cbtn['variable']))) 711 self.assertTrue(success) 712 713 cbtn2['command'] = '' 714 res = cbtn2.invoke() 715 self.assertEqual(str(res), '') 716 self.assertLessEqual(len(success), 1) 717 self.assertEqual(conv(cbtn2['value']), myvar.get()) 718 self.assertEqual(myvar.get(), 719 conv(cbtn.tk.globalgetvar(cbtn['variable']))) 720 721 self.assertEqual(str(cbtn['variable']), str(cbtn2['variable'])) 722 723 724 class MenubuttonTest(AbstractLabelTest, unittest.TestCase): 725 OPTIONS = ( 726 'class', 'compound', 'cursor', 'direction', 727 'image', 'menu', 'padding', 'state', 'style', 728 'takefocus', 'text', 'textvariable', 729 'underline', 'width', 730 ) 731 732 def create(self, **kwargs): 733 return ttk.Menubutton(self.root, **kwargs) 734 735 def test_direction(self): 736 widget = self.create() 737 self.checkEnumParam(widget, 'direction', 738 'above', 'below', 'left', 'right', 'flush') 739 740 def test_menu(self): 741 widget = self.create() 742 menu = tkinter.Menu(widget, name='menu') 743 self.checkParam(widget, 'menu', menu, conv=str) 744 menu.destroy() 745 746 747 @add_standard_options(StandardTtkOptionsTests) 748 class ScaleTest(AbstractWidgetTest, unittest.TestCase): 749 OPTIONS = ( 750 'class', 'command', 'cursor', 'from', 'length', 751 'orient', 'style', 'takefocus', 'to', 'value', 'variable', 752 ) 753 _conv_pixels = noconv 754 default_orient = 'horizontal' 755 756 def setUp(self): 757 super().setUp() 758 self.scale = self.create() 759 self.scale.pack() 760 self.scale.update() 761 762 def create(self, **kwargs): 763 return ttk.Scale(self.root, **kwargs) 764 765 def test_from(self): 766 widget = self.create() 767 self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=False) 768 769 def test_length(self): 770 widget = self.create() 771 self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i') 772 773 def test_to(self): 774 widget = self.create() 775 self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, conv=False) 776 777 def test_value(self): 778 widget = self.create() 779 self.checkFloatParam(widget, 'value', 300, 14.9, 15.1, -10, conv=False) 780 781 def test_custom_event(self): 782 failure = [1, 1, 1] # will need to be empty 783 784 funcid = self.scale.bind('<<RangeChanged>>', lambda evt: failure.pop()) 785 786 self.scale['from'] = 10 787 self.scale['from_'] = 10 788 self.scale['to'] = 3 789 790 self.assertFalse(failure) 791 792 failure = [1, 1, 1] 793 self.scale.configure(from_=2, to=5) 794 self.scale.configure(from_=0, to=-2) 795 self.scale.configure(to=10) 796 797 self.assertFalse(failure) 798 799 800 def test_get(self): 801 if self.wantobjects: 802 conv = lambda x: x 803 else: 804 conv = float 805 806 scale_width = self.scale.winfo_width() 807 self.assertEqual(self.scale.get(scale_width, 0), self.scale['to']) 808 809 self.assertEqual(conv(self.scale.get(0, 0)), conv(self.scale['from'])) 810 self.assertEqual(self.scale.get(), self.scale['value']) 811 self.scale['value'] = 30 812 self.assertEqual(self.scale.get(), self.scale['value']) 813 814 self.assertRaises(tkinter.TclError, self.scale.get, '', 0) 815 self.assertRaises(tkinter.TclError, self.scale.get, 0, '') 816 817 818 def test_set(self): 819 if self.wantobjects: 820 conv = lambda x: x 821 else: 822 conv = float 823 824 # set restricts the max/min values according to the current range 825 max = conv(self.scale['to']) 826 new_max = max + 10 827 self.scale.set(new_max) 828 self.assertEqual(conv(self.scale.get()), max) 829 min = conv(self.scale['from']) 830 self.scale.set(min - 1) 831 self.assertEqual(conv(self.scale.get()), min) 832 833 # changing directly the variable doesn't impose this limitation tho 834 var = tkinter.DoubleVar(self.root) 835 self.scale['variable'] = var 836 var.set(max + 5) 837 self.assertEqual(conv(self.scale.get()), var.get()) 838 self.assertEqual(conv(self.scale.get()), max + 5) 839 del var 840 841 # the same happens with the value option 842 self.scale['value'] = max + 10 843 self.assertEqual(conv(self.scale.get()), max + 10) 844 self.assertEqual(conv(self.scale.get()), conv(self.scale['value'])) 845 846 # nevertheless, note that the max/min values we can get specifying 847 # x, y coords are the ones according to the current range 848 self.assertEqual(conv(self.scale.get(0, 0)), min) 849 self.assertEqual(conv(self.scale.get(self.scale.winfo_width(), 0)), max) 850 851 self.assertRaises(tkinter.TclError, self.scale.set, None) 852 853 854 @add_standard_options(StandardTtkOptionsTests) 855 class ProgressbarTest(AbstractWidgetTest, unittest.TestCase): 856 OPTIONS = ( 857 'class', 'cursor', 'orient', 'length', 858 'mode', 'maximum', 'phase', 859 'style', 'takefocus', 'value', 'variable', 860 ) 861 _conv_pixels = noconv 862 default_orient = 'horizontal' 863 864 def create(self, **kwargs): 865 return ttk.Progressbar(self.root, **kwargs) 866 867 def test_length(self): 868 widget = self.create() 869 self.checkPixelsParam(widget, 'length', 100.1, 56.7, '2i') 870 871 def test_maximum(self): 872 widget = self.create() 873 self.checkFloatParam(widget, 'maximum', 150.2, 77.7, 0, -10, conv=False) 874 875 def test_mode(self): 876 widget = self.create() 877 self.checkEnumParam(widget, 'mode', 'determinate', 'indeterminate') 878 879 def test_phase(self): 880 # XXX 881 pass 882 883 def test_value(self): 884 widget = self.create() 885 self.checkFloatParam(widget, 'value', 150.2, 77.7, 0, -10, 886 conv=False) 887 888 889 @unittest.skipIf(sys.platform == 'darwin', 890 'ttk.Scrollbar is special on MacOSX') 891 @add_standard_options(StandardTtkOptionsTests) 892 class ScrollbarTest(AbstractWidgetTest, unittest.TestCase): 893 OPTIONS = ( 894 'class', 'command', 'cursor', 'orient', 'style', 'takefocus', 895 ) 896 default_orient = 'vertical' 897 898 def create(self, **kwargs): 899 return ttk.Scrollbar(self.root, **kwargs) 900 901 902 @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) 903 class NotebookTest(AbstractWidgetTest, unittest.TestCase): 904 OPTIONS = ( 905 'class', 'cursor', 'height', 'padding', 'style', 'takefocus', 'width', 906 ) 907 908 def setUp(self): 909 super().setUp() 910 self.nb = self.create(padding=0) 911 self.child1 = ttk.Label(self.root) 912 self.child2 = ttk.Label(self.root) 913 self.nb.add(self.child1, text='a') 914 self.nb.add(self.child2, text='b') 915 916 def create(self, **kwargs): 917 return ttk.Notebook(self.root, **kwargs) 918 919 def test_tab_identifiers(self): 920 self.nb.forget(0) 921 self.nb.hide(self.child2) 922 self.assertRaises(tkinter.TclError, self.nb.tab, self.child1) 923 self.assertEqual(self.nb.index('end'), 1) 924 self.nb.add(self.child2) 925 self.assertEqual(self.nb.index('end'), 1) 926 self.nb.select(self.child2) 927 928 self.assertTrue(self.nb.tab('current')) 929 self.nb.add(self.child1, text='a') 930 931 self.nb.pack() 932 self.nb.wait_visibility() 933 if sys.platform == 'darwin': 934 tb_idx = "@20,5" 935 else: 936 tb_idx = "@5,5" 937 self.assertEqual(self.nb.tab(tb_idx), self.nb.tab('current')) 938 939 for i in range(5, 100, 5): 940 try: 941 if self.nb.tab('@%d, 5' % i, text=None) == 'a': 942 break 943 except tkinter.TclError: 944 pass 945 946 else: 947 self.fail("Tab with text 'a' not found") 948 949 950 def test_add_and_hidden(self): 951 self.assertRaises(tkinter.TclError, self.nb.hide, -1) 952 self.assertRaises(tkinter.TclError, self.nb.hide, 'hi') 953 self.assertRaises(tkinter.TclError, self.nb.hide, None) 954 self.assertRaises(tkinter.TclError, self.nb.add, None) 955 self.assertRaises(tkinter.TclError, self.nb.add, ttk.Label(self.root), 956 unknown='option') 957 958 tabs = self.nb.tabs() 959 self.nb.hide(self.child1) 960 self.nb.add(self.child1) 961 self.assertEqual(self.nb.tabs(), tabs) 962 963 child = ttk.Label(self.root) 964 self.nb.add(child, text='c') 965 tabs = self.nb.tabs() 966 967 curr = self.nb.index('current') 968 # verify that the tab gets readded at its previous position 969 child2_index = self.nb.index(self.child2) 970 self.nb.hide(self.child2) 971 self.nb.add(self.child2) 972 self.assertEqual(self.nb.tabs(), tabs) 973 self.assertEqual(self.nb.index(self.child2), child2_index) 974 self.assertEqual(str(self.child2), self.nb.tabs()[child2_index]) 975 # but the tab next to it (not hidden) is the one selected now 976 self.assertEqual(self.nb.index('current'), curr + 1) 977 978 979 def test_forget(self): 980 self.assertRaises(tkinter.TclError, self.nb.forget, -1) 981 self.assertRaises(tkinter.TclError, self.nb.forget, 'hi') 982 self.assertRaises(tkinter.TclError, self.nb.forget, None) 983 984 tabs = self.nb.tabs() 985 child1_index = self.nb.index(self.child1) 986 self.nb.forget(self.child1) 987 self.assertNotIn(str(self.child1), self.nb.tabs()) 988 self.assertEqual(len(tabs) - 1, len(self.nb.tabs())) 989 990 self.nb.add(self.child1) 991 self.assertEqual(self.nb.index(self.child1), 1) 992 self.assertNotEqual(child1_index, self.nb.index(self.child1)) 993 994 995 def test_index(self): 996 self.assertRaises(tkinter.TclError, self.nb.index, -1) 997 self.assertRaises(tkinter.TclError, self.nb.index, None) 998 999 self.assertIsInstance(self.nb.index('end'), int) 1000 self.assertEqual(self.nb.index(self.child1), 0) 1001 self.assertEqual(self.nb.index(self.child2), 1) 1002 self.assertEqual(self.nb.index('end'), 2) 1003 1004 1005 def test_insert(self): 1006 # moving tabs 1007 tabs = self.nb.tabs() 1008 self.nb.insert(1, tabs[0]) 1009 self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0])) 1010 self.nb.insert(self.child1, self.child2) 1011 self.assertEqual(self.nb.tabs(), tabs) 1012 self.nb.insert('end', self.child1) 1013 self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0])) 1014 self.nb.insert('end', 0) 1015 self.assertEqual(self.nb.tabs(), tabs) 1016 # bad moves 1017 self.assertRaises(tkinter.TclError, self.nb.insert, 2, tabs[0]) 1018 self.assertRaises(tkinter.TclError, self.nb.insert, -1, tabs[0]) 1019 1020 # new tab 1021 child3 = ttk.Label(self.root) 1022 self.nb.insert(1, child3) 1023 self.assertEqual(self.nb.tabs(), (tabs[0], str(child3), tabs[1])) 1024 self.nb.forget(child3) 1025 self.assertEqual(self.nb.tabs(), tabs) 1026 self.nb.insert(self.child1, child3) 1027 self.assertEqual(self.nb.tabs(), (str(child3), ) + tabs) 1028 self.nb.forget(child3) 1029 self.assertRaises(tkinter.TclError, self.nb.insert, 2, child3) 1030 self.assertRaises(tkinter.TclError, self.nb.insert, -1, child3) 1031 1032 # bad inserts 1033 self.assertRaises(tkinter.TclError, self.nb.insert, 'end', None) 1034 self.assertRaises(tkinter.TclError, self.nb.insert, None, 0) 1035 self.assertRaises(tkinter.TclError, self.nb.insert, None, None) 1036 1037 1038 def test_select(self): 1039 self.nb.pack() 1040 self.nb.wait_visibility() 1041 1042 success = [] 1043 tab_changed = [] 1044 1045 self.child1.bind('<Unmap>', lambda evt: success.append(True)) 1046 self.nb.bind('<<NotebookTabChanged>>', 1047 lambda evt: tab_changed.append(True)) 1048 1049 self.assertEqual(self.nb.select(), str(self.child1)) 1050 self.nb.select(self.child2) 1051 self.assertTrue(success) 1052 self.assertEqual(self.nb.select(), str(self.child2)) 1053 1054 self.nb.update() 1055 self.assertTrue(tab_changed) 1056 1057 1058 def test_tab(self): 1059 self.assertRaises(tkinter.TclError, self.nb.tab, -1) 1060 self.assertRaises(tkinter.TclError, self.nb.tab, 'notab') 1061 self.assertRaises(tkinter.TclError, self.nb.tab, None) 1062 1063 self.assertIsInstance(self.nb.tab(self.child1), dict) 1064 self.assertEqual(self.nb.tab(self.child1, text=None), 'a') 1065 # newer form for querying a single option 1066 self.assertEqual(self.nb.tab(self.child1, 'text'), 'a') 1067 self.nb.tab(self.child1, text='abc') 1068 self.assertEqual(self.nb.tab(self.child1, text=None), 'abc') 1069 self.assertEqual(self.nb.tab(self.child1, 'text'), 'abc') 1070 1071 1072 def test_tabs(self): 1073 self.assertEqual(len(self.nb.tabs()), 2) 1074 1075 self.nb.forget(self.child1) 1076 self.nb.forget(self.child2) 1077 1078 self.assertEqual(self.nb.tabs(), ()) 1079 1080 1081 def test_traversal(self): 1082 self.nb.pack() 1083 self.nb.wait_visibility() 1084 1085 self.nb.select(0) 1086 1087 simulate_mouse_click(self.nb, 5, 5) 1088 self.nb.focus_force() 1089 self.nb.event_generate('<Control-Tab>') 1090 self.assertEqual(self.nb.select(), str(self.child2)) 1091 self.nb.focus_force() 1092 self.nb.event_generate('<Shift-Control-Tab>') 1093 self.assertEqual(self.nb.select(), str(self.child1)) 1094 self.nb.focus_force() 1095 self.nb.event_generate('<Shift-Control-Tab>') 1096 self.assertEqual(self.nb.select(), str(self.child2)) 1097 1098 self.nb.tab(self.child1, text='a', underline=0) 1099 self.nb.enable_traversal() 1100 self.nb.focus_force() 1101 simulate_mouse_click(self.nb, 5, 5) 1102 if sys.platform == 'darwin': 1103 self.nb.event_generate('<Option-a>') 1104 else: 1105 self.nb.event_generate('<Alt-a>') 1106 self.assertEqual(self.nb.select(), str(self.child1)) 1107 1108 1109 @add_standard_options(StandardTtkOptionsTests) 1110 class TreeviewTest(AbstractWidgetTest, unittest.TestCase): 1111 OPTIONS = ( 1112 'class', 'columns', 'cursor', 'displaycolumns', 1113 'height', 'padding', 'selectmode', 'show', 1114 'style', 'takefocus', 'xscrollcommand', 'yscrollcommand', 1115 ) 1116 1117 def setUp(self): 1118 super().setUp() 1119 self.tv = self.create(padding=0) 1120 1121 def create(self, **kwargs): 1122 return ttk.Treeview(self.root, **kwargs) 1123 1124 def test_columns(self): 1125 widget = self.create() 1126 self.checkParam(widget, 'columns', 'a b c', 1127 expected=('a', 'b', 'c')) 1128 self.checkParam(widget, 'columns', ('a', 'b', 'c')) 1129 self.checkParam(widget, 'columns', (), 1130 expected='' if get_tk_patchlevel() < (8, 5, 10) else ()) 1131 1132 def test_displaycolumns(self): 1133 widget = self.create() 1134 widget['columns'] = ('a', 'b', 'c') 1135 self.checkParam(widget, 'displaycolumns', 'b a c', 1136 expected=('b', 'a', 'c')) 1137 self.checkParam(widget, 'displaycolumns', ('b', 'a', 'c')) 1138 self.checkParam(widget, 'displaycolumns', '#all', 1139 expected=('#all',)) 1140 self.checkParam(widget, 'displaycolumns', (2, 1, 0)) 1141 self.checkInvalidParam(widget, 'displaycolumns', ('a', 'b', 'd'), 1142 errmsg='Invalid column index d') 1143 self.checkInvalidParam(widget, 'displaycolumns', (1, 2, 3), 1144 errmsg='Column index 3 out of bounds') 1145 self.checkInvalidParam(widget, 'displaycolumns', (1, -2), 1146 errmsg='Column index -2 out of bounds') 1147 1148 def test_height(self): 1149 widget = self.create() 1150 self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', conv=False) 1151 self.checkPixelsParam(widget, 'height', 101.2, 102.6, conv=noconv) 1152 1153 def test_selectmode(self): 1154 widget = self.create() 1155 self.checkEnumParam(widget, 'selectmode', 1156 'none', 'browse', 'extended') 1157 1158 def test_show(self): 1159 widget = self.create() 1160 self.checkParam(widget, 'show', 'tree headings', 1161 expected=('tree', 'headings')) 1162 self.checkParam(widget, 'show', ('tree', 'headings')) 1163 self.checkParam(widget, 'show', ('headings', 'tree')) 1164 self.checkParam(widget, 'show', 'tree', expected=('tree',)) 1165 self.checkParam(widget, 'show', 'headings', expected=('headings',)) 1166 1167 def test_bbox(self): 1168 self.tv.pack() 1169 self.assertEqual(self.tv.bbox(''), '') 1170 self.tv.wait_visibility() 1171 self.tv.update() 1172 1173 item_id = self.tv.insert('', 'end') 1174 children = self.tv.get_children() 1175 self.assertTrue(children) 1176 1177 bbox = self.tv.bbox(children[0]) 1178 self.assertIsBoundingBox(bbox) 1179 1180 # compare width in bboxes 1181 self.tv['columns'] = ['test'] 1182 self.tv.column('test', width=50) 1183 bbox_column0 = self.tv.bbox(children[0], 0) 1184 root_width = self.tv.column('#0', width=None) 1185 if not self.wantobjects: 1186 root_width = int(root_width) 1187 self.assertEqual(bbox_column0[0], bbox[0] + root_width) 1188 1189 # verify that bbox of a closed item is the empty string 1190 child1 = self.tv.insert(item_id, 'end') 1191 self.assertEqual(self.tv.bbox(child1), '') 1192 1193 1194 def test_children(self): 1195 # no children yet, should get an empty tuple 1196 self.assertEqual(self.tv.get_children(), ()) 1197 1198 item_id = self.tv.insert('', 'end') 1199 self.assertIsInstance(self.tv.get_children(), tuple) 1200 self.assertEqual(self.tv.get_children()[0], item_id) 1201 1202 # add item_id and child3 as children of child2 1203 child2 = self.tv.insert('', 'end') 1204 child3 = self.tv.insert('', 'end') 1205 self.tv.set_children(child2, item_id, child3) 1206 self.assertEqual(self.tv.get_children(child2), (item_id, child3)) 1207 1208 # child3 has child2 as parent, thus trying to set child2 as a children 1209 # of child3 should result in an error 1210 self.assertRaises(tkinter.TclError, 1211 self.tv.set_children, child3, child2) 1212 1213 # remove child2 children 1214 self.tv.set_children(child2) 1215 self.assertEqual(self.tv.get_children(child2), ()) 1216 1217 # remove root's children 1218 self.tv.set_children('') 1219 self.assertEqual(self.tv.get_children(), ()) 1220 1221 1222 def test_column(self): 1223 # return a dict with all options/values 1224 self.assertIsInstance(self.tv.column('#0'), dict) 1225 # return a single value of the given option 1226 if self.wantobjects: 1227 self.assertIsInstance(self.tv.column('#0', width=None), int) 1228 # set a new value for an option 1229 self.tv.column('#0', width=10) 1230 # testing new way to get option value 1231 self.assertEqual(self.tv.column('#0', 'width'), 1232 10 if self.wantobjects else '10') 1233 self.assertEqual(self.tv.column('#0', width=None), 1234 10 if self.wantobjects else '10') 1235 # check read-only option 1236 self.assertRaises(tkinter.TclError, self.tv.column, '#0', id='X') 1237 1238 self.assertRaises(tkinter.TclError, self.tv.column, 'invalid') 1239 invalid_kws = [ 1240 {'unknown_option': 'some value'}, {'stretch': 'wrong'}, 1241 {'anchor': 'wrong'}, {'width': 'wrong'}, {'minwidth': 'wrong'} 1242 ] 1243 for kw in invalid_kws: 1244 self.assertRaises(tkinter.TclError, self.tv.column, '#0', 1245 **kw) 1246 1247 1248 def test_delete(self): 1249 self.assertRaises(tkinter.TclError, self.tv.delete, '#0') 1250 1251 item_id = self.tv.insert('', 'end') 1252 item2 = self.tv.insert(item_id, 'end') 1253 self.assertEqual(self.tv.get_children(), (item_id, )) 1254 self.assertEqual(self.tv.get_children(item_id), (item2, )) 1255 1256 self.tv.delete(item_id) 1257 self.assertFalse(self.tv.get_children()) 1258 1259 # reattach should fail 1260 self.assertRaises(tkinter.TclError, 1261 self.tv.reattach, item_id, '', 'end') 1262 1263 # test multiple item delete 1264 item1 = self.tv.insert('', 'end') 1265 item2 = self.tv.insert('', 'end') 1266 self.assertEqual(self.tv.get_children(), (item1, item2)) 1267 1268 self.tv.delete(item1, item2) 1269 self.assertFalse(self.tv.get_children()) 1270 1271 1272 def test_detach_reattach(self): 1273 item_id = self.tv.insert('', 'end') 1274 item2 = self.tv.insert(item_id, 'end') 1275 1276 # calling detach without items is valid, although it does nothing 1277 prev = self.tv.get_children() 1278 self.tv.detach() # this should do nothing 1279 self.assertEqual(prev, self.tv.get_children()) 1280 1281 self.assertEqual(self.tv.get_children(), (item_id, )) 1282 self.assertEqual(self.tv.get_children(item_id), (item2, )) 1283 1284 # detach item with children 1285 self.tv.detach(item_id) 1286 self.assertFalse(self.tv.get_children()) 1287 1288 # reattach item with children 1289 self.tv.reattach(item_id, '', 'end') 1290 self.assertEqual(self.tv.get_children(), (item_id, )) 1291 self.assertEqual(self.tv.get_children(item_id), (item2, )) 1292 1293 # move a children to the root 1294 self.tv.move(item2, '', 'end') 1295 self.assertEqual(self.tv.get_children(), (item_id, item2)) 1296 self.assertEqual(self.tv.get_children(item_id), ()) 1297 1298 # bad values 1299 self.assertRaises(tkinter.TclError, 1300 self.tv.reattach, 'nonexistent', '', 'end') 1301 self.assertRaises(tkinter.TclError, 1302 self.tv.detach, 'nonexistent') 1303 self.assertRaises(tkinter.TclError, 1304 self.tv.reattach, item2, 'otherparent', 'end') 1305 self.assertRaises(tkinter.TclError, 1306 self.tv.reattach, item2, '', 'invalid') 1307 1308 # multiple detach 1309 self.tv.detach(item_id, item2) 1310 self.assertEqual(self.tv.get_children(), ()) 1311 self.assertEqual(self.tv.get_children(item_id), ()) 1312 1313 1314 def test_exists(self): 1315 self.assertEqual(self.tv.exists('something'), False) 1316 self.assertEqual(self.tv.exists(''), True) 1317 self.assertEqual(self.tv.exists({}), False) 1318 1319 # the following will make a tk.call equivalent to 1320 # tk.call(treeview, "exists") which should result in an error 1321 # in the tcl interpreter since tk requires an item. 1322 self.assertRaises(tkinter.TclError, self.tv.exists, None) 1323 1324 1325 def test_focus(self): 1326 # nothing is focused right now 1327 self.assertEqual(self.tv.focus(), '') 1328 1329 item1 = self.tv.insert('', 'end') 1330 self.tv.focus(item1) 1331 self.assertEqual(self.tv.focus(), item1) 1332 1333 self.tv.delete(item1) 1334 self.assertEqual(self.tv.focus(), '') 1335 1336 # try focusing inexistent item 1337 self.assertRaises(tkinter.TclError, self.tv.focus, 'hi') 1338 1339 1340 def test_heading(self): 1341 # check a dict is returned 1342 self.assertIsInstance(self.tv.heading('#0'), dict) 1343 1344 # check a value is returned 1345 self.tv.heading('#0', text='hi') 1346 self.assertEqual(self.tv.heading('#0', 'text'), 'hi') 1347 self.assertEqual(self.tv.heading('#0', text=None), 'hi') 1348 1349 # invalid option 1350 self.assertRaises(tkinter.TclError, self.tv.heading, '#0', 1351 background=None) 1352 # invalid value 1353 self.assertRaises(tkinter.TclError, self.tv.heading, '#0', 1354 anchor=1) 1355 1356 def test_heading_callback(self): 1357 def simulate_heading_click(x, y): 1358 simulate_mouse_click(self.tv, x, y) 1359 self.tv.update() 1360 1361 success = [] # no success for now 1362 1363 self.tv.pack() 1364 self.tv.wait_visibility() 1365 self.tv.heading('#0', command=lambda: success.append(True)) 1366 self.tv.column('#0', width=100) 1367 self.tv.update() 1368 1369 # assuming that the coords (5, 5) fall into heading #0 1370 simulate_heading_click(5, 5) 1371 if not success: 1372 self.fail("The command associated to the treeview heading wasn't " 1373 "invoked.") 1374 1375 success = [] 1376 commands = self.tv.master._tclCommands 1377 self.tv.heading('#0', command=str(self.tv.heading('#0', command=None))) 1378 self.assertEqual(commands, self.tv.master._tclCommands) 1379 simulate_heading_click(5, 5) 1380 if not success: 1381 self.fail("The command associated to the treeview heading wasn't " 1382 "invoked.") 1383 1384 # XXX The following raises an error in a tcl interpreter, but not in 1385 # Python 1386 #self.tv.heading('#0', command='I dont exist') 1387 #simulate_heading_click(5, 5) 1388 1389 1390 def test_index(self): 1391 # item 'what' doesn't exist 1392 self.assertRaises(tkinter.TclError, self.tv.index, 'what') 1393 1394 self.assertEqual(self.tv.index(''), 0) 1395 1396 item1 = self.tv.insert('', 'end') 1397 item2 = self.tv.insert('', 'end') 1398 c1 = self.tv.insert(item1, 'end') 1399 c2 = self.tv.insert(item1, 'end') 1400 self.assertEqual(self.tv.index(item1), 0) 1401 self.assertEqual(self.tv.index(c1), 0) 1402 self.assertEqual(self.tv.index(c2), 1) 1403 self.assertEqual(self.tv.index(item2), 1) 1404 1405 self.tv.move(item2, '', 0) 1406 self.assertEqual(self.tv.index(item2), 0) 1407 self.assertEqual(self.tv.index(item1), 1) 1408 1409 # check that index still works even after its parent and siblings 1410 # have been detached 1411 self.tv.detach(item1) 1412 self.assertEqual(self.tv.index(c2), 1) 1413 self.tv.detach(c1) 1414 self.assertEqual(self.tv.index(c2), 0) 1415 1416 # but it fails after item has been deleted 1417 self.tv.delete(item1) 1418 self.assertRaises(tkinter.TclError, self.tv.index, c2) 1419 1420 1421 def test_insert_item(self): 1422 # parent 'none' doesn't exist 1423 self.assertRaises(tkinter.TclError, self.tv.insert, 'none', 'end') 1424 1425 # open values 1426 self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', 1427 open='') 1428 self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', 1429 open='please') 1430 self.assertFalse(self.tv.delete(self.tv.insert('', 'end', open=True))) 1431 self.assertFalse(self.tv.delete(self.tv.insert('', 'end', open=False))) 1432 1433 # invalid index 1434 self.assertRaises(tkinter.TclError, self.tv.insert, '', 'middle') 1435 1436 # trying to duplicate item id is invalid 1437 itemid = self.tv.insert('', 'end', 'first-item') 1438 self.assertEqual(itemid, 'first-item') 1439 self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', 1440 'first-item') 1441 self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', 1442 MockTclObj('first-item')) 1443 1444 # unicode values 1445 value = '\xe1ba' 1446 item = self.tv.insert('', 'end', values=(value, )) 1447 self.assertEqual(self.tv.item(item, 'values'), 1448 (value,) if self.wantobjects else value) 1449 self.assertEqual(self.tv.item(item, values=None), 1450 (value,) if self.wantobjects else value) 1451 1452 self.tv.item(item, values=self.root.splitlist(self.tv.item(item, values=None))) 1453 self.assertEqual(self.tv.item(item, values=None), 1454 (value,) if self.wantobjects else value) 1455 1456 self.assertIsInstance(self.tv.item(item), dict) 1457 1458 # erase item values 1459 self.tv.item(item, values='') 1460 self.assertFalse(self.tv.item(item, values=None)) 1461 1462 # item tags 1463 item = self.tv.insert('', 'end', tags=[1, 2, value]) 1464 self.assertEqual(self.tv.item(item, tags=None), 1465 ('1', '2', value) if self.wantobjects else 1466 '1 2 %s' % value) 1467 self.tv.item(item, tags=[]) 1468 self.assertFalse(self.tv.item(item, tags=None)) 1469 self.tv.item(item, tags=(1, 2)) 1470 self.assertEqual(self.tv.item(item, tags=None), 1471 ('1', '2') if self.wantobjects else '1 2') 1472 1473 # values with spaces 1474 item = self.tv.insert('', 'end', values=('a b c', 1475 '%s %s' % (value, value))) 1476 self.assertEqual(self.tv.item(item, values=None), 1477 ('a b c', '%s %s' % (value, value)) if self.wantobjects else 1478 '{a b c} {%s %s}' % (value, value)) 1479 1480 # text 1481 self.assertEqual(self.tv.item( 1482 self.tv.insert('', 'end', text="Label here"), text=None), 1483 "Label here") 1484 self.assertEqual(self.tv.item( 1485 self.tv.insert('', 'end', text=value), text=None), 1486 value) 1487 1488 1489 def test_selection(self): 1490 self.assertRaises(TypeError, self.tv.selection, 'spam') 1491 # item 'none' doesn't exist 1492 self.assertRaises(tkinter.TclError, self.tv.selection_set, 'none') 1493 self.assertRaises(tkinter.TclError, self.tv.selection_add, 'none') 1494 self.assertRaises(tkinter.TclError, self.tv.selection_remove, 'none') 1495 self.assertRaises(tkinter.TclError, self.tv.selection_toggle, 'none') 1496 1497 item1 = self.tv.insert('', 'end') 1498 item2 = self.tv.insert('', 'end') 1499 c1 = self.tv.insert(item1, 'end') 1500 c2 = self.tv.insert(item1, 'end') 1501 c3 = self.tv.insert(item1, 'end') 1502 self.assertEqual(self.tv.selection(), ()) 1503 1504 self.tv.selection_set(c1, item2) 1505 self.assertEqual(self.tv.selection(), (c1, item2)) 1506 self.tv.selection_set(c2) 1507 self.assertEqual(self.tv.selection(), (c2,)) 1508 1509 self.tv.selection_add(c1, item2) 1510 self.assertEqual(self.tv.selection(), (c1, c2, item2)) 1511 self.tv.selection_add(item1) 1512 self.assertEqual(self.tv.selection(), (item1, c1, c2, item2)) 1513 self.tv.selection_add() 1514 self.assertEqual(self.tv.selection(), (item1, c1, c2, item2)) 1515 1516 self.tv.selection_remove(item1, c3) 1517 self.assertEqual(self.tv.selection(), (c1, c2, item2)) 1518 self.tv.selection_remove(c2) 1519 self.assertEqual(self.tv.selection(), (c1, item2)) 1520 self.tv.selection_remove() 1521 self.assertEqual(self.tv.selection(), (c1, item2)) 1522 1523 self.tv.selection_toggle(c1, c3) 1524 self.assertEqual(self.tv.selection(), (c3, item2)) 1525 self.tv.selection_toggle(item2) 1526 self.assertEqual(self.tv.selection(), (c3,)) 1527 self.tv.selection_toggle() 1528 self.assertEqual(self.tv.selection(), (c3,)) 1529 1530 self.tv.insert('', 'end', id='with spaces') 1531 self.tv.selection_set('with spaces') 1532 self.assertEqual(self.tv.selection(), ('with spaces',)) 1533 1534 self.tv.insert('', 'end', id='{brace') 1535 self.tv.selection_set('{brace') 1536 self.assertEqual(self.tv.selection(), ('{brace',)) 1537 1538 self.tv.insert('', 'end', id='unicode\u20ac') 1539 self.tv.selection_set('unicode\u20ac') 1540 self.assertEqual(self.tv.selection(), ('unicode\u20ac',)) 1541 1542 self.tv.insert('', 'end', id=b'bytes\xe2\x82\xac') 1543 self.tv.selection_set(b'bytes\xe2\x82\xac') 1544 self.assertEqual(self.tv.selection(), ('bytes\xe2\x82\xac',)) 1545 1546 self.tv.selection_set() 1547 self.assertEqual(self.tv.selection(), ()) 1548 1549 # Old interface 1550 self.tv.selection_set((c1, item2)) 1551 self.assertEqual(self.tv.selection(), (c1, item2)) 1552 self.tv.selection_add((c1, item1)) 1553 self.assertEqual(self.tv.selection(), (item1, c1, item2)) 1554 self.tv.selection_remove((item1, c3)) 1555 self.assertEqual(self.tv.selection(), (c1, item2)) 1556 self.tv.selection_toggle((c1, c3)) 1557 self.assertEqual(self.tv.selection(), (c3, item2)) 1558 1559 if sys.version_info >= (3, 7): 1560 import warnings 1561 warnings.warn( 1562 'Deprecated API of Treeview.selection() should be removed') 1563 self.tv.selection_set() 1564 self.assertEqual(self.tv.selection(), ()) 1565 with self.assertWarns(DeprecationWarning): 1566 self.tv.selection('set', (c1, item2)) 1567 self.assertEqual(self.tv.selection(), (c1, item2)) 1568 with self.assertWarns(DeprecationWarning): 1569 self.tv.selection('add', (c1, item1)) 1570 self.assertEqual(self.tv.selection(), (item1, c1, item2)) 1571 with self.assertWarns(DeprecationWarning): 1572 self.tv.selection('remove', (item1, c3)) 1573 self.assertEqual(self.tv.selection(), (c1, item2)) 1574 with self.assertWarns(DeprecationWarning): 1575 self.tv.selection('toggle', (c1, c3)) 1576 self.assertEqual(self.tv.selection(), (c3, item2)) 1577 with self.assertWarns(DeprecationWarning): 1578 selection = self.tv.selection(None) 1579 self.assertEqual(selection, (c3, item2)) 1580 1581 def test_set(self): 1582 self.tv['columns'] = ['A', 'B'] 1583 item = self.tv.insert('', 'end', values=['a', 'b']) 1584 self.assertEqual(self.tv.set(item), {'A': 'a', 'B': 'b'}) 1585 1586 self.tv.set(item, 'B', 'a') 1587 self.assertEqual(self.tv.item(item, values=None), 1588 ('a', 'a') if self.wantobjects else 'a a') 1589 1590 self.tv['columns'] = ['B'] 1591 self.assertEqual(self.tv.set(item), {'B': 'a'}) 1592 1593 self.tv.set(item, 'B', 'b') 1594 self.assertEqual(self.tv.set(item, column='B'), 'b') 1595 self.assertEqual(self.tv.item(item, values=None), 1596 ('b', 'a') if self.wantobjects else 'b a') 1597 1598 self.tv.set(item, 'B', 123) 1599 self.assertEqual(self.tv.set(item, 'B'), 1600 123 if self.wantobjects else '123') 1601 self.assertEqual(self.tv.item(item, values=None), 1602 (123, 'a') if self.wantobjects else '123 a') 1603 self.assertEqual(self.tv.set(item), 1604 {'B': 123} if self.wantobjects else {'B': '123'}) 1605 1606 # inexistent column 1607 self.assertRaises(tkinter.TclError, self.tv.set, item, 'A') 1608 self.assertRaises(tkinter.TclError, self.tv.set, item, 'A', 'b') 1609 1610 # inexistent item 1611 self.assertRaises(tkinter.TclError, self.tv.set, 'notme') 1612 1613 1614 def test_tag_bind(self): 1615 events = [] 1616 item1 = self.tv.insert('', 'end', tags=['call']) 1617 item2 = self.tv.insert('', 'end', tags=['call']) 1618 self.tv.tag_bind('call', '<ButtonPress-1>', 1619 lambda evt: events.append(1)) 1620 self.tv.tag_bind('call', '<ButtonRelease-1>', 1621 lambda evt: events.append(2)) 1622 1623 self.tv.pack() 1624 self.tv.wait_visibility() 1625 self.tv.update() 1626 1627 pos_y = set() 1628 found = set() 1629 for i in range(0, 100, 10): 1630 if len(found) == 2: # item1 and item2 already found 1631 break 1632 item_id = self.tv.identify_row(i) 1633 if item_id and item_id not in found: 1634 pos_y.add(i) 1635 found.add(item_id) 1636 1637 self.assertEqual(len(pos_y), 2) # item1 and item2 y pos 1638 for y in pos_y: 1639 simulate_mouse_click(self.tv, 0, y) 1640 1641 # by now there should be 4 things in the events list, since each 1642 # item had a bind for two events that were simulated above 1643 self.assertEqual(len(events), 4) 1644 for evt in zip(events[::2], events[1::2]): 1645 self.assertEqual(evt, (1, 2)) 1646 1647 1648 def test_tag_configure(self): 1649 # Just testing parameter passing for now 1650 self.assertRaises(TypeError, self.tv.tag_configure) 1651 self.assertRaises(tkinter.TclError, self.tv.tag_configure, 1652 'test', sky='blue') 1653 self.tv.tag_configure('test', foreground='blue') 1654 self.assertEqual(str(self.tv.tag_configure('test', 'foreground')), 1655 'blue') 1656 self.assertEqual(str(self.tv.tag_configure('test', foreground=None)), 1657 'blue') 1658 self.assertIsInstance(self.tv.tag_configure('test'), dict) 1659 1660 def test_tag_has(self): 1661 item1 = self.tv.insert('', 'end', text='Item 1', tags=['tag1']) 1662 item2 = self.tv.insert('', 'end', text='Item 2', tags=['tag2']) 1663 self.assertRaises(TypeError, self.tv.tag_has) 1664 self.assertRaises(TclError, self.tv.tag_has, 'tag1', 'non-existing') 1665 self.assertTrue(self.tv.tag_has('tag1', item1)) 1666 self.assertFalse(self.tv.tag_has('tag1', item2)) 1667 self.assertFalse(self.tv.tag_has('tag2', item1)) 1668 self.assertTrue(self.tv.tag_has('tag2', item2)) 1669 self.assertFalse(self.tv.tag_has('tag3', item1)) 1670 self.assertFalse(self.tv.tag_has('tag3', item2)) 1671 self.assertEqual(self.tv.tag_has('tag1'), (item1,)) 1672 self.assertEqual(self.tv.tag_has('tag2'), (item2,)) 1673 self.assertEqual(self.tv.tag_has('tag3'), ()) 1674 1675 1676 @add_standard_options(StandardTtkOptionsTests) 1677 class SeparatorTest(AbstractWidgetTest, unittest.TestCase): 1678 OPTIONS = ( 1679 'class', 'cursor', 'orient', 'style', 'takefocus', 1680 # 'state'? 1681 ) 1682 default_orient = 'horizontal' 1683 1684 def create(self, **kwargs): 1685 return ttk.Separator(self.root, **kwargs) 1686 1687 1688 @add_standard_options(StandardTtkOptionsTests) 1689 class SizegripTest(AbstractWidgetTest, unittest.TestCase): 1690 OPTIONS = ( 1691 'class', 'cursor', 'style', 'takefocus', 1692 # 'state'? 1693 ) 1694 1695 def create(self, **kwargs): 1696 return ttk.Sizegrip(self.root, **kwargs) 1697 1698 tests_gui = ( 1699 ButtonTest, CheckbuttonTest, ComboboxTest, EntryTest, 1700 FrameTest, LabelFrameTest, LabelTest, MenubuttonTest, 1701 NotebookTest, PanedWindowTest, ProgressbarTest, 1702 RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest, 1703 SizegripTest, TreeviewTest, WidgetTest, 1704 ) 1705 1706 if __name__ == "__main__": 1707 unittest.main() 1708