1import unittest 2import tkinter 3from tkinter import ttk, TclError 4from test.support import requires 5import sys 6 7from tkinter.test.test_ttk.test_functions import MockTclObj 8from tkinter.test.support import (AbstractTkTest, tcl_version, get_tk_patchlevel, 9 simulate_mouse_click) 10from tkinter.test.widget_tests import (add_standard_options, noconv, 11 AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests, 12 setUpModule) 13 14requires('gui') 15 16 17class 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 56class 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 115class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): 116 _conv_pixels = noconv 117 118 119@add_standard_options(StandardTtkOptionsTests) 120class 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) 132class 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 157class 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) 188class 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) 208class 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) 231class 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) 280class 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 # bpo-27313: macOS Cocoa widget differs from X, allow either 333 if sys.platform == 'darwin': 334 self.assertIn(self.entry.identify(5, 5), 335 ("textarea", "Combobox.button") ) 336 else: 337 self.assertEqual(self.entry.identify(5, 5), "textarea") 338 self.assertEqual(self.entry.identify(-1, -1), "") 339 340 self.assertRaises(tkinter.TclError, self.entry.identify, None, 5) 341 self.assertRaises(tkinter.TclError, self.entry.identify, 5, None) 342 self.assertRaises(tkinter.TclError, self.entry.identify, 5, '') 343 344 345 def test_validation_options(self): 346 success = [] 347 test_invalid = lambda: success.append(True) 348 349 self.entry['validate'] = 'none' 350 self.entry['validatecommand'] = lambda: False 351 352 self.entry['invalidcommand'] = test_invalid 353 self.entry.validate() 354 self.assertTrue(success) 355 356 self.entry['invalidcommand'] = '' 357 self.entry.validate() 358 self.assertEqual(len(success), 1) 359 360 self.entry['invalidcommand'] = test_invalid 361 self.entry['validatecommand'] = lambda: True 362 self.entry.validate() 363 self.assertEqual(len(success), 1) 364 365 self.entry['validatecommand'] = '' 366 self.entry.validate() 367 self.assertEqual(len(success), 1) 368 369 self.entry['validatecommand'] = True 370 self.assertRaises(tkinter.TclError, self.entry.validate) 371 372 373 def test_validation(self): 374 validation = [] 375 def validate(to_insert): 376 if not 'a' <= to_insert.lower() <= 'z': 377 validation.append(False) 378 return False 379 validation.append(True) 380 return True 381 382 self.entry['validate'] = 'key' 383 self.entry['validatecommand'] = self.entry.register(validate), '%S' 384 385 self.entry.insert('end', 1) 386 self.entry.insert('end', 'a') 387 self.assertEqual(validation, [False, True]) 388 self.assertEqual(self.entry.get(), 'a') 389 390 391 def test_revalidation(self): 392 def validate(content): 393 for letter in content: 394 if not 'a' <= letter.lower() <= 'z': 395 return False 396 return True 397 398 self.entry['validatecommand'] = self.entry.register(validate), '%P' 399 400 self.entry.insert('end', 'avocado') 401 self.assertEqual(self.entry.validate(), True) 402 self.assertEqual(self.entry.state(), ()) 403 404 self.entry.delete(0, 'end') 405 self.assertEqual(self.entry.get(), '') 406 407 self.entry.insert('end', 'a1b') 408 self.assertEqual(self.entry.validate(), False) 409 self.assertEqual(self.entry.state(), ('invalid', )) 410 411 self.entry.delete(1) 412 self.assertEqual(self.entry.validate(), True) 413 self.assertEqual(self.entry.state(), ()) 414 415 416@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) 417class ComboboxTest(EntryTest, unittest.TestCase): 418 OPTIONS = ( 419 'background', 'class', 'cursor', 'exportselection', 420 'font', 'foreground', 'height', 'invalidcommand', 421 'justify', 'postcommand', 'show', 'state', 'style', 422 'takefocus', 'textvariable', 423 'validate', 'validatecommand', 'values', 424 'width', 'xscrollcommand', 425 ) 426 427 def setUp(self): 428 super().setUp() 429 self.combo = self.create() 430 431 def create(self, **kwargs): 432 return ttk.Combobox(self.root, **kwargs) 433 434 def test_height(self): 435 widget = self.create() 436 self.checkParams(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i') 437 438 def _show_drop_down_listbox(self): 439 width = self.combo.winfo_width() 440 self.combo.event_generate('<ButtonPress-1>', x=width - 5, y=5) 441 self.combo.event_generate('<ButtonRelease-1>', x=width - 5, y=5) 442 self.combo.update_idletasks() 443 444 445 def test_virtual_event(self): 446 success = [] 447 448 self.combo['values'] = [1] 449 self.combo.bind('<<ComboboxSelected>>', 450 lambda evt: success.append(True)) 451 self.combo.pack() 452 self.combo.wait_visibility() 453 454 height = self.combo.winfo_height() 455 self._show_drop_down_listbox() 456 self.combo.update() 457 self.combo.event_generate('<Return>') 458 self.combo.update() 459 460 self.assertTrue(success) 461 462 463 def test_postcommand(self): 464 success = [] 465 466 self.combo['postcommand'] = lambda: success.append(True) 467 self.combo.pack() 468 self.combo.wait_visibility() 469 470 self._show_drop_down_listbox() 471 self.assertTrue(success) 472 473 # testing postcommand removal 474 self.combo['postcommand'] = '' 475 self._show_drop_down_listbox() 476 self.assertEqual(len(success), 1) 477 478 479 def test_values(self): 480 def check_get_current(getval, currval): 481 self.assertEqual(self.combo.get(), getval) 482 self.assertEqual(self.combo.current(), currval) 483 484 self.assertEqual(self.combo['values'], 485 () if tcl_version < (8, 5) else '') 486 check_get_current('', -1) 487 488 self.checkParam(self.combo, 'values', 'mon tue wed thur', 489 expected=('mon', 'tue', 'wed', 'thur')) 490 self.checkParam(self.combo, 'values', ('mon', 'tue', 'wed', 'thur')) 491 self.checkParam(self.combo, 'values', (42, 3.14, '', 'any string')) 492 self.checkParam(self.combo, 'values', '') 493 494 self.combo['values'] = ['a', 1, 'c'] 495 496 self.combo.set('c') 497 check_get_current('c', 2) 498 499 self.combo.current(0) 500 check_get_current('a', 0) 501 502 self.combo.set('d') 503 check_get_current('d', -1) 504 505 # testing values with empty string 506 self.combo.set('') 507 self.combo['values'] = (1, 2, '', 3) 508 check_get_current('', 2) 509 510 # testing values with empty string set through configure 511 self.combo.configure(values=[1, '', 2]) 512 self.assertEqual(self.combo['values'], 513 ('1', '', '2') if self.wantobjects else 514 '1 {} 2') 515 516 # testing values with spaces 517 self.combo['values'] = ['a b', 'a\tb', 'a\nb'] 518 self.assertEqual(self.combo['values'], 519 ('a b', 'a\tb', 'a\nb') if self.wantobjects else 520 '{a b} {a\tb} {a\nb}') 521 522 # testing values with special characters 523 self.combo['values'] = [r'a\tb', '"a"', '} {'] 524 self.assertEqual(self.combo['values'], 525 (r'a\tb', '"a"', '} {') if self.wantobjects else 526 r'a\\tb {"a"} \}\ \{') 527 528 # out of range 529 self.assertRaises(tkinter.TclError, self.combo.current, 530 len(self.combo['values'])) 531 # it expects an integer (or something that can be converted to int) 532 self.assertRaises(tkinter.TclError, self.combo.current, '') 533 534 # testing creating combobox with empty string in values 535 combo2 = ttk.Combobox(self.root, values=[1, 2, '']) 536 self.assertEqual(combo2['values'], 537 ('1', '2', '') if self.wantobjects else '1 2 {}') 538 combo2.destroy() 539 540 541@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) 542class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): 543 OPTIONS = ( 544 'class', 'cursor', 'height', 545 'orient', 'style', 'takefocus', 'width', 546 ) 547 548 def setUp(self): 549 super().setUp() 550 self.paned = self.create() 551 552 def create(self, **kwargs): 553 return ttk.PanedWindow(self.root, **kwargs) 554 555 def test_orient(self): 556 widget = self.create() 557 self.assertEqual(str(widget['orient']), 'vertical') 558 errmsg='attempt to change read-only option' 559 if get_tk_patchlevel() < (8, 6, 0, 'beta', 3): 560 errmsg='Attempt to change read-only option' 561 self.checkInvalidParam(widget, 'orient', 'horizontal', 562 errmsg=errmsg) 563 widget2 = self.create(orient='horizontal') 564 self.assertEqual(str(widget2['orient']), 'horizontal') 565 566 def test_add(self): 567 # attempt to add a child that is not a direct child of the paned window 568 label = ttk.Label(self.paned) 569 child = ttk.Label(label) 570 self.assertRaises(tkinter.TclError, self.paned.add, child) 571 label.destroy() 572 child.destroy() 573 # another attempt 574 label = ttk.Label(self.root) 575 child = ttk.Label(label) 576 self.assertRaises(tkinter.TclError, self.paned.add, child) 577 child.destroy() 578 label.destroy() 579 580 good_child = ttk.Label(self.root) 581 self.paned.add(good_child) 582 # re-adding a child is not accepted 583 self.assertRaises(tkinter.TclError, self.paned.add, good_child) 584 585 other_child = ttk.Label(self.paned) 586 self.paned.add(other_child) 587 self.assertEqual(self.paned.pane(0), self.paned.pane(1)) 588 self.assertRaises(tkinter.TclError, self.paned.pane, 2) 589 good_child.destroy() 590 other_child.destroy() 591 self.assertRaises(tkinter.TclError, self.paned.pane, 0) 592 593 594 def test_forget(self): 595 self.assertRaises(tkinter.TclError, self.paned.forget, None) 596 self.assertRaises(tkinter.TclError, self.paned.forget, 0) 597 598 self.paned.add(ttk.Label(self.root)) 599 self.paned.forget(0) 600 self.assertRaises(tkinter.TclError, self.paned.forget, 0) 601 602 603 def test_insert(self): 604 self.assertRaises(tkinter.TclError, self.paned.insert, None, 0) 605 self.assertRaises(tkinter.TclError, self.paned.insert, 0, None) 606 self.assertRaises(tkinter.TclError, self.paned.insert, 0, 0) 607 608 child = ttk.Label(self.root) 609 child2 = ttk.Label(self.root) 610 child3 = ttk.Label(self.root) 611 612 self.assertRaises(tkinter.TclError, self.paned.insert, 0, child) 613 614 self.paned.insert('end', child2) 615 self.paned.insert(0, child) 616 self.assertEqual(self.paned.panes(), (str(child), str(child2))) 617 618 self.paned.insert(0, child2) 619 self.assertEqual(self.paned.panes(), (str(child2), str(child))) 620 621 self.paned.insert('end', child3) 622 self.assertEqual(self.paned.panes(), 623 (str(child2), str(child), str(child3))) 624 625 # reinserting a child should move it to its current position 626 panes = self.paned.panes() 627 self.paned.insert('end', child3) 628 self.assertEqual(panes, self.paned.panes()) 629 630 # moving child3 to child2 position should result in child2 ending up 631 # in previous child position and child ending up in previous child3 632 # position 633 self.paned.insert(child2, child3) 634 self.assertEqual(self.paned.panes(), 635 (str(child3), str(child2), str(child))) 636 637 638 def test_pane(self): 639 self.assertRaises(tkinter.TclError, self.paned.pane, 0) 640 641 child = ttk.Label(self.root) 642 self.paned.add(child) 643 self.assertIsInstance(self.paned.pane(0), dict) 644 self.assertEqual(self.paned.pane(0, weight=None), 645 0 if self.wantobjects else '0') 646 # newer form for querying a single option 647 self.assertEqual(self.paned.pane(0, 'weight'), 648 0 if self.wantobjects else '0') 649 self.assertEqual(self.paned.pane(0), self.paned.pane(str(child))) 650 651 self.assertRaises(tkinter.TclError, self.paned.pane, 0, 652 badoption='somevalue') 653 654 655 def test_sashpos(self): 656 self.assertRaises(tkinter.TclError, self.paned.sashpos, None) 657 self.assertRaises(tkinter.TclError, self.paned.sashpos, '') 658 self.assertRaises(tkinter.TclError, self.paned.sashpos, 0) 659 660 child = ttk.Label(self.paned, text='a') 661 self.paned.add(child, weight=1) 662 self.assertRaises(tkinter.TclError, self.paned.sashpos, 0) 663 child2 = ttk.Label(self.paned, text='b') 664 self.paned.add(child2) 665 self.assertRaises(tkinter.TclError, self.paned.sashpos, 1) 666 667 self.paned.pack(expand=True, fill='both') 668 self.paned.wait_visibility() 669 670 curr_pos = self.paned.sashpos(0) 671 self.paned.sashpos(0, 1000) 672 self.assertNotEqual(curr_pos, self.paned.sashpos(0)) 673 self.assertIsInstance(self.paned.sashpos(0), int) 674 675 676@add_standard_options(StandardTtkOptionsTests) 677class RadiobuttonTest(AbstractLabelTest, unittest.TestCase): 678 OPTIONS = ( 679 'class', 'command', 'compound', 'cursor', 680 'image', 681 'padding', 'state', 'style', 682 'takefocus', 'text', 'textvariable', 683 'underline', 'value', 'variable', 'width', 684 ) 685 686 def create(self, **kwargs): 687 return ttk.Radiobutton(self.root, **kwargs) 688 689 def test_value(self): 690 widget = self.create() 691 self.checkParams(widget, 'value', 1, 2.3, '', 'any string') 692 693 def test_invoke(self): 694 success = [] 695 def cb_test(): 696 success.append(1) 697 return "cb test called" 698 699 myvar = tkinter.IntVar(self.root) 700 cbtn = ttk.Radiobutton(self.root, command=cb_test, 701 variable=myvar, value=0) 702 cbtn2 = ttk.Radiobutton(self.root, command=cb_test, 703 variable=myvar, value=1) 704 705 if self.wantobjects: 706 conv = lambda x: x 707 else: 708 conv = int 709 710 res = cbtn.invoke() 711 self.assertEqual(res, "cb test called") 712 self.assertEqual(conv(cbtn['value']), myvar.get()) 713 self.assertEqual(myvar.get(), 714 conv(cbtn.tk.globalgetvar(cbtn['variable']))) 715 self.assertTrue(success) 716 717 cbtn2['command'] = '' 718 res = cbtn2.invoke() 719 self.assertEqual(str(res), '') 720 self.assertLessEqual(len(success), 1) 721 self.assertEqual(conv(cbtn2['value']), myvar.get()) 722 self.assertEqual(myvar.get(), 723 conv(cbtn.tk.globalgetvar(cbtn['variable']))) 724 725 self.assertEqual(str(cbtn['variable']), str(cbtn2['variable'])) 726 727 728class MenubuttonTest(AbstractLabelTest, unittest.TestCase): 729 OPTIONS = ( 730 'class', 'compound', 'cursor', 'direction', 731 'image', 'menu', 'padding', 'state', 'style', 732 'takefocus', 'text', 'textvariable', 733 'underline', 'width', 734 ) 735 736 def create(self, **kwargs): 737 return ttk.Menubutton(self.root, **kwargs) 738 739 def test_direction(self): 740 widget = self.create() 741 self.checkEnumParam(widget, 'direction', 742 'above', 'below', 'left', 'right', 'flush') 743 744 def test_menu(self): 745 widget = self.create() 746 menu = tkinter.Menu(widget, name='menu') 747 self.checkParam(widget, 'menu', menu, conv=str) 748 menu.destroy() 749 750 751@add_standard_options(StandardTtkOptionsTests) 752class ScaleTest(AbstractWidgetTest, unittest.TestCase): 753 OPTIONS = ( 754 'class', 'command', 'cursor', 'from', 'length', 755 'orient', 'style', 'takefocus', 'to', 'value', 'variable', 756 ) 757 _conv_pixels = noconv 758 default_orient = 'horizontal' 759 760 def setUp(self): 761 super().setUp() 762 self.scale = self.create() 763 self.scale.pack() 764 self.scale.update() 765 766 def create(self, **kwargs): 767 return ttk.Scale(self.root, **kwargs) 768 769 def test_from(self): 770 widget = self.create() 771 self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=False) 772 773 def test_length(self): 774 widget = self.create() 775 self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i') 776 777 def test_to(self): 778 widget = self.create() 779 self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, conv=False) 780 781 def test_value(self): 782 widget = self.create() 783 self.checkFloatParam(widget, 'value', 300, 14.9, 15.1, -10, conv=False) 784 785 def test_custom_event(self): 786 failure = [1, 1, 1] # will need to be empty 787 788 funcid = self.scale.bind('<<RangeChanged>>', lambda evt: failure.pop()) 789 790 self.scale['from'] = 10 791 self.scale['from_'] = 10 792 self.scale['to'] = 3 793 794 self.assertFalse(failure) 795 796 failure = [1, 1, 1] 797 self.scale.configure(from_=2, to=5) 798 self.scale.configure(from_=0, to=-2) 799 self.scale.configure(to=10) 800 801 self.assertFalse(failure) 802 803 804 def test_get(self): 805 if self.wantobjects: 806 conv = lambda x: x 807 else: 808 conv = float 809 810 scale_width = self.scale.winfo_width() 811 self.assertEqual(self.scale.get(scale_width, 0), self.scale['to']) 812 813 self.assertEqual(conv(self.scale.get(0, 0)), conv(self.scale['from'])) 814 self.assertEqual(self.scale.get(), self.scale['value']) 815 self.scale['value'] = 30 816 self.assertEqual(self.scale.get(), self.scale['value']) 817 818 self.assertRaises(tkinter.TclError, self.scale.get, '', 0) 819 self.assertRaises(tkinter.TclError, self.scale.get, 0, '') 820 821 822 def test_set(self): 823 if self.wantobjects: 824 conv = lambda x: x 825 else: 826 conv = float 827 828 # set restricts the max/min values according to the current range 829 max = conv(self.scale['to']) 830 new_max = max + 10 831 self.scale.set(new_max) 832 self.assertEqual(conv(self.scale.get()), max) 833 min = conv(self.scale['from']) 834 self.scale.set(min - 1) 835 self.assertEqual(conv(self.scale.get()), min) 836 837 # changing directly the variable doesn't impose this limitation tho 838 var = tkinter.DoubleVar(self.root) 839 self.scale['variable'] = var 840 var.set(max + 5) 841 self.assertEqual(conv(self.scale.get()), var.get()) 842 self.assertEqual(conv(self.scale.get()), max + 5) 843 del var 844 845 # the same happens with the value option 846 self.scale['value'] = max + 10 847 self.assertEqual(conv(self.scale.get()), max + 10) 848 self.assertEqual(conv(self.scale.get()), conv(self.scale['value'])) 849 850 # nevertheless, note that the max/min values we can get specifying 851 # x, y coords are the ones according to the current range 852 self.assertEqual(conv(self.scale.get(0, 0)), min) 853 self.assertEqual(conv(self.scale.get(self.scale.winfo_width(), 0)), max) 854 855 self.assertRaises(tkinter.TclError, self.scale.set, None) 856 857 858@add_standard_options(StandardTtkOptionsTests) 859class ProgressbarTest(AbstractWidgetTest, unittest.TestCase): 860 OPTIONS = ( 861 'class', 'cursor', 'orient', 'length', 862 'mode', 'maximum', 'phase', 863 'style', 'takefocus', 'value', 'variable', 864 ) 865 _conv_pixels = noconv 866 default_orient = 'horizontal' 867 868 def create(self, **kwargs): 869 return ttk.Progressbar(self.root, **kwargs) 870 871 def test_length(self): 872 widget = self.create() 873 self.checkPixelsParam(widget, 'length', 100.1, 56.7, '2i') 874 875 def test_maximum(self): 876 widget = self.create() 877 self.checkFloatParam(widget, 'maximum', 150.2, 77.7, 0, -10, conv=False) 878 879 def test_mode(self): 880 widget = self.create() 881 self.checkEnumParam(widget, 'mode', 'determinate', 'indeterminate') 882 883 def test_phase(self): 884 # XXX 885 pass 886 887 def test_value(self): 888 widget = self.create() 889 self.checkFloatParam(widget, 'value', 150.2, 77.7, 0, -10, 890 conv=False) 891 892 893@unittest.skipIf(sys.platform == 'darwin', 894 'ttk.Scrollbar is special on MacOSX') 895@add_standard_options(StandardTtkOptionsTests) 896class ScrollbarTest(AbstractWidgetTest, unittest.TestCase): 897 OPTIONS = ( 898 'class', 'command', 'cursor', 'orient', 'style', 'takefocus', 899 ) 900 default_orient = 'vertical' 901 902 def create(self, **kwargs): 903 return ttk.Scrollbar(self.root, **kwargs) 904 905 906@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) 907class NotebookTest(AbstractWidgetTest, unittest.TestCase): 908 OPTIONS = ( 909 'class', 'cursor', 'height', 'padding', 'style', 'takefocus', 'width', 910 ) 911 912 def setUp(self): 913 super().setUp() 914 self.nb = self.create(padding=0) 915 self.child1 = ttk.Label(self.root) 916 self.child2 = ttk.Label(self.root) 917 self.nb.add(self.child1, text='a') 918 self.nb.add(self.child2, text='b') 919 920 def create(self, **kwargs): 921 return ttk.Notebook(self.root, **kwargs) 922 923 def test_tab_identifiers(self): 924 self.nb.forget(0) 925 self.nb.hide(self.child2) 926 self.assertRaises(tkinter.TclError, self.nb.tab, self.child1) 927 self.assertEqual(self.nb.index('end'), 1) 928 self.nb.add(self.child2) 929 self.assertEqual(self.nb.index('end'), 1) 930 self.nb.select(self.child2) 931 932 self.assertTrue(self.nb.tab('current')) 933 self.nb.add(self.child1, text='a') 934 935 self.nb.pack() 936 self.nb.wait_visibility() 937 if sys.platform == 'darwin': 938 tb_idx = "@20,5" 939 else: 940 tb_idx = "@5,5" 941 self.assertEqual(self.nb.tab(tb_idx), self.nb.tab('current')) 942 943 for i in range(5, 100, 5): 944 try: 945 if self.nb.tab('@%d, 5' % i, text=None) == 'a': 946 break 947 except tkinter.TclError: 948 pass 949 950 else: 951 self.fail("Tab with text 'a' not found") 952 953 954 def test_add_and_hidden(self): 955 self.assertRaises(tkinter.TclError, self.nb.hide, -1) 956 self.assertRaises(tkinter.TclError, self.nb.hide, 'hi') 957 self.assertRaises(tkinter.TclError, self.nb.hide, None) 958 self.assertRaises(tkinter.TclError, self.nb.add, None) 959 self.assertRaises(tkinter.TclError, self.nb.add, ttk.Label(self.root), 960 unknown='option') 961 962 tabs = self.nb.tabs() 963 self.nb.hide(self.child1) 964 self.nb.add(self.child1) 965 self.assertEqual(self.nb.tabs(), tabs) 966 967 child = ttk.Label(self.root) 968 self.nb.add(child, text='c') 969 tabs = self.nb.tabs() 970 971 curr = self.nb.index('current') 972 # verify that the tab gets readded at its previous position 973 child2_index = self.nb.index(self.child2) 974 self.nb.hide(self.child2) 975 self.nb.add(self.child2) 976 self.assertEqual(self.nb.tabs(), tabs) 977 self.assertEqual(self.nb.index(self.child2), child2_index) 978 self.assertEqual(str(self.child2), self.nb.tabs()[child2_index]) 979 # but the tab next to it (not hidden) is the one selected now 980 self.assertEqual(self.nb.index('current'), curr + 1) 981 982 983 def test_forget(self): 984 self.assertRaises(tkinter.TclError, self.nb.forget, -1) 985 self.assertRaises(tkinter.TclError, self.nb.forget, 'hi') 986 self.assertRaises(tkinter.TclError, self.nb.forget, None) 987 988 tabs = self.nb.tabs() 989 child1_index = self.nb.index(self.child1) 990 self.nb.forget(self.child1) 991 self.assertNotIn(str(self.child1), self.nb.tabs()) 992 self.assertEqual(len(tabs) - 1, len(self.nb.tabs())) 993 994 self.nb.add(self.child1) 995 self.assertEqual(self.nb.index(self.child1), 1) 996 self.assertNotEqual(child1_index, self.nb.index(self.child1)) 997 998 999 def test_index(self): 1000 self.assertRaises(tkinter.TclError, self.nb.index, -1) 1001 self.assertRaises(tkinter.TclError, self.nb.index, None) 1002 1003 self.assertIsInstance(self.nb.index('end'), int) 1004 self.assertEqual(self.nb.index(self.child1), 0) 1005 self.assertEqual(self.nb.index(self.child2), 1) 1006 self.assertEqual(self.nb.index('end'), 2) 1007 1008 1009 def test_insert(self): 1010 # moving tabs 1011 tabs = self.nb.tabs() 1012 self.nb.insert(1, tabs[0]) 1013 self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0])) 1014 self.nb.insert(self.child1, self.child2) 1015 self.assertEqual(self.nb.tabs(), tabs) 1016 self.nb.insert('end', self.child1) 1017 self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0])) 1018 self.nb.insert('end', 0) 1019 self.assertEqual(self.nb.tabs(), tabs) 1020 # bad moves 1021 self.assertRaises(tkinter.TclError, self.nb.insert, 2, tabs[0]) 1022 self.assertRaises(tkinter.TclError, self.nb.insert, -1, tabs[0]) 1023 1024 # new tab 1025 child3 = ttk.Label(self.root) 1026 self.nb.insert(1, child3) 1027 self.assertEqual(self.nb.tabs(), (tabs[0], str(child3), tabs[1])) 1028 self.nb.forget(child3) 1029 self.assertEqual(self.nb.tabs(), tabs) 1030 self.nb.insert(self.child1, child3) 1031 self.assertEqual(self.nb.tabs(), (str(child3), ) + tabs) 1032 self.nb.forget(child3) 1033 self.assertRaises(tkinter.TclError, self.nb.insert, 2, child3) 1034 self.assertRaises(tkinter.TclError, self.nb.insert, -1, child3) 1035 1036 # bad inserts 1037 self.assertRaises(tkinter.TclError, self.nb.insert, 'end', None) 1038 self.assertRaises(tkinter.TclError, self.nb.insert, None, 0) 1039 self.assertRaises(tkinter.TclError, self.nb.insert, None, None) 1040 1041 1042 def test_select(self): 1043 self.nb.pack() 1044 self.nb.wait_visibility() 1045 1046 success = [] 1047 tab_changed = [] 1048 1049 self.child1.bind('<Unmap>', lambda evt: success.append(True)) 1050 self.nb.bind('<<NotebookTabChanged>>', 1051 lambda evt: tab_changed.append(True)) 1052 1053 self.assertEqual(self.nb.select(), str(self.child1)) 1054 self.nb.select(self.child2) 1055 self.assertTrue(success) 1056 self.assertEqual(self.nb.select(), str(self.child2)) 1057 1058 self.nb.update() 1059 self.assertTrue(tab_changed) 1060 1061 1062 def test_tab(self): 1063 self.assertRaises(tkinter.TclError, self.nb.tab, -1) 1064 self.assertRaises(tkinter.TclError, self.nb.tab, 'notab') 1065 self.assertRaises(tkinter.TclError, self.nb.tab, None) 1066 1067 self.assertIsInstance(self.nb.tab(self.child1), dict) 1068 self.assertEqual(self.nb.tab(self.child1, text=None), 'a') 1069 # newer form for querying a single option 1070 self.assertEqual(self.nb.tab(self.child1, 'text'), 'a') 1071 self.nb.tab(self.child1, text='abc') 1072 self.assertEqual(self.nb.tab(self.child1, text=None), 'abc') 1073 self.assertEqual(self.nb.tab(self.child1, 'text'), 'abc') 1074 1075 1076 def test_tabs(self): 1077 self.assertEqual(len(self.nb.tabs()), 2) 1078 1079 self.nb.forget(self.child1) 1080 self.nb.forget(self.child2) 1081 1082 self.assertEqual(self.nb.tabs(), ()) 1083 1084 1085 def test_traversal(self): 1086 self.nb.pack() 1087 self.nb.wait_visibility() 1088 1089 self.nb.select(0) 1090 1091 simulate_mouse_click(self.nb, 5, 5) 1092 self.nb.focus_force() 1093 self.nb.event_generate('<Control-Tab>') 1094 self.assertEqual(self.nb.select(), str(self.child2)) 1095 self.nb.focus_force() 1096 self.nb.event_generate('<Shift-Control-Tab>') 1097 self.assertEqual(self.nb.select(), str(self.child1)) 1098 self.nb.focus_force() 1099 self.nb.event_generate('<Shift-Control-Tab>') 1100 self.assertEqual(self.nb.select(), str(self.child2)) 1101 1102 self.nb.tab(self.child1, text='a', underline=0) 1103 self.nb.enable_traversal() 1104 self.nb.focus_force() 1105 simulate_mouse_click(self.nb, 5, 5) 1106 if sys.platform == 'darwin': 1107 self.nb.event_generate('<Option-a>') 1108 else: 1109 self.nb.event_generate('<Alt-a>') 1110 self.assertEqual(self.nb.select(), str(self.child1)) 1111 1112@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) 1113class SpinboxTest(EntryTest, unittest.TestCase): 1114 OPTIONS = ( 1115 'background', 'class', 'command', 'cursor', 'exportselection', 1116 'font', 'foreground', 'format', 'from', 'increment', 1117 'invalidcommand', 'justify', 'show', 'state', 'style', 1118 'takefocus', 'textvariable', 'to', 'validate', 'validatecommand', 1119 'values', 'width', 'wrap', 'xscrollcommand', 1120 ) 1121 1122 def setUp(self): 1123 super().setUp() 1124 self.spin = self.create() 1125 self.spin.pack() 1126 1127 def create(self, **kwargs): 1128 return ttk.Spinbox(self.root, **kwargs) 1129 1130 def _click_increment_arrow(self): 1131 width = self.spin.winfo_width() 1132 height = self.spin.winfo_height() 1133 x = width - 5 1134 y = height//2 - 5 1135 self.spin.event_generate('<ButtonPress-1>', x=x, y=y) 1136 self.spin.event_generate('<ButtonRelease-1>', x=x, y=y) 1137 self.spin.update_idletasks() 1138 1139 def _click_decrement_arrow(self): 1140 width = self.spin.winfo_width() 1141 height = self.spin.winfo_height() 1142 x = width - 5 1143 y = height//2 + 4 1144 self.spin.event_generate('<ButtonPress-1>', x=x, y=y) 1145 self.spin.event_generate('<ButtonRelease-1>', x=x, y=y) 1146 self.spin.update_idletasks() 1147 1148 def test_command(self): 1149 success = [] 1150 1151 self.spin['command'] = lambda: success.append(True) 1152 self.spin.update() 1153 self._click_increment_arrow() 1154 self.spin.update() 1155 self.assertTrue(success) 1156 1157 self._click_decrement_arrow() 1158 self.assertEqual(len(success), 2) 1159 1160 # testing postcommand removal 1161 self.spin['command'] = '' 1162 self.spin.update_idletasks() 1163 self._click_increment_arrow() 1164 self._click_decrement_arrow() 1165 self.spin.update() 1166 self.assertEqual(len(success), 2) 1167 1168 def test_to(self): 1169 self.spin['from'] = 0 1170 self.spin['to'] = 5 1171 self.spin.set(4) 1172 self.spin.update() 1173 self._click_increment_arrow() # 5 1174 1175 self.assertEqual(self.spin.get(), '5') 1176 1177 self._click_increment_arrow() # 5 1178 self.assertEqual(self.spin.get(), '5') 1179 1180 def test_from(self): 1181 self.spin['from'] = 1 1182 self.spin['to'] = 10 1183 self.spin.set(2) 1184 self.spin.update() 1185 self._click_decrement_arrow() # 1 1186 self.assertEqual(self.spin.get(), '1') 1187 self._click_decrement_arrow() # 1 1188 self.assertEqual(self.spin.get(), '1') 1189 1190 def test_increment(self): 1191 self.spin['from'] = 0 1192 self.spin['to'] = 10 1193 self.spin['increment'] = 4 1194 self.spin.set(1) 1195 self.spin.update() 1196 1197 self._click_increment_arrow() # 5 1198 self.assertEqual(self.spin.get(), '5') 1199 self.spin['increment'] = 2 1200 self.spin.update() 1201 self._click_decrement_arrow() # 3 1202 self.assertEqual(self.spin.get(), '3') 1203 1204 def test_format(self): 1205 self.spin.set(1) 1206 self.spin['format'] = '%10.3f' 1207 self.spin.update() 1208 self._click_increment_arrow() 1209 value = self.spin.get() 1210 1211 self.assertEqual(len(value), 10) 1212 self.assertEqual(value.index('.'), 6) 1213 1214 self.spin['format'] = '' 1215 self.spin.update() 1216 self._click_increment_arrow() 1217 value = self.spin.get() 1218 self.assertTrue('.' not in value) 1219 self.assertEqual(len(value), 1) 1220 1221 def test_wrap(self): 1222 self.spin['to'] = 10 1223 self.spin['from'] = 1 1224 self.spin.set(1) 1225 self.spin['wrap'] = True 1226 self.spin.update() 1227 1228 self._click_decrement_arrow() 1229 self.assertEqual(self.spin.get(), '10') 1230 1231 self._click_increment_arrow() 1232 self.assertEqual(self.spin.get(), '1') 1233 1234 self.spin['wrap'] = False 1235 self.spin.update() 1236 1237 self._click_decrement_arrow() 1238 self.assertEqual(self.spin.get(), '1') 1239 1240 def test_values(self): 1241 self.assertEqual(self.spin['values'], 1242 () if tcl_version < (8, 5) else '') 1243 self.checkParam(self.spin, 'values', 'mon tue wed thur', 1244 expected=('mon', 'tue', 'wed', 'thur')) 1245 self.checkParam(self.spin, 'values', ('mon', 'tue', 'wed', 'thur')) 1246 self.checkParam(self.spin, 'values', (42, 3.14, '', 'any string')) 1247 self.checkParam(self.spin, 'values', '') 1248 1249 self.spin['values'] = ['a', 1, 'c'] 1250 1251 # test incrementing / decrementing values 1252 self.spin.set('a') 1253 self.spin.update() 1254 self._click_increment_arrow() 1255 self.assertEqual(self.spin.get(), '1') 1256 1257 self._click_decrement_arrow() 1258 self.assertEqual(self.spin.get(), 'a') 1259 1260 # testing values with empty string set through configure 1261 self.spin.configure(values=[1, '', 2]) 1262 self.assertEqual(self.spin['values'], 1263 ('1', '', '2') if self.wantobjects else 1264 '1 {} 2') 1265 1266 # testing values with spaces 1267 self.spin['values'] = ['a b', 'a\tb', 'a\nb'] 1268 self.assertEqual(self.spin['values'], 1269 ('a b', 'a\tb', 'a\nb') if self.wantobjects else 1270 '{a b} {a\tb} {a\nb}') 1271 1272 # testing values with special characters 1273 self.spin['values'] = [r'a\tb', '"a"', '} {'] 1274 self.assertEqual(self.spin['values'], 1275 (r'a\tb', '"a"', '} {') if self.wantobjects else 1276 r'a\\tb {"a"} \}\ \{') 1277 1278 # testing creating spinbox with empty string in values 1279 spin2 = ttk.Spinbox(self.root, values=[1, 2, '']) 1280 self.assertEqual(spin2['values'], 1281 ('1', '2', '') if self.wantobjects else '1 2 {}') 1282 spin2.destroy() 1283 1284 1285@add_standard_options(StandardTtkOptionsTests) 1286class TreeviewTest(AbstractWidgetTest, unittest.TestCase): 1287 OPTIONS = ( 1288 'class', 'columns', 'cursor', 'displaycolumns', 1289 'height', 'padding', 'selectmode', 'show', 1290 'style', 'takefocus', 'xscrollcommand', 'yscrollcommand', 1291 ) 1292 1293 def setUp(self): 1294 super().setUp() 1295 self.tv = self.create(padding=0) 1296 1297 def create(self, **kwargs): 1298 return ttk.Treeview(self.root, **kwargs) 1299 1300 def test_columns(self): 1301 widget = self.create() 1302 self.checkParam(widget, 'columns', 'a b c', 1303 expected=('a', 'b', 'c')) 1304 self.checkParam(widget, 'columns', ('a', 'b', 'c')) 1305 self.checkParam(widget, 'columns', '') 1306 1307 def test_displaycolumns(self): 1308 widget = self.create() 1309 widget['columns'] = ('a', 'b', 'c') 1310 self.checkParam(widget, 'displaycolumns', 'b a c', 1311 expected=('b', 'a', 'c')) 1312 self.checkParam(widget, 'displaycolumns', ('b', 'a', 'c')) 1313 self.checkParam(widget, 'displaycolumns', '#all', 1314 expected=('#all',)) 1315 self.checkParam(widget, 'displaycolumns', (2, 1, 0)) 1316 self.checkInvalidParam(widget, 'displaycolumns', ('a', 'b', 'd'), 1317 errmsg='Invalid column index d') 1318 self.checkInvalidParam(widget, 'displaycolumns', (1, 2, 3), 1319 errmsg='Column index 3 out of bounds') 1320 self.checkInvalidParam(widget, 'displaycolumns', (1, -2), 1321 errmsg='Column index -2 out of bounds') 1322 1323 def test_height(self): 1324 widget = self.create() 1325 self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', conv=False) 1326 self.checkPixelsParam(widget, 'height', 101.2, 102.6, conv=noconv) 1327 1328 def test_selectmode(self): 1329 widget = self.create() 1330 self.checkEnumParam(widget, 'selectmode', 1331 'none', 'browse', 'extended') 1332 1333 def test_show(self): 1334 widget = self.create() 1335 self.checkParam(widget, 'show', 'tree headings', 1336 expected=('tree', 'headings')) 1337 self.checkParam(widget, 'show', ('tree', 'headings')) 1338 self.checkParam(widget, 'show', ('headings', 'tree')) 1339 self.checkParam(widget, 'show', 'tree', expected=('tree',)) 1340 self.checkParam(widget, 'show', 'headings', expected=('headings',)) 1341 1342 def test_bbox(self): 1343 self.tv.pack() 1344 self.assertEqual(self.tv.bbox(''), '') 1345 self.tv.wait_visibility() 1346 self.tv.update() 1347 1348 item_id = self.tv.insert('', 'end') 1349 children = self.tv.get_children() 1350 self.assertTrue(children) 1351 1352 bbox = self.tv.bbox(children[0]) 1353 self.assertIsBoundingBox(bbox) 1354 1355 # compare width in bboxes 1356 self.tv['columns'] = ['test'] 1357 self.tv.column('test', width=50) 1358 bbox_column0 = self.tv.bbox(children[0], 0) 1359 root_width = self.tv.column('#0', width=None) 1360 if not self.wantobjects: 1361 root_width = int(root_width) 1362 self.assertEqual(bbox_column0[0], bbox[0] + root_width) 1363 1364 # verify that bbox of a closed item is the empty string 1365 child1 = self.tv.insert(item_id, 'end') 1366 self.assertEqual(self.tv.bbox(child1), '') 1367 1368 1369 def test_children(self): 1370 # no children yet, should get an empty tuple 1371 self.assertEqual(self.tv.get_children(), ()) 1372 1373 item_id = self.tv.insert('', 'end') 1374 self.assertIsInstance(self.tv.get_children(), tuple) 1375 self.assertEqual(self.tv.get_children()[0], item_id) 1376 1377 # add item_id and child3 as children of child2 1378 child2 = self.tv.insert('', 'end') 1379 child3 = self.tv.insert('', 'end') 1380 self.tv.set_children(child2, item_id, child3) 1381 self.assertEqual(self.tv.get_children(child2), (item_id, child3)) 1382 1383 # child3 has child2 as parent, thus trying to set child2 as a children 1384 # of child3 should result in an error 1385 self.assertRaises(tkinter.TclError, 1386 self.tv.set_children, child3, child2) 1387 1388 # remove child2 children 1389 self.tv.set_children(child2) 1390 self.assertEqual(self.tv.get_children(child2), ()) 1391 1392 # remove root's children 1393 self.tv.set_children('') 1394 self.assertEqual(self.tv.get_children(), ()) 1395 1396 1397 def test_column(self): 1398 # return a dict with all options/values 1399 self.assertIsInstance(self.tv.column('#0'), dict) 1400 # return a single value of the given option 1401 if self.wantobjects: 1402 self.assertIsInstance(self.tv.column('#0', width=None), int) 1403 # set a new value for an option 1404 self.tv.column('#0', width=10) 1405 # testing new way to get option value 1406 self.assertEqual(self.tv.column('#0', 'width'), 1407 10 if self.wantobjects else '10') 1408 self.assertEqual(self.tv.column('#0', width=None), 1409 10 if self.wantobjects else '10') 1410 # check read-only option 1411 self.assertRaises(tkinter.TclError, self.tv.column, '#0', id='X') 1412 1413 self.assertRaises(tkinter.TclError, self.tv.column, 'invalid') 1414 invalid_kws = [ 1415 {'unknown_option': 'some value'}, {'stretch': 'wrong'}, 1416 {'anchor': 'wrong'}, {'width': 'wrong'}, {'minwidth': 'wrong'} 1417 ] 1418 for kw in invalid_kws: 1419 self.assertRaises(tkinter.TclError, self.tv.column, '#0', 1420 **kw) 1421 1422 1423 def test_delete(self): 1424 self.assertRaises(tkinter.TclError, self.tv.delete, '#0') 1425 1426 item_id = self.tv.insert('', 'end') 1427 item2 = self.tv.insert(item_id, 'end') 1428 self.assertEqual(self.tv.get_children(), (item_id, )) 1429 self.assertEqual(self.tv.get_children(item_id), (item2, )) 1430 1431 self.tv.delete(item_id) 1432 self.assertFalse(self.tv.get_children()) 1433 1434 # reattach should fail 1435 self.assertRaises(tkinter.TclError, 1436 self.tv.reattach, item_id, '', 'end') 1437 1438 # test multiple item delete 1439 item1 = self.tv.insert('', 'end') 1440 item2 = self.tv.insert('', 'end') 1441 self.assertEqual(self.tv.get_children(), (item1, item2)) 1442 1443 self.tv.delete(item1, item2) 1444 self.assertFalse(self.tv.get_children()) 1445 1446 1447 def test_detach_reattach(self): 1448 item_id = self.tv.insert('', 'end') 1449 item2 = self.tv.insert(item_id, 'end') 1450 1451 # calling detach without items is valid, although it does nothing 1452 prev = self.tv.get_children() 1453 self.tv.detach() # this should do nothing 1454 self.assertEqual(prev, self.tv.get_children()) 1455 1456 self.assertEqual(self.tv.get_children(), (item_id, )) 1457 self.assertEqual(self.tv.get_children(item_id), (item2, )) 1458 1459 # detach item with children 1460 self.tv.detach(item_id) 1461 self.assertFalse(self.tv.get_children()) 1462 1463 # reattach item with children 1464 self.tv.reattach(item_id, '', 'end') 1465 self.assertEqual(self.tv.get_children(), (item_id, )) 1466 self.assertEqual(self.tv.get_children(item_id), (item2, )) 1467 1468 # move a children to the root 1469 self.tv.move(item2, '', 'end') 1470 self.assertEqual(self.tv.get_children(), (item_id, item2)) 1471 self.assertEqual(self.tv.get_children(item_id), ()) 1472 1473 # bad values 1474 self.assertRaises(tkinter.TclError, 1475 self.tv.reattach, 'nonexistent', '', 'end') 1476 self.assertRaises(tkinter.TclError, 1477 self.tv.detach, 'nonexistent') 1478 self.assertRaises(tkinter.TclError, 1479 self.tv.reattach, item2, 'otherparent', 'end') 1480 self.assertRaises(tkinter.TclError, 1481 self.tv.reattach, item2, '', 'invalid') 1482 1483 # multiple detach 1484 self.tv.detach(item_id, item2) 1485 self.assertEqual(self.tv.get_children(), ()) 1486 self.assertEqual(self.tv.get_children(item_id), ()) 1487 1488 1489 def test_exists(self): 1490 self.assertEqual(self.tv.exists('something'), False) 1491 self.assertEqual(self.tv.exists(''), True) 1492 self.assertEqual(self.tv.exists({}), False) 1493 1494 # the following will make a tk.call equivalent to 1495 # tk.call(treeview, "exists") which should result in an error 1496 # in the tcl interpreter since tk requires an item. 1497 self.assertRaises(tkinter.TclError, self.tv.exists, None) 1498 1499 1500 def test_focus(self): 1501 # nothing is focused right now 1502 self.assertEqual(self.tv.focus(), '') 1503 1504 item1 = self.tv.insert('', 'end') 1505 self.tv.focus(item1) 1506 self.assertEqual(self.tv.focus(), item1) 1507 1508 self.tv.delete(item1) 1509 self.assertEqual(self.tv.focus(), '') 1510 1511 # try focusing inexistent item 1512 self.assertRaises(tkinter.TclError, self.tv.focus, 'hi') 1513 1514 1515 def test_heading(self): 1516 # check a dict is returned 1517 self.assertIsInstance(self.tv.heading('#0'), dict) 1518 1519 # check a value is returned 1520 self.tv.heading('#0', text='hi') 1521 self.assertEqual(self.tv.heading('#0', 'text'), 'hi') 1522 self.assertEqual(self.tv.heading('#0', text=None), 'hi') 1523 1524 # invalid option 1525 self.assertRaises(tkinter.TclError, self.tv.heading, '#0', 1526 background=None) 1527 # invalid value 1528 self.assertRaises(tkinter.TclError, self.tv.heading, '#0', 1529 anchor=1) 1530 1531 def test_heading_callback(self): 1532 def simulate_heading_click(x, y): 1533 simulate_mouse_click(self.tv, x, y) 1534 self.tv.update() 1535 1536 success = [] # no success for now 1537 1538 self.tv.pack() 1539 self.tv.wait_visibility() 1540 self.tv.heading('#0', command=lambda: success.append(True)) 1541 self.tv.column('#0', width=100) 1542 self.tv.update() 1543 1544 # assuming that the coords (5, 5) fall into heading #0 1545 simulate_heading_click(5, 5) 1546 if not success: 1547 self.fail("The command associated to the treeview heading wasn't " 1548 "invoked.") 1549 1550 success = [] 1551 commands = self.tv.master._tclCommands 1552 self.tv.heading('#0', command=str(self.tv.heading('#0', command=None))) 1553 self.assertEqual(commands, self.tv.master._tclCommands) 1554 simulate_heading_click(5, 5) 1555 if not success: 1556 self.fail("The command associated to the treeview heading wasn't " 1557 "invoked.") 1558 1559 # XXX The following raises an error in a tcl interpreter, but not in 1560 # Python 1561 #self.tv.heading('#0', command='I dont exist') 1562 #simulate_heading_click(5, 5) 1563 1564 1565 def test_index(self): 1566 # item 'what' doesn't exist 1567 self.assertRaises(tkinter.TclError, self.tv.index, 'what') 1568 1569 self.assertEqual(self.tv.index(''), 0) 1570 1571 item1 = self.tv.insert('', 'end') 1572 item2 = self.tv.insert('', 'end') 1573 c1 = self.tv.insert(item1, 'end') 1574 c2 = self.tv.insert(item1, 'end') 1575 self.assertEqual(self.tv.index(item1), 0) 1576 self.assertEqual(self.tv.index(c1), 0) 1577 self.assertEqual(self.tv.index(c2), 1) 1578 self.assertEqual(self.tv.index(item2), 1) 1579 1580 self.tv.move(item2, '', 0) 1581 self.assertEqual(self.tv.index(item2), 0) 1582 self.assertEqual(self.tv.index(item1), 1) 1583 1584 # check that index still works even after its parent and siblings 1585 # have been detached 1586 self.tv.detach(item1) 1587 self.assertEqual(self.tv.index(c2), 1) 1588 self.tv.detach(c1) 1589 self.assertEqual(self.tv.index(c2), 0) 1590 1591 # but it fails after item has been deleted 1592 self.tv.delete(item1) 1593 self.assertRaises(tkinter.TclError, self.tv.index, c2) 1594 1595 1596 def test_insert_item(self): 1597 # parent 'none' doesn't exist 1598 self.assertRaises(tkinter.TclError, self.tv.insert, 'none', 'end') 1599 1600 # open values 1601 self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', 1602 open='') 1603 self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', 1604 open='please') 1605 self.assertFalse(self.tv.delete(self.tv.insert('', 'end', open=True))) 1606 self.assertFalse(self.tv.delete(self.tv.insert('', 'end', open=False))) 1607 1608 # invalid index 1609 self.assertRaises(tkinter.TclError, self.tv.insert, '', 'middle') 1610 1611 # trying to duplicate item id is invalid 1612 itemid = self.tv.insert('', 'end', 'first-item') 1613 self.assertEqual(itemid, 'first-item') 1614 self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', 1615 'first-item') 1616 self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', 1617 MockTclObj('first-item')) 1618 1619 # unicode values 1620 value = '\xe1ba' 1621 item = self.tv.insert('', 'end', values=(value, )) 1622 self.assertEqual(self.tv.item(item, 'values'), 1623 (value,) if self.wantobjects else value) 1624 self.assertEqual(self.tv.item(item, values=None), 1625 (value,) if self.wantobjects else value) 1626 1627 self.tv.item(item, values=self.root.splitlist(self.tv.item(item, values=None))) 1628 self.assertEqual(self.tv.item(item, values=None), 1629 (value,) if self.wantobjects else value) 1630 1631 self.assertIsInstance(self.tv.item(item), dict) 1632 1633 # erase item values 1634 self.tv.item(item, values='') 1635 self.assertFalse(self.tv.item(item, values=None)) 1636 1637 # item tags 1638 item = self.tv.insert('', 'end', tags=[1, 2, value]) 1639 self.assertEqual(self.tv.item(item, tags=None), 1640 ('1', '2', value) if self.wantobjects else 1641 '1 2 %s' % value) 1642 self.tv.item(item, tags=[]) 1643 self.assertFalse(self.tv.item(item, tags=None)) 1644 self.tv.item(item, tags=(1, 2)) 1645 self.assertEqual(self.tv.item(item, tags=None), 1646 ('1', '2') if self.wantobjects else '1 2') 1647 1648 # values with spaces 1649 item = self.tv.insert('', 'end', values=('a b c', 1650 '%s %s' % (value, value))) 1651 self.assertEqual(self.tv.item(item, values=None), 1652 ('a b c', '%s %s' % (value, value)) if self.wantobjects else 1653 '{a b c} {%s %s}' % (value, value)) 1654 1655 # text 1656 self.assertEqual(self.tv.item( 1657 self.tv.insert('', 'end', text="Label here"), text=None), 1658 "Label here") 1659 self.assertEqual(self.tv.item( 1660 self.tv.insert('', 'end', text=value), text=None), 1661 value) 1662 1663 # test for values which are not None 1664 itemid = self.tv.insert('', 'end', 0) 1665 self.assertEqual(itemid, '0') 1666 itemid = self.tv.insert('', 'end', 0.0) 1667 self.assertEqual(itemid, '0.0') 1668 # this is because False resolves to 0 and element with 0 iid is already present 1669 self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', False) 1670 self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', '') 1671 1672 1673 def test_selection(self): 1674 self.assertRaises(TypeError, self.tv.selection, 'spam') 1675 # item 'none' doesn't exist 1676 self.assertRaises(tkinter.TclError, self.tv.selection_set, 'none') 1677 self.assertRaises(tkinter.TclError, self.tv.selection_add, 'none') 1678 self.assertRaises(tkinter.TclError, self.tv.selection_remove, 'none') 1679 self.assertRaises(tkinter.TclError, self.tv.selection_toggle, 'none') 1680 1681 item1 = self.tv.insert('', 'end') 1682 item2 = self.tv.insert('', 'end') 1683 c1 = self.tv.insert(item1, 'end') 1684 c2 = self.tv.insert(item1, 'end') 1685 c3 = self.tv.insert(item1, 'end') 1686 self.assertEqual(self.tv.selection(), ()) 1687 1688 self.tv.selection_set(c1, item2) 1689 self.assertEqual(self.tv.selection(), (c1, item2)) 1690 self.tv.selection_set(c2) 1691 self.assertEqual(self.tv.selection(), (c2,)) 1692 1693 self.tv.selection_add(c1, item2) 1694 self.assertEqual(self.tv.selection(), (c1, c2, item2)) 1695 self.tv.selection_add(item1) 1696 self.assertEqual(self.tv.selection(), (item1, c1, c2, item2)) 1697 self.tv.selection_add() 1698 self.assertEqual(self.tv.selection(), (item1, c1, c2, item2)) 1699 1700 self.tv.selection_remove(item1, c3) 1701 self.assertEqual(self.tv.selection(), (c1, c2, item2)) 1702 self.tv.selection_remove(c2) 1703 self.assertEqual(self.tv.selection(), (c1, item2)) 1704 self.tv.selection_remove() 1705 self.assertEqual(self.tv.selection(), (c1, item2)) 1706 1707 self.tv.selection_toggle(c1, c3) 1708 self.assertEqual(self.tv.selection(), (c3, item2)) 1709 self.tv.selection_toggle(item2) 1710 self.assertEqual(self.tv.selection(), (c3,)) 1711 self.tv.selection_toggle() 1712 self.assertEqual(self.tv.selection(), (c3,)) 1713 1714 self.tv.insert('', 'end', id='with spaces') 1715 self.tv.selection_set('with spaces') 1716 self.assertEqual(self.tv.selection(), ('with spaces',)) 1717 1718 self.tv.insert('', 'end', id='{brace') 1719 self.tv.selection_set('{brace') 1720 self.assertEqual(self.tv.selection(), ('{brace',)) 1721 1722 self.tv.insert('', 'end', id='unicode\u20ac') 1723 self.tv.selection_set('unicode\u20ac') 1724 self.assertEqual(self.tv.selection(), ('unicode\u20ac',)) 1725 1726 self.tv.insert('', 'end', id=b'bytes\xe2\x82\xac') 1727 self.tv.selection_set(b'bytes\xe2\x82\xac') 1728 self.assertEqual(self.tv.selection(), ('bytes\xe2\x82\xac',)) 1729 1730 self.tv.selection_set() 1731 self.assertEqual(self.tv.selection(), ()) 1732 1733 # Old interface 1734 self.tv.selection_set((c1, item2)) 1735 self.assertEqual(self.tv.selection(), (c1, item2)) 1736 self.tv.selection_add((c1, item1)) 1737 self.assertEqual(self.tv.selection(), (item1, c1, item2)) 1738 self.tv.selection_remove((item1, c3)) 1739 self.assertEqual(self.tv.selection(), (c1, item2)) 1740 self.tv.selection_toggle((c1, c3)) 1741 self.assertEqual(self.tv.selection(), (c3, item2)) 1742 1743 1744 def test_set(self): 1745 self.tv['columns'] = ['A', 'B'] 1746 item = self.tv.insert('', 'end', values=['a', 'b']) 1747 self.assertEqual(self.tv.set(item), {'A': 'a', 'B': 'b'}) 1748 1749 self.tv.set(item, 'B', 'a') 1750 self.assertEqual(self.tv.item(item, values=None), 1751 ('a', 'a') if self.wantobjects else 'a a') 1752 1753 self.tv['columns'] = ['B'] 1754 self.assertEqual(self.tv.set(item), {'B': 'a'}) 1755 1756 self.tv.set(item, 'B', 'b') 1757 self.assertEqual(self.tv.set(item, column='B'), 'b') 1758 self.assertEqual(self.tv.item(item, values=None), 1759 ('b', 'a') if self.wantobjects else 'b a') 1760 1761 self.tv.set(item, 'B', 123) 1762 self.assertEqual(self.tv.set(item, 'B'), 1763 123 if self.wantobjects else '123') 1764 self.assertEqual(self.tv.item(item, values=None), 1765 (123, 'a') if self.wantobjects else '123 a') 1766 self.assertEqual(self.tv.set(item), 1767 {'B': 123} if self.wantobjects else {'B': '123'}) 1768 1769 # inexistent column 1770 self.assertRaises(tkinter.TclError, self.tv.set, item, 'A') 1771 self.assertRaises(tkinter.TclError, self.tv.set, item, 'A', 'b') 1772 1773 # inexistent item 1774 self.assertRaises(tkinter.TclError, self.tv.set, 'notme') 1775 1776 1777 def test_tag_bind(self): 1778 events = [] 1779 item1 = self.tv.insert('', 'end', tags=['call']) 1780 item2 = self.tv.insert('', 'end', tags=['call']) 1781 self.tv.tag_bind('call', '<ButtonPress-1>', 1782 lambda evt: events.append(1)) 1783 self.tv.tag_bind('call', '<ButtonRelease-1>', 1784 lambda evt: events.append(2)) 1785 1786 self.tv.pack() 1787 self.tv.wait_visibility() 1788 self.tv.update() 1789 1790 pos_y = set() 1791 found = set() 1792 for i in range(0, 100, 10): 1793 if len(found) == 2: # item1 and item2 already found 1794 break 1795 item_id = self.tv.identify_row(i) 1796 if item_id and item_id not in found: 1797 pos_y.add(i) 1798 found.add(item_id) 1799 1800 self.assertEqual(len(pos_y), 2) # item1 and item2 y pos 1801 for y in pos_y: 1802 simulate_mouse_click(self.tv, 0, y) 1803 1804 # by now there should be 4 things in the events list, since each 1805 # item had a bind for two events that were simulated above 1806 self.assertEqual(len(events), 4) 1807 for evt in zip(events[::2], events[1::2]): 1808 self.assertEqual(evt, (1, 2)) 1809 1810 1811 def test_tag_configure(self): 1812 # Just testing parameter passing for now 1813 self.assertRaises(TypeError, self.tv.tag_configure) 1814 self.assertRaises(tkinter.TclError, self.tv.tag_configure, 1815 'test', sky='blue') 1816 self.tv.tag_configure('test', foreground='blue') 1817 self.assertEqual(str(self.tv.tag_configure('test', 'foreground')), 1818 'blue') 1819 self.assertEqual(str(self.tv.tag_configure('test', foreground=None)), 1820 'blue') 1821 self.assertIsInstance(self.tv.tag_configure('test'), dict) 1822 1823 def test_tag_has(self): 1824 item1 = self.tv.insert('', 'end', text='Item 1', tags=['tag1']) 1825 item2 = self.tv.insert('', 'end', text='Item 2', tags=['tag2']) 1826 self.assertRaises(TypeError, self.tv.tag_has) 1827 self.assertRaises(TclError, self.tv.tag_has, 'tag1', 'non-existing') 1828 self.assertTrue(self.tv.tag_has('tag1', item1)) 1829 self.assertFalse(self.tv.tag_has('tag1', item2)) 1830 self.assertFalse(self.tv.tag_has('tag2', item1)) 1831 self.assertTrue(self.tv.tag_has('tag2', item2)) 1832 self.assertFalse(self.tv.tag_has('tag3', item1)) 1833 self.assertFalse(self.tv.tag_has('tag3', item2)) 1834 self.assertEqual(self.tv.tag_has('tag1'), (item1,)) 1835 self.assertEqual(self.tv.tag_has('tag2'), (item2,)) 1836 self.assertEqual(self.tv.tag_has('tag3'), ()) 1837 1838 1839@add_standard_options(StandardTtkOptionsTests) 1840class SeparatorTest(AbstractWidgetTest, unittest.TestCase): 1841 OPTIONS = ( 1842 'class', 'cursor', 'orient', 'style', 'takefocus', 1843 # 'state'? 1844 ) 1845 default_orient = 'horizontal' 1846 1847 def create(self, **kwargs): 1848 return ttk.Separator(self.root, **kwargs) 1849 1850 1851@add_standard_options(StandardTtkOptionsTests) 1852class SizegripTest(AbstractWidgetTest, unittest.TestCase): 1853 OPTIONS = ( 1854 'class', 'cursor', 'style', 'takefocus', 1855 # 'state'? 1856 ) 1857 1858 def create(self, **kwargs): 1859 return ttk.Sizegrip(self.root, **kwargs) 1860 1861tests_gui = ( 1862 ButtonTest, CheckbuttonTest, ComboboxTest, EntryTest, 1863 FrameTest, LabelFrameTest, LabelTest, MenubuttonTest, 1864 NotebookTest, PanedWindowTest, ProgressbarTest, 1865 RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest, 1866 SizegripTest, SpinboxTest, TreeviewTest, WidgetTest, 1867 ) 1868 1869if __name__ == "__main__": 1870 unittest.main() 1871