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