Home | History | Annotate | Download | only in test_ttk
      1 import unittest
      2 import Tkinter as tkinter
      3 from Tkinter import TclError
      4 import ttk
      5 from test.test_support import requires, run_unittest, have_unicode, u
      6 import sys
      7 
      8 from test_functions import MockTclObj
      9 from support import (AbstractTkTest, tcl_version, get_tk_patchlevel,
     10                      simulate_mouse_click)
     11 from widget_tests import (add_standard_options, noconv, noconv_meth,
     12     AbstractWidgetTest, StandardOptionsTests,
     13     IntegerSizeTests, PixelSizeTests,
     14     setUpModule)
     15 
     16 requires('gui')
     17 
     18 
     19 class StandardTtkOptionsTests(StandardOptionsTests):
     20 
     21     def test_class(self):
     22         widget = self.create()
     23         self.assertEqual(widget['class'], '')
     24         errmsg='attempt to change read-only option'
     25         if get_tk_patchlevel() < (8, 6, 0, 'beta', 3):
     26             errmsg='Attempt to change read-only option'
     27         self.checkInvalidParam(widget, 'class', 'Foo', errmsg=errmsg)
     28         widget2 = self.create(class_='Foo')
     29         self.assertEqual(widget2['class'], 'Foo')
     30 
     31     def test_padding(self):
     32         widget = self.create()
     33         self.checkParam(widget, 'padding', 0, expected=('0',))
     34         self.checkParam(widget, 'padding', 5, expected=('5',))
     35         self.checkParam(widget, 'padding', (5, 6), expected=('5', '6'))
     36         self.checkParam(widget, 'padding', (5, 6, 7),
     37                         expected=('5', '6', '7'))
     38         self.checkParam(widget, 'padding', (5, 6, 7, 8),
     39                         expected=('5', '6', '7', '8'))
     40         self.checkParam(widget, 'padding', ('5p', '6p', '7p', '8p'))
     41         self.checkParam(widget, 'padding', (), expected='')
     42 
     43     def test_style(self):
     44         widget = self.create()
     45         self.assertEqual(widget['style'], '')
     46         errmsg = 'Layout Foo not found'
     47         if hasattr(self, 'default_orient'):
     48             errmsg = ('Layout %s.Foo not found' %
     49                       getattr(self, 'default_orient').title())
     50         self.checkInvalidParam(widget, 'style', 'Foo',
     51                 errmsg=errmsg)
     52         widget2 = self.create(class_='Foo')
     53         self.assertEqual(widget2['class'], 'Foo')
     54         # XXX
     55         pass
     56 
     57 
     58 class WidgetTest(AbstractTkTest, unittest.TestCase):
     59     """Tests methods available in every ttk widget."""
     60 
     61     def setUp(self):
     62         super(WidgetTest, self).setUp()
     63         self.widget = ttk.Button(self.root, width=0, text="Text")
     64         self.widget.pack()
     65         self.widget.wait_visibility()
     66 
     67 
     68     def test_identify(self):
     69         self.widget.update_idletasks()
     70         self.assertEqual(self.widget.identify(
     71             self.widget.winfo_width() // 2,
     72             self.widget.winfo_height() // 2
     73             ), "label")
     74         self.assertEqual(self.widget.identify(-1, -1), "")
     75 
     76         self.assertRaises(tkinter.TclError, self.widget.identify, None, 5)
     77         self.assertRaises(tkinter.TclError, self.widget.identify, 5, None)
     78         self.assertRaises(tkinter.TclError, self.widget.identify, 5, '')
     79 
     80 
     81     def test_widget_state(self):
     82         # XXX not sure about the portability of all these tests
     83         self.assertEqual(self.widget.state(), ())
     84         self.assertEqual(self.widget.instate(['!disabled']), True)
     85 
     86         # changing from !disabled to disabled
     87         self.assertEqual(self.widget.state(['disabled']), ('!disabled', ))
     88         # no state change
     89         self.assertEqual(self.widget.state(['disabled']), ())
     90         # change back to !disable but also active
     91         self.assertEqual(self.widget.state(['!disabled', 'active']),
     92             ('!active', 'disabled'))
     93         # no state changes, again
     94         self.assertEqual(self.widget.state(['!disabled', 'active']), ())
     95         self.assertEqual(self.widget.state(['active', '!disabled']), ())
     96 
     97         def test_cb(arg1, **kw):
     98             return arg1, kw
     99         self.assertEqual(self.widget.instate(['!disabled'],
    100             test_cb, "hi", **{"msg": "there"}),
    101             ('hi', {'msg': 'there'}))
    102 
    103         # attempt to set invalid statespec
    104         currstate = self.widget.state()
    105         self.assertRaises(tkinter.TclError, self.widget.instate,
    106             ['badstate'])
    107         self.assertRaises(tkinter.TclError, self.widget.instate,
    108             ['disabled', 'badstate'])
    109         # verify that widget didn't change its state
    110         self.assertEqual(currstate, self.widget.state())
    111 
    112         # ensuring that passing None as state doesn't modify current state
    113         self.widget.state(['active', '!disabled'])
    114         self.assertEqual(self.widget.state(), ('active', ))
    115 
    116 
    117 class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
    118     _conv_pixels = noconv_meth
    119 
    120 
    121 @add_standard_options(StandardTtkOptionsTests)
    122 class FrameTest(AbstractToplevelTest, unittest.TestCase):
    123     OPTIONS = (
    124         'borderwidth', 'class', 'cursor', 'height',
    125         'padding', 'relief', 'style', 'takefocus',
    126         'width',
    127     )
    128 
    129     def create(self, **kwargs):
    130         return ttk.Frame(self.root, **kwargs)
    131 
    132 
    133 @add_standard_options(StandardTtkOptionsTests)
    134 class LabelFrameTest(AbstractToplevelTest, unittest.TestCase):
    135     OPTIONS = (
    136         'borderwidth', 'class', 'cursor', 'height',
    137         'labelanchor', 'labelwidget',
    138         'padding', 'relief', 'style', 'takefocus',
    139         'text', 'underline', 'width',
    140     )
    141 
    142     def create(self, **kwargs):
    143         return ttk.LabelFrame(self.root, **kwargs)
    144 
    145     def test_labelanchor(self):
    146         widget = self.create()
    147         self.checkEnumParam(widget, 'labelanchor',
    148                 'e', 'en', 'es', 'n', 'ne', 'nw', 's', 'se', 'sw', 'w', 'wn', 'ws',
    149                 errmsg='Bad label anchor specification {}')
    150         self.checkInvalidParam(widget, 'labelanchor', 'center')
    151 
    152     def test_labelwidget(self):
    153         widget = self.create()
    154         label = ttk.Label(self.root, text='Mupp', name='foo')
    155         self.checkParam(widget, 'labelwidget', label, expected='.foo')
    156         label.destroy()
    157 
    158 
    159 class AbstractLabelTest(AbstractWidgetTest):
    160 
    161     def checkImageParam(self, widget, name):
    162         image = tkinter.PhotoImage(master=self.root, name='image1')
    163         image2 = tkinter.PhotoImage(master=self.root, name='image2')
    164         self.checkParam(widget, name, image, expected=('image1',))
    165         self.checkParam(widget, name, 'image1', expected=('image1',))
    166         self.checkParam(widget, name, (image,), expected=('image1',))
    167         self.checkParam(widget, name, (image, 'active', image2),
    168                         expected=('image1', 'active', 'image2'))
    169         self.checkParam(widget, name, 'image1 active image2',
    170                         expected=('image1', 'active', 'image2'))
    171         self.checkInvalidParam(widget, name, 'spam',
    172                 errmsg='image "spam" doesn\'t exist')
    173 
    174     def test_compound(self):
    175         widget = self.create()
    176         self.checkEnumParam(widget, 'compound',
    177                 'none', 'text', 'image', 'center',
    178                 'top', 'bottom', 'left', 'right')
    179 
    180     def test_state(self):
    181         widget = self.create()
    182         self.checkParams(widget, 'state', 'active', 'disabled', 'normal')
    183 
    184     def test_width(self):
    185         widget = self.create()
    186         self.checkParams(widget, 'width', 402, -402, 0)
    187 
    188 
    189 @add_standard_options(StandardTtkOptionsTests)
    190 class LabelTest(AbstractLabelTest, unittest.TestCase):
    191     OPTIONS = (
    192         'anchor', 'background', 'borderwidth',
    193         'class', 'compound', 'cursor', 'font', 'foreground',
    194         'image', 'justify', 'padding', 'relief', 'state', 'style',
    195         'takefocus', 'text', 'textvariable',
    196         'underline', 'width', 'wraplength',
    197     )
    198     _conv_pixels = noconv_meth
    199 
    200     def create(self, **kwargs):
    201         return ttk.Label(self.root, **kwargs)
    202 
    203     def test_font(self):
    204         widget = self.create()
    205         self.checkParam(widget, 'font',
    206                         '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*')
    207 
    208 
    209 @add_standard_options(StandardTtkOptionsTests)
    210 class ButtonTest(AbstractLabelTest, unittest.TestCase):
    211     OPTIONS = (
    212         'class', 'command', 'compound', 'cursor', 'default',
    213         'image', 'padding', 'state', 'style',
    214         'takefocus', 'text', 'textvariable',
    215         'underline', 'width',
    216     )
    217 
    218     def create(self, **kwargs):
    219         return ttk.Button(self.root, **kwargs)
    220 
    221     def test_default(self):
    222         widget = self.create()
    223         self.checkEnumParam(widget, 'default', 'normal', 'active', 'disabled')
    224 
    225     def test_invoke(self):
    226         success = []
    227         btn = ttk.Button(self.root, command=lambda: success.append(1))
    228         btn.invoke()
    229         self.assertTrue(success)
    230 
    231 
    232 @add_standard_options(StandardTtkOptionsTests)
    233 class CheckbuttonTest(AbstractLabelTest, unittest.TestCase):
    234     OPTIONS = (
    235         'class', 'command', 'compound', 'cursor',
    236         'image',
    237         'offvalue', 'onvalue',
    238         'padding', 'state', 'style',
    239         'takefocus', 'text', 'textvariable',
    240         'underline', 'variable', 'width',
    241     )
    242 
    243     def create(self, **kwargs):
    244         return ttk.Checkbutton(self.root, **kwargs)
    245 
    246     def test_offvalue(self):
    247         widget = self.create()
    248         self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string')
    249 
    250     def test_onvalue(self):
    251         widget = self.create()
    252         self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string')
    253 
    254     def test_invoke(self):
    255         success = []
    256         def cb_test():
    257             success.append(1)
    258             return "cb test called"
    259 
    260         cbtn = ttk.Checkbutton(self.root, command=cb_test)
    261         # the variable automatically created by ttk.Checkbutton is actually
    262         # undefined till we invoke the Checkbutton
    263         self.assertEqual(cbtn.state(), ('alternate', ))
    264         self.assertRaises(tkinter.TclError, cbtn.tk.globalgetvar,
    265             cbtn['variable'])
    266 
    267         res = cbtn.invoke()
    268         self.assertEqual(res, "cb test called")
    269         self.assertEqual(cbtn['onvalue'],
    270             cbtn.tk.globalgetvar(cbtn['variable']))
    271         self.assertTrue(success)
    272 
    273         cbtn['command'] = ''
    274         res = cbtn.invoke()
    275         self.assertFalse(str(res))
    276         self.assertLessEqual(len(success), 1)
    277         self.assertEqual(cbtn['offvalue'],
    278             cbtn.tk.globalgetvar(cbtn['variable']))
    279 
    280 
    281 @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
    282 class EntryTest(AbstractWidgetTest, unittest.TestCase):
    283     OPTIONS = (
    284         'background', 'class', 'cursor',
    285         'exportselection', 'font', 'foreground',
    286         'invalidcommand', 'justify',
    287         'show', 'state', 'style', 'takefocus', 'textvariable',
    288         'validate', 'validatecommand', 'width', 'xscrollcommand',
    289     )
    290 
    291     def setUp(self):
    292         super(EntryTest, self).setUp()
    293         self.entry = self.create()
    294 
    295     def create(self, **kwargs):
    296         return ttk.Entry(self.root, **kwargs)
    297 
    298     def test_invalidcommand(self):
    299         widget = self.create()
    300         self.checkCommandParam(widget, 'invalidcommand')
    301 
    302     def test_show(self):
    303         widget = self.create()
    304         self.checkParam(widget, 'show', '*')
    305         self.checkParam(widget, 'show', '')
    306         self.checkParam(widget, 'show', ' ')
    307 
    308     def test_state(self):
    309         widget = self.create()
    310         self.checkParams(widget, 'state',
    311                          'disabled', 'normal', 'readonly')
    312 
    313     def test_validate(self):
    314         widget = self.create()
    315         self.checkEnumParam(widget, 'validate',
    316                 'all', 'key', 'focus', 'focusin', 'focusout', 'none')
    317 
    318     def test_validatecommand(self):
    319         widget = self.create()
    320         self.checkCommandParam(widget, 'validatecommand')
    321 
    322 
    323     def test_bbox(self):
    324         self.assertIsBoundingBox(self.entry.bbox(0))
    325         self.assertRaises(tkinter.TclError, self.entry.bbox, 'noindex')
    326         self.assertRaises(tkinter.TclError, self.entry.bbox, None)
    327 
    328 
    329     def test_identify(self):
    330         self.entry.pack()
    331         self.entry.wait_visibility()
    332         self.entry.update_idletasks()
    333 
    334         self.assertEqual(self.entry.identify(5, 5), "textarea")
    335         self.assertEqual(self.entry.identify(-1, -1), "")
    336 
    337         self.assertRaises(tkinter.TclError, self.entry.identify, None, 5)
    338         self.assertRaises(tkinter.TclError, self.entry.identify, 5, None)
    339         self.assertRaises(tkinter.TclError, self.entry.identify, 5, '')
    340 
    341 
    342     def test_validation_options(self):
    343         success = []
    344         test_invalid = lambda: success.append(True)
    345 
    346         self.entry['validate'] = 'none'
    347         self.entry['validatecommand'] = lambda: False
    348 
    349         self.entry['invalidcommand'] = test_invalid
    350         self.entry.validate()
    351         self.assertTrue(success)
    352 
    353         self.entry['invalidcommand'] = ''
    354         self.entry.validate()
    355         self.assertEqual(len(success), 1)
    356 
    357         self.entry['invalidcommand'] = test_invalid
    358         self.entry['validatecommand'] = lambda: True
    359         self.entry.validate()
    360         self.assertEqual(len(success), 1)
    361 
    362         self.entry['validatecommand'] = ''
    363         self.entry.validate()
    364         self.assertEqual(len(success), 1)
    365 
    366         self.entry['validatecommand'] = True
    367         self.assertRaises(tkinter.TclError, self.entry.validate)
    368 
    369 
    370     def test_validation(self):
    371         validation = []
    372         def validate(to_insert):
    373             if not 'a' <= to_insert.lower() <= 'z':
    374                 validation.append(False)
    375                 return False
    376             validation.append(True)
    377             return True
    378 
    379         self.entry['validate'] = 'key'
    380         self.entry['validatecommand'] = self.entry.register(validate), '%S'
    381 
    382         self.entry.insert('end', 1)
    383         self.entry.insert('end', 'a')
    384         self.assertEqual(validation, [False, True])
    385         self.assertEqual(self.entry.get(), 'a')
    386 
    387 
    388     def test_revalidation(self):
    389         def validate(content):
    390             for letter in content:
    391                 if not 'a' <= letter.lower() <= 'z':
    392                     return False
    393             return True
    394 
    395         self.entry['validatecommand'] = self.entry.register(validate), '%P'
    396 
    397         self.entry.insert('end', 'avocado')
    398         self.assertEqual(self.entry.validate(), True)
    399         self.assertEqual(self.entry.state(), ())
    400 
    401         self.entry.delete(0, 'end')
    402         self.assertEqual(self.entry.get(), '')
    403 
    404         self.entry.insert('end', 'a1b')
    405         self.assertEqual(self.entry.validate(), False)
    406         self.assertEqual(self.entry.state(), ('invalid', ))
    407 
    408         self.entry.delete(1)
    409         self.assertEqual(self.entry.validate(), True)
    410         self.assertEqual(self.entry.state(), ())
    411 
    412 
    413 @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
    414 class ComboboxTest(EntryTest, unittest.TestCase):
    415     OPTIONS = (
    416         'background', 'class', 'cursor', 'exportselection',
    417         'font', 'foreground', 'height', 'invalidcommand',
    418         'justify', 'postcommand', 'show', 'state', 'style',
    419         'takefocus', 'textvariable',
    420         'validate', 'validatecommand', 'values',
    421         'width', 'xscrollcommand',
    422     )
    423 
    424     def setUp(self):
    425         super(ComboboxTest, self).setUp()
    426         self.combo = self.create()
    427 
    428     def create(self, **kwargs):
    429         return ttk.Combobox(self.root, **kwargs)
    430 
    431     def test_height(self):
    432         widget = self.create()
    433         self.checkParams(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i')
    434 
    435     def _show_drop_down_listbox(self):
    436         width = self.combo.winfo_width()
    437         self.combo.event_generate('<ButtonPress-1>', x=width - 5, y=5)
    438         self.combo.event_generate('<ButtonRelease-1>', x=width - 5, y=5)
    439         self.combo.update_idletasks()
    440 
    441 
    442     def test_virtual_event(self):
    443         success = []
    444 
    445         self.combo['values'] = [1]
    446         self.combo.bind('<<ComboboxSelected>>',
    447             lambda evt: success.append(True))
    448         self.combo.pack()
    449         self.combo.wait_visibility()
    450 
    451         height = self.combo.winfo_height()
    452         self._show_drop_down_listbox()
    453         self.combo.update()
    454         self.combo.event_generate('<Return>')
    455         self.combo.update()
    456 
    457         self.assertTrue(success)
    458 
    459 
    460     def test_postcommand(self):
    461         success = []
    462 
    463         self.combo['postcommand'] = lambda: success.append(True)
    464         self.combo.pack()
    465         self.combo.wait_visibility()
    466 
    467         self._show_drop_down_listbox()
    468         self.assertTrue(success)
    469 
    470         # testing postcommand removal
    471         self.combo['postcommand'] = ''
    472         self._show_drop_down_listbox()
    473         self.assertEqual(len(success), 1)
    474 
    475 
    476     def test_values(self):
    477         def check_get_current(getval, currval):
    478             self.assertEqual(self.combo.get(), getval)
    479             self.assertEqual(self.combo.current(), currval)
    480 
    481         self.assertEqual(self.combo['values'],
    482                          () if tcl_version < (8, 5) else '')
    483         check_get_current('', -1)
    484 
    485         self.checkParam(self.combo, 'values', 'mon tue wed thur',
    486                         expected=('mon', 'tue', 'wed', 'thur'))
    487         self.checkParam(self.combo, 'values', ('mon', 'tue', 'wed', 'thur'))
    488         self.checkParam(self.combo, 'values', (42, 3.14, '', 'any string'))
    489         self.checkParam(self.combo, 'values', () if tcl_version < (8, 5) else '')
    490 
    491         self.combo['values'] = ['a', 1, 'c']
    492 
    493         self.combo.set('c')
    494         check_get_current('c', 2)
    495 
    496         self.combo.current(0)
    497         check_get_current('a', 0)
    498 
    499         self.combo.set('d')
    500         check_get_current('d', -1)
    501 
    502         # testing values with empty string
    503         self.combo.set('')
    504         self.combo['values'] = (1, 2, '', 3)
    505         check_get_current('', 2)
    506 
    507         # testing values with empty string set through configure
    508         self.combo.configure(values=[1, '', 2])
    509         self.assertEqual(self.combo['values'],
    510                          ('1', '', '2') if self.wantobjects else
    511                          '1 {} 2')
    512 
    513         # testing values with spaces
    514         self.combo['values'] = ['a b', 'a\tb', 'a\nb']
    515         self.assertEqual(self.combo['values'],
    516                          ('a b', 'a\tb', 'a\nb') if self.wantobjects else
    517                          '{a b} {a\tb} {a\nb}')
    518 
    519         # testing values with special characters
    520         self.combo['values'] = [r'a\tb', '"a"', '} {']
    521         self.assertEqual(self.combo['values'],
    522                          (r'a\tb', '"a"', '} {') if self.wantobjects else
    523                          r'a\\tb {"a"} \}\ \{')
    524 
    525         # out of range
    526         self.assertRaises(tkinter.TclError, self.combo.current,
    527             len(self.combo['values']))
    528         # it expects an integer (or something that can be converted to int)
    529         self.assertRaises(tkinter.TclError, self.combo.current, '')
    530 
    531         # testing creating combobox with empty string in values
    532         combo2 = ttk.Combobox(self.root, values=[1, 2, ''])
    533         self.assertEqual(combo2['values'],
    534                          ('1', '2', '') if self.wantobjects else '1 2 {}')
    535         combo2.destroy()
    536 
    537 
    538 @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
    539 class PanedWindowTest(AbstractWidgetTest, unittest.TestCase):
    540     OPTIONS = (
    541         'class', 'cursor', 'height',
    542         'orient', 'style', 'takefocus', 'width',
    543     )
    544 
    545     def setUp(self):
    546         super(PanedWindowTest, self).setUp()
    547         self.paned = self.create()
    548 
    549     def create(self, **kwargs):
    550         return ttk.PanedWindow(self.root, **kwargs)
    551 
    552     def test_orient(self):
    553         widget = self.create()
    554         self.assertEqual(str(widget['orient']), 'vertical')
    555         errmsg='attempt to change read-only option'
    556         if get_tk_patchlevel() < (8, 6, 0, 'beta', 3):
    557             errmsg='Attempt to change read-only option'
    558         self.checkInvalidParam(widget, 'orient', 'horizontal',
    559                 errmsg=errmsg)
    560         widget2 = self.create(orient='horizontal')
    561         self.assertEqual(str(widget2['orient']), 'horizontal')
    562 
    563     def test_add(self):
    564         # attempt to add a child that is not a direct child of the paned window
    565         label = ttk.Label(self.paned)
    566         child = ttk.Label(label)
    567         self.assertRaises(tkinter.TclError, self.paned.add, child)
    568         label.destroy()
    569         child.destroy()
    570         # another attempt
    571         label = ttk.Label(self.root)
    572         child = ttk.Label(label)
    573         self.assertRaises(tkinter.TclError, self.paned.add, child)
    574         child.destroy()
    575         label.destroy()
    576 
    577         good_child = ttk.Label(self.root)
    578         self.paned.add(good_child)
    579         # re-adding a child is not accepted
    580         self.assertRaises(tkinter.TclError, self.paned.add, good_child)
    581 
    582         other_child = ttk.Label(self.paned)
    583         self.paned.add(other_child)
    584         self.assertEqual(self.paned.pane(0), self.paned.pane(1))
    585         self.assertRaises(tkinter.TclError, self.paned.pane, 2)
    586         good_child.destroy()
    587         other_child.destroy()
    588         self.assertRaises(tkinter.TclError, self.paned.pane, 0)
    589 
    590 
    591     def test_forget(self):
    592         self.assertRaises(tkinter.TclError, self.paned.forget, None)
    593         self.assertRaises(tkinter.TclError, self.paned.forget, 0)
    594 
    595         self.paned.add(ttk.Label(self.root))
    596         self.paned.forget(0)
    597         self.assertRaises(tkinter.TclError, self.paned.forget, 0)
    598 
    599 
    600     def test_insert(self):
    601         self.assertRaises(tkinter.TclError, self.paned.insert, None, 0)
    602         self.assertRaises(tkinter.TclError, self.paned.insert, 0, None)
    603         self.assertRaises(tkinter.TclError, self.paned.insert, 0, 0)
    604 
    605         child = ttk.Label(self.root)
    606         child2 = ttk.Label(self.root)
    607         child3 = ttk.Label(self.root)
    608 
    609         self.assertRaises(tkinter.TclError, self.paned.insert, 0, child)
    610 
    611         self.paned.insert('end', child2)
    612         self.paned.insert(0, child)
    613         self.assertEqual(self.paned.panes(), (str(child), str(child2)))
    614 
    615         self.paned.insert(0, child2)
    616         self.assertEqual(self.paned.panes(), (str(child2), str(child)))
    617 
    618         self.paned.insert('end', child3)
    619         self.assertEqual(self.paned.panes(),
    620             (str(child2), str(child), str(child3)))
    621 
    622         # reinserting a child should move it to its current position
    623         panes = self.paned.panes()
    624         self.paned.insert('end', child3)
    625         self.assertEqual(panes, self.paned.panes())
    626 
    627         # moving child3 to child2 position should result in child2 ending up
    628         # in previous child position and child ending up in previous child3
    629         # position
    630         self.paned.insert(child2, child3)
    631         self.assertEqual(self.paned.panes(),
    632             (str(child3), str(child2), str(child)))
    633 
    634 
    635     def test_pane(self):
    636         self.assertRaises(tkinter.TclError, self.paned.pane, 0)
    637 
    638         child = ttk.Label(self.root)
    639         self.paned.add(child)
    640         self.assertIsInstance(self.paned.pane(0), dict)
    641         self.assertEqual(self.paned.pane(0, weight=None),
    642                          0 if self.wantobjects else '0')
    643         # newer form for querying a single option
    644         self.assertEqual(self.paned.pane(0, 'weight'),
    645                          0 if self.wantobjects else '0')
    646         self.assertEqual(self.paned.pane(0), self.paned.pane(str(child)))
    647 
    648         self.assertRaises(tkinter.TclError, self.paned.pane, 0,
    649             badoption='somevalue')
    650 
    651 
    652     def test_sashpos(self):
    653         self.assertRaises(tkinter.TclError, self.paned.sashpos, None)
    654         self.assertRaises(tkinter.TclError, self.paned.sashpos, '')
    655         self.assertRaises(tkinter.TclError, self.paned.sashpos, 0)
    656 
    657         child = ttk.Label(self.paned, text='a')
    658         self.paned.add(child, weight=1)
    659         self.assertRaises(tkinter.TclError, self.paned.sashpos, 0)
    660         child2 = ttk.Label(self.paned, text='b')
    661         self.paned.add(child2)
    662         self.assertRaises(tkinter.TclError, self.paned.sashpos, 1)
    663 
    664         self.paned.pack(expand=True, fill='both')
    665         self.paned.wait_visibility()
    666 
    667         curr_pos = self.paned.sashpos(0)
    668         self.paned.sashpos(0, 1000)
    669         self.assertNotEqual(curr_pos, self.paned.sashpos(0))
    670         self.assertIsInstance(self.paned.sashpos(0), int)
    671 
    672 
    673 @add_standard_options(StandardTtkOptionsTests)
    674 class RadiobuttonTest(AbstractLabelTest, unittest.TestCase):
    675     OPTIONS = (
    676         'class', 'command', 'compound', 'cursor',
    677         'image',
    678         'padding', 'state', 'style',
    679         'takefocus', 'text', 'textvariable',
    680         'underline', 'value', 'variable', 'width',
    681     )
    682 
    683     def create(self, **kwargs):
    684         return ttk.Radiobutton(self.root, **kwargs)
    685 
    686     def test_value(self):
    687         widget = self.create()
    688         self.checkParams(widget, 'value', 1, 2.3, '', 'any string')
    689 
    690     def test_invoke(self):
    691         success = []
    692         def cb_test():
    693             success.append(1)
    694             return "cb test called"
    695 
    696         myvar = tkinter.IntVar(self.root)
    697         cbtn = ttk.Radiobutton(self.root, command=cb_test,
    698                                variable=myvar, value=0)
    699         cbtn2 = ttk.Radiobutton(self.root, command=cb_test,
    700                                 variable=myvar, value=1)
    701 
    702         if self.wantobjects:
    703             conv = lambda x: x
    704         else:
    705             conv = int
    706 
    707         res = cbtn.invoke()
    708         self.assertEqual(res, "cb test called")
    709         self.assertEqual(conv(cbtn['value']), myvar.get())
    710         self.assertEqual(myvar.get(),
    711             conv(cbtn.tk.globalgetvar(cbtn['variable'])))
    712         self.assertTrue(success)
    713 
    714         cbtn2['command'] = ''
    715         res = cbtn2.invoke()
    716         self.assertEqual(str(res), '')
    717         self.assertLessEqual(len(success), 1)
    718         self.assertEqual(conv(cbtn2['value']), myvar.get())
    719         self.assertEqual(myvar.get(),
    720             conv(cbtn.tk.globalgetvar(cbtn['variable'])))
    721 
    722         self.assertEqual(str(cbtn['variable']), str(cbtn2['variable']))
    723 
    724 
    725 class MenubuttonTest(AbstractLabelTest, unittest.TestCase):
    726     OPTIONS = (
    727         'class', 'compound', 'cursor', 'direction',
    728         'image', 'menu', 'padding', 'state', 'style',
    729         'takefocus', 'text', 'textvariable',
    730         'underline', 'width',
    731     )
    732 
    733     def create(self, **kwargs):
    734         return ttk.Menubutton(self.root, **kwargs)
    735 
    736     def test_direction(self):
    737         widget = self.create()
    738         self.checkEnumParam(widget, 'direction',
    739                 'above', 'below', 'left', 'right', 'flush')
    740 
    741     def test_menu(self):
    742         widget = self.create()
    743         menu = tkinter.Menu(widget, name='menu')
    744         self.checkParam(widget, 'menu', menu, conv=str)
    745         menu.destroy()
    746 
    747 
    748 @add_standard_options(StandardTtkOptionsTests)
    749 class ScaleTest(AbstractWidgetTest, unittest.TestCase):
    750     OPTIONS = (
    751         'class', 'command', 'cursor', 'from', 'length',
    752         'orient', 'style', 'takefocus', 'to', 'value', 'variable',
    753     )
    754     _conv_pixels = noconv_meth
    755     default_orient = 'horizontal'
    756 
    757     def setUp(self):
    758         super(ScaleTest, self).setUp()
    759         self.scale = self.create()
    760         self.scale.pack()
    761         self.scale.update()
    762 
    763     def create(self, **kwargs):
    764         return ttk.Scale(self.root, **kwargs)
    765 
    766     def test_from(self):
    767         widget = self.create()
    768         self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=False)
    769 
    770     def test_length(self):
    771         widget = self.create()
    772         self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i')
    773 
    774     def test_to(self):
    775         widget = self.create()
    776         self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, conv=False)
    777 
    778     def test_value(self):
    779         widget = self.create()
    780         self.checkFloatParam(widget, 'value', 300, 14.9, 15.1, -10, conv=False)
    781 
    782     def test_custom_event(self):
    783         failure = [1, 1, 1] # will need to be empty
    784 
    785         funcid = self.scale.bind('<<RangeChanged>>', lambda evt: failure.pop())
    786 
    787         self.scale['from'] = 10
    788         self.scale['from_'] = 10
    789         self.scale['to'] = 3
    790 
    791         self.assertFalse(failure)
    792 
    793         failure = [1, 1, 1]
    794         self.scale.configure(from_=2, to=5)
    795         self.scale.configure(from_=0, to=-2)
    796         self.scale.configure(to=10)
    797 
    798         self.assertFalse(failure)
    799 
    800 
    801     def test_get(self):
    802         if self.wantobjects:
    803             conv = lambda x: x
    804         else:
    805             conv = float
    806 
    807         scale_width = self.scale.winfo_width()
    808         self.assertEqual(self.scale.get(scale_width, 0), self.scale['to'])
    809 
    810         self.assertEqual(conv(self.scale.get(0, 0)), conv(self.scale['from']))
    811         self.assertEqual(self.scale.get(), self.scale['value'])
    812         self.scale['value'] = 30
    813         self.assertEqual(self.scale.get(), self.scale['value'])
    814 
    815         self.assertRaises(tkinter.TclError, self.scale.get, '', 0)
    816         self.assertRaises(tkinter.TclError, self.scale.get, 0, '')
    817 
    818 
    819     def test_set(self):
    820         if self.wantobjects:
    821             conv = lambda x: x
    822         else:
    823             conv = float
    824 
    825         # set restricts the max/min values according to the current range
    826         max = conv(self.scale['to'])
    827         new_max = max + 10
    828         self.scale.set(new_max)
    829         self.assertEqual(conv(self.scale.get()), max)
    830         min = conv(self.scale['from'])
    831         self.scale.set(min - 1)
    832         self.assertEqual(conv(self.scale.get()), min)
    833 
    834         # changing directly the variable doesn't impose this limitation tho
    835         var = tkinter.DoubleVar(self.root)
    836         self.scale['variable'] = var
    837         var.set(max + 5)
    838         self.assertEqual(conv(self.scale.get()), var.get())
    839         self.assertEqual(conv(self.scale.get()), max + 5)
    840         del var
    841 
    842         # the same happens with the value option
    843         self.scale['value'] = max + 10
    844         self.assertEqual(conv(self.scale.get()), max + 10)
    845         self.assertEqual(conv(self.scale.get()), conv(self.scale['value']))
    846 
    847         # nevertheless, note that the max/min values we can get specifying
    848         # x, y coords are the ones according to the current range
    849         self.assertEqual(conv(self.scale.get(0, 0)), min)
    850         self.assertEqual(conv(self.scale.get(self.scale.winfo_width(), 0)), max)
    851 
    852         self.assertRaises(tkinter.TclError, self.scale.set, None)
    853 
    854 
    855 @add_standard_options(StandardTtkOptionsTests)
    856 class ProgressbarTest(AbstractWidgetTest, unittest.TestCase):
    857     OPTIONS = (
    858         'class', 'cursor', 'orient', 'length',
    859         'mode', 'maximum', 'phase',
    860         'style', 'takefocus', 'value', 'variable',
    861     )
    862     _conv_pixels = noconv_meth
    863     default_orient = 'horizontal'
    864 
    865     def create(self, **kwargs):
    866         return ttk.Progressbar(self.root, **kwargs)
    867 
    868     def test_length(self):
    869         widget = self.create()
    870         self.checkPixelsParam(widget, 'length', 100.1, 56.7, '2i')
    871 
    872     def test_maximum(self):
    873         widget = self.create()
    874         self.checkFloatParam(widget, 'maximum', 150.2, 77.7, 0, -10, conv=False)
    875 
    876     def test_mode(self):
    877         widget = self.create()
    878         self.checkEnumParam(widget, 'mode', 'determinate', 'indeterminate')
    879 
    880     def test_phase(self):
    881         # XXX
    882         pass
    883 
    884     def test_value(self):
    885         widget = self.create()
    886         self.checkFloatParam(widget, 'value', 150.2, 77.7, 0, -10,
    887                              conv=False)
    888 
    889 
    890 @unittest.skipIf(sys.platform == 'darwin',
    891                  'ttk.Scrollbar is special on MacOSX')
    892 @add_standard_options(StandardTtkOptionsTests)
    893 class ScrollbarTest(AbstractWidgetTest, unittest.TestCase):
    894     OPTIONS = (
    895         'class', 'command', 'cursor', 'orient', 'style', 'takefocus',
    896     )
    897     default_orient = 'vertical'
    898 
    899     def create(self, **kwargs):
    900         return ttk.Scrollbar(self.root, **kwargs)
    901 
    902 
    903 @add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
    904 class NotebookTest(AbstractWidgetTest, unittest.TestCase):
    905     OPTIONS = (
    906         'class', 'cursor', 'height', 'padding', 'style', 'takefocus', 'width',
    907     )
    908 
    909     def setUp(self):
    910         super(NotebookTest, self).setUp()
    911         self.nb = self.create(padding=0)
    912         self.child1 = ttk.Label(self.root)
    913         self.child2 = ttk.Label(self.root)
    914         self.nb.add(self.child1, text='a')
    915         self.nb.add(self.child2, text='b')
    916 
    917     def create(self, **kwargs):
    918         return ttk.Notebook(self.root, **kwargs)
    919 
    920     def test_tab_identifiers(self):
    921         self.nb.forget(0)
    922         self.nb.hide(self.child2)
    923         self.assertRaises(tkinter.TclError, self.nb.tab, self.child1)
    924         self.assertEqual(self.nb.index('end'), 1)
    925         self.nb.add(self.child2)
    926         self.assertEqual(self.nb.index('end'), 1)
    927         self.nb.select(self.child2)
    928 
    929         self.assertTrue(self.nb.tab('current'))
    930         self.nb.add(self.child1, text='a')
    931 
    932         self.nb.pack()
    933         self.nb.wait_visibility()
    934         if sys.platform == 'darwin':
    935             tb_idx = "@20,5"
    936         else:
    937             tb_idx = "@5,5"
    938         self.assertEqual(self.nb.tab(tb_idx), self.nb.tab('current'))
    939 
    940         for i in range(5, 100, 5):
    941             try:
    942                 if self.nb.tab('@%d, 5' % i, text=None) == 'a':
    943                     break
    944             except tkinter.TclError:
    945                 pass
    946 
    947         else:
    948             self.fail("Tab with text 'a' not found")
    949 
    950 
    951     def test_add_and_hidden(self):
    952         self.assertRaises(tkinter.TclError, self.nb.hide, -1)
    953         self.assertRaises(tkinter.TclError, self.nb.hide, 'hi')
    954         self.assertRaises(tkinter.TclError, self.nb.hide, None)
    955         self.assertRaises(tkinter.TclError, self.nb.add, None)
    956         self.assertRaises(tkinter.TclError, self.nb.add, ttk.Label(self.root),
    957             unknown='option')
    958 
    959         tabs = self.nb.tabs()
    960         self.nb.hide(self.child1)
    961         self.nb.add(self.child1)
    962         self.assertEqual(self.nb.tabs(), tabs)
    963 
    964         child = ttk.Label(self.root)
    965         self.nb.add(child, text='c')
    966         tabs = self.nb.tabs()
    967 
    968         curr = self.nb.index('current')
    969         # verify that the tab gets readded at its previous position
    970         child2_index = self.nb.index(self.child2)
    971         self.nb.hide(self.child2)
    972         self.nb.add(self.child2)
    973         self.assertEqual(self.nb.tabs(), tabs)
    974         self.assertEqual(self.nb.index(self.child2), child2_index)
    975         self.assertEqual(str(self.child2), self.nb.tabs()[child2_index])
    976         # but the tab next to it (not hidden) is the one selected now
    977         self.assertEqual(self.nb.index('current'), curr + 1)
    978 
    979 
    980     def test_forget(self):
    981         self.assertRaises(tkinter.TclError, self.nb.forget, -1)
    982         self.assertRaises(tkinter.TclError, self.nb.forget, 'hi')
    983         self.assertRaises(tkinter.TclError, self.nb.forget, None)
    984 
    985         tabs = self.nb.tabs()
    986         child1_index = self.nb.index(self.child1)
    987         self.nb.forget(self.child1)
    988         self.assertNotIn(str(self.child1), self.nb.tabs())
    989         self.assertEqual(len(tabs) - 1, len(self.nb.tabs()))
    990 
    991         self.nb.add(self.child1)
    992         self.assertEqual(self.nb.index(self.child1), 1)
    993         self.assertNotEqual(child1_index, self.nb.index(self.child1))
    994 
    995 
    996     def test_index(self):
    997         self.assertRaises(tkinter.TclError, self.nb.index, -1)
    998         self.assertRaises(tkinter.TclError, self.nb.index, None)
    999 
   1000         self.assertIsInstance(self.nb.index('end'), int)
   1001         self.assertEqual(self.nb.index(self.child1), 0)
   1002         self.assertEqual(self.nb.index(self.child2), 1)
   1003         self.assertEqual(self.nb.index('end'), 2)
   1004 
   1005 
   1006     def test_insert(self):
   1007         # moving tabs
   1008         tabs = self.nb.tabs()
   1009         self.nb.insert(1, tabs[0])
   1010         self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0]))
   1011         self.nb.insert(self.child1, self.child2)
   1012         self.assertEqual(self.nb.tabs(), tabs)
   1013         self.nb.insert('end', self.child1)
   1014         self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0]))
   1015         self.nb.insert('end', 0)
   1016         self.assertEqual(self.nb.tabs(), tabs)
   1017         # bad moves
   1018         self.assertRaises(tkinter.TclError, self.nb.insert, 2, tabs[0])
   1019         self.assertRaises(tkinter.TclError, self.nb.insert, -1, tabs[0])
   1020 
   1021         # new tab
   1022         child3 = ttk.Label(self.root)
   1023         self.nb.insert(1, child3)
   1024         self.assertEqual(self.nb.tabs(), (tabs[0], str(child3), tabs[1]))
   1025         self.nb.forget(child3)
   1026         self.assertEqual(self.nb.tabs(), tabs)
   1027         self.nb.insert(self.child1, child3)
   1028         self.assertEqual(self.nb.tabs(), (str(child3), ) + tabs)
   1029         self.nb.forget(child3)
   1030         self.assertRaises(tkinter.TclError, self.nb.insert, 2, child3)
   1031         self.assertRaises(tkinter.TclError, self.nb.insert, -1, child3)
   1032 
   1033         # bad inserts
   1034         self.assertRaises(tkinter.TclError, self.nb.insert, 'end', None)
   1035         self.assertRaises(tkinter.TclError, self.nb.insert, None, 0)
   1036         self.assertRaises(tkinter.TclError, self.nb.insert, None, None)
   1037 
   1038 
   1039     def test_select(self):
   1040         self.nb.pack()
   1041         self.nb.wait_visibility()
   1042 
   1043         success = []
   1044         tab_changed = []
   1045 
   1046         self.child1.bind('<Unmap>', lambda evt: success.append(True))
   1047         self.nb.bind('<<NotebookTabChanged>>',
   1048             lambda evt: tab_changed.append(True))
   1049 
   1050         self.assertEqual(self.nb.select(), str(self.child1))
   1051         self.nb.select(self.child2)
   1052         self.assertTrue(success)
   1053         self.assertEqual(self.nb.select(), str(self.child2))
   1054 
   1055         self.nb.update()
   1056         self.assertTrue(tab_changed)
   1057 
   1058 
   1059     def test_tab(self):
   1060         self.assertRaises(tkinter.TclError, self.nb.tab, -1)
   1061         self.assertRaises(tkinter.TclError, self.nb.tab, 'notab')
   1062         self.assertRaises(tkinter.TclError, self.nb.tab, None)
   1063 
   1064         self.assertIsInstance(self.nb.tab(self.child1), dict)
   1065         self.assertEqual(self.nb.tab(self.child1, text=None), 'a')
   1066         # newer form for querying a single option
   1067         self.assertEqual(self.nb.tab(self.child1, 'text'), 'a')
   1068         self.nb.tab(self.child1, text='abc')
   1069         self.assertEqual(self.nb.tab(self.child1, text=None), 'abc')
   1070         self.assertEqual(self.nb.tab(self.child1, 'text'), 'abc')
   1071 
   1072 
   1073     def test_tabs(self):
   1074         self.assertEqual(len(self.nb.tabs()), 2)
   1075 
   1076         self.nb.forget(self.child1)
   1077         self.nb.forget(self.child2)
   1078 
   1079         self.assertEqual(self.nb.tabs(), ())
   1080 
   1081 
   1082     def test_traversal(self):
   1083         self.nb.pack()
   1084         self.nb.wait_visibility()
   1085 
   1086         self.nb.select(0)
   1087 
   1088         simulate_mouse_click(self.nb, 5, 5)
   1089         self.nb.focus_force()
   1090         self.nb.event_generate('<Control-Tab>')
   1091         self.assertEqual(self.nb.select(), str(self.child2))
   1092         self.nb.focus_force()
   1093         self.nb.event_generate('<Shift-Control-Tab>')
   1094         self.assertEqual(self.nb.select(), str(self.child1))
   1095         self.nb.focus_force()
   1096         self.nb.event_generate('<Shift-Control-Tab>')
   1097         self.assertEqual(self.nb.select(), str(self.child2))
   1098 
   1099         self.nb.tab(self.child1, text='a', underline=0)
   1100         self.nb.enable_traversal()
   1101         self.nb.focus_force()
   1102         simulate_mouse_click(self.nb, 5, 5)
   1103         if sys.platform == 'darwin':
   1104             self.nb.event_generate('<Option-a>')
   1105         else:
   1106             self.nb.event_generate('<Alt-a>')
   1107         self.assertEqual(self.nb.select(), str(self.child1))
   1108 
   1109 
   1110 @add_standard_options(StandardTtkOptionsTests)
   1111 class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
   1112     OPTIONS = (
   1113         'class', 'columns', 'cursor', 'displaycolumns',
   1114         'height', 'padding', 'selectmode', 'show',
   1115         'style', 'takefocus', 'xscrollcommand', 'yscrollcommand',
   1116     )
   1117 
   1118     def setUp(self):
   1119         super(TreeviewTest, self).setUp()
   1120         self.tv = self.create(padding=0)
   1121 
   1122     def create(self, **kwargs):
   1123         return ttk.Treeview(self.root, **kwargs)
   1124 
   1125     def test_columns(self):
   1126         widget = self.create()
   1127         self.checkParam(widget, 'columns', 'a b c',
   1128                         expected=('a', 'b', 'c'))
   1129         self.checkParam(widget, 'columns', ('a', 'b', 'c'))
   1130         self.checkParam(widget, 'columns', () if tcl_version < (8, 5) 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 = u'\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         # item 'none' doesn't exist
   1491         self.assertRaises(tkinter.TclError, self.tv.selection_set, 'none')
   1492         self.assertRaises(tkinter.TclError, self.tv.selection_add, 'none')
   1493         self.assertRaises(tkinter.TclError, self.tv.selection_remove, 'none')
   1494         self.assertRaises(tkinter.TclError, self.tv.selection_toggle, 'none')
   1495 
   1496         item1 = self.tv.insert('', 'end')
   1497         item2 = self.tv.insert('', 'end')
   1498         c1 = self.tv.insert(item1, 'end')
   1499         c2 = self.tv.insert(item1, 'end')
   1500         c3 = self.tv.insert(item1, 'end')
   1501         self.assertEqual(self.tv.selection(), ())
   1502 
   1503         self.tv.selection_set((c1, item2))
   1504         self.assertEqual(self.tv.selection(), (c1, item2))
   1505         self.tv.selection_set(c2)
   1506         self.assertEqual(self.tv.selection(), (c2,))
   1507 
   1508         self.tv.selection_add((c1, item2))
   1509         self.assertEqual(self.tv.selection(), (c1, c2, item2))
   1510         self.tv.selection_add(item1)
   1511         self.assertEqual(self.tv.selection(), (item1, c1, c2, item2))
   1512 
   1513         self.tv.selection_remove((item1, c3))
   1514         self.assertEqual(self.tv.selection(), (c1, c2, item2))
   1515         self.tv.selection_remove(c2)
   1516         self.assertEqual(self.tv.selection(), (c1, item2))
   1517 
   1518         self.tv.selection_toggle((c1, c3))
   1519         self.assertEqual(self.tv.selection(), (c3, item2))
   1520         self.tv.selection_toggle(item2)
   1521         self.assertEqual(self.tv.selection(), (c3,))
   1522 
   1523         self.tv.insert('', 'end', id='with spaces')
   1524         self.tv.selection_set('with spaces')
   1525         self.assertEqual(self.tv.selection(), ('with spaces',))
   1526 
   1527         self.tv.insert('', 'end', id='{brace')
   1528         self.tv.selection_set('{brace')
   1529         self.assertEqual(self.tv.selection(), ('{brace',))
   1530 
   1531         if have_unicode:
   1532             self.tv.insert('', 'end', id=u(r'unicode\u20ac'))
   1533             self.tv.selection_set(u(r'unicode\u20ac'))
   1534             self.assertEqual(self.tv.selection(), (u(r'unicode\u20ac'),))
   1535 
   1536         self.tv.insert('', 'end', id='bytes\xe2\x82\xac')
   1537         self.tv.selection_set('bytes\xe2\x82\xac')
   1538         self.assertEqual(self.tv.selection(),
   1539                          (u(r'bytes\u20ac') if have_unicode else
   1540                           'bytes\xe2\x82\xac',))
   1541 
   1542 
   1543     def test_set(self):
   1544         self.tv['columns'] = ['A', 'B']
   1545         item = self.tv.insert('', 'end', values=['a', 'b'])
   1546         self.assertEqual(self.tv.set(item), {'A': 'a', 'B': 'b'})
   1547 
   1548         self.tv.set(item, 'B', 'a')
   1549         self.assertEqual(self.tv.item(item, values=None),
   1550                          ('a', 'a') if self.wantobjects else 'a a')
   1551 
   1552         self.tv['columns'] = ['B']
   1553         self.assertEqual(self.tv.set(item), {'B': 'a'})
   1554 
   1555         self.tv.set(item, 'B', 'b')
   1556         self.assertEqual(self.tv.set(item, column='B'), 'b')
   1557         self.assertEqual(self.tv.item(item, values=None),
   1558                          ('b', 'a') if self.wantobjects else 'b a')
   1559 
   1560         self.tv.set(item, 'B', 123)
   1561         self.assertEqual(self.tv.set(item, 'B'),
   1562                          123 if self.wantobjects else '123')
   1563         self.assertEqual(self.tv.item(item, values=None),
   1564                          (123, 'a') if self.wantobjects else '123 a')
   1565         self.assertEqual(self.tv.set(item),
   1566                          {'B': 123} if self.wantobjects else {'B': '123'})
   1567 
   1568         # inexistent column
   1569         self.assertRaises(tkinter.TclError, self.tv.set, item, 'A')
   1570         self.assertRaises(tkinter.TclError, self.tv.set, item, 'A', 'b')
   1571 
   1572         # inexistent item
   1573         self.assertRaises(tkinter.TclError, self.tv.set, 'notme')
   1574 
   1575 
   1576     def test_tag_bind(self):
   1577         events = []
   1578         item1 = self.tv.insert('', 'end', tags=['call'])
   1579         item2 = self.tv.insert('', 'end', tags=['call'])
   1580         self.tv.tag_bind('call', '<ButtonPress-1>',
   1581             lambda evt: events.append(1))
   1582         self.tv.tag_bind('call', '<ButtonRelease-1>',
   1583             lambda evt: events.append(2))
   1584 
   1585         self.tv.pack()
   1586         self.tv.wait_visibility()
   1587         self.tv.update()
   1588 
   1589         pos_y = set()
   1590         found = set()
   1591         for i in range(0, 100, 10):
   1592             if len(found) == 2: # item1 and item2 already found
   1593                 break
   1594             item_id = self.tv.identify_row(i)
   1595             if item_id and item_id not in found:
   1596                 pos_y.add(i)
   1597                 found.add(item_id)
   1598 
   1599         self.assertEqual(len(pos_y), 2) # item1 and item2 y pos
   1600         for y in pos_y:
   1601             simulate_mouse_click(self.tv, 0, y)
   1602 
   1603         # by now there should be 4 things in the events list, since each
   1604         # item had a bind for two events that were simulated above
   1605         self.assertEqual(len(events), 4)
   1606         for evt in zip(events[::2], events[1::2]):
   1607             self.assertEqual(evt, (1, 2))
   1608 
   1609 
   1610     def test_tag_configure(self):
   1611         # Just testing parameter passing for now
   1612         self.assertRaises(TypeError, self.tv.tag_configure)
   1613         self.assertRaises(tkinter.TclError, self.tv.tag_configure,
   1614             'test', sky='blue')
   1615         self.tv.tag_configure('test', foreground='blue')
   1616         self.assertEqual(str(self.tv.tag_configure('test', 'foreground')),
   1617             'blue')
   1618         self.assertEqual(str(self.tv.tag_configure('test', foreground=None)),
   1619             'blue')
   1620         self.assertIsInstance(self.tv.tag_configure('test'), dict)
   1621 
   1622     def test_tag_has(self):
   1623         item1 = self.tv.insert('', 'end', text='Item 1', tags=['tag1'])
   1624         item2 = self.tv.insert('', 'end', text='Item 2', tags=['tag2'])
   1625         self.assertRaises(TypeError, self.tv.tag_has)
   1626         self.assertRaises(TclError, self.tv.tag_has, 'tag1', 'non-existing')
   1627         self.assertTrue(self.tv.tag_has('tag1', item1))
   1628         self.assertFalse(self.tv.tag_has('tag1', item2))
   1629         self.assertFalse(self.tv.tag_has('tag2', item1))
   1630         self.assertTrue(self.tv.tag_has('tag2', item2))
   1631         self.assertFalse(self.tv.tag_has('tag3', item1))
   1632         self.assertFalse(self.tv.tag_has('tag3', item2))
   1633         self.assertEqual(self.tv.tag_has('tag1'), (item1,))
   1634         self.assertEqual(self.tv.tag_has('tag2'), (item2,))
   1635         self.assertEqual(self.tv.tag_has('tag3'), ())
   1636 
   1637 
   1638 @add_standard_options(StandardTtkOptionsTests)
   1639 class SeparatorTest(AbstractWidgetTest, unittest.TestCase):
   1640     OPTIONS = (
   1641         'class', 'cursor', 'orient', 'style', 'takefocus',
   1642         # 'state'?
   1643     )
   1644     default_orient = 'horizontal'
   1645 
   1646     def create(self, **kwargs):
   1647         return ttk.Separator(self.root, **kwargs)
   1648 
   1649 
   1650 @add_standard_options(StandardTtkOptionsTests)
   1651 class SizegripTest(AbstractWidgetTest, unittest.TestCase):
   1652     OPTIONS = (
   1653         'class', 'cursor', 'style', 'takefocus',
   1654         # 'state'?
   1655     )
   1656 
   1657     def create(self, **kwargs):
   1658         return ttk.Sizegrip(self.root, **kwargs)
   1659 
   1660 
   1661 tests_gui = (
   1662         ButtonTest, CheckbuttonTest, ComboboxTest, EntryTest,
   1663         FrameTest, LabelFrameTest, LabelTest, MenubuttonTest,
   1664         NotebookTest, PanedWindowTest, ProgressbarTest,
   1665         RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest,
   1666         SizegripTest, TreeviewTest, WidgetTest,
   1667         )
   1668 
   1669 tests_gui = (
   1670         TreeviewTest,
   1671         )
   1672 
   1673 if __name__ == "__main__":
   1674     run_unittest(*tests_gui)
   1675