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