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