• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import unittest
2import Tkinter as tkinter
3from Tkinter import TclError
4import ttk
5from test.test_support import requires, run_unittest, have_unicode, u
6import sys
7
8from test_functions import MockTclObj
9from support import (AbstractTkTest, tcl_version, get_tk_patchlevel,
10                     simulate_mouse_click)
11from widget_tests import (add_standard_options, noconv, noconv_meth,
12    AbstractWidgetTest, StandardOptionsTests,
13    IntegerSizeTests, PixelSizeTests,
14    setUpModule)
15
16requires('gui')
17
18
19class 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
58class 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
117class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
118    _conv_pixels = noconv_meth
119
120
121@add_standard_options(StandardTtkOptionsTests)
122class 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)
134class 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
159class 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)
190class 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)
210class 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)
233class 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)
282class 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)
414class 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)
539class 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)
674class 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
725class 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)
749class 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)
856class 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)
893class 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)
904class 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)
1111class 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        # test for values which are not None
1489        itemid = self.tv.insert('', 'end', 0)
1490        self.assertEqual(itemid, '0')
1491        itemid = self.tv.insert('', 'end', 0.0)
1492        self.assertEqual(itemid, '0.0')
1493        # this is because False resolves to 0 and element with 0 iid is already present
1494        self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', False)
1495        self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', '')
1496
1497
1498    def test_selection(self):
1499        # item 'none' doesn't exist
1500        self.assertRaises(tkinter.TclError, self.tv.selection_set, 'none')
1501        self.assertRaises(tkinter.TclError, self.tv.selection_add, 'none')
1502        self.assertRaises(tkinter.TclError, self.tv.selection_remove, 'none')
1503        self.assertRaises(tkinter.TclError, self.tv.selection_toggle, 'none')
1504
1505        item1 = self.tv.insert('', 'end')
1506        item2 = self.tv.insert('', 'end')
1507        c1 = self.tv.insert(item1, 'end')
1508        c2 = self.tv.insert(item1, 'end')
1509        c3 = self.tv.insert(item1, 'end')
1510        self.assertEqual(self.tv.selection(), ())
1511
1512        self.tv.selection_set((c1, item2))
1513        self.assertEqual(self.tv.selection(), (c1, item2))
1514        self.tv.selection_set(c2)
1515        self.assertEqual(self.tv.selection(), (c2,))
1516
1517        self.tv.selection_add((c1, item2))
1518        self.assertEqual(self.tv.selection(), (c1, c2, item2))
1519        self.tv.selection_add(item1)
1520        self.assertEqual(self.tv.selection(), (item1, c1, c2, item2))
1521
1522        self.tv.selection_remove((item1, c3))
1523        self.assertEqual(self.tv.selection(), (c1, c2, item2))
1524        self.tv.selection_remove(c2)
1525        self.assertEqual(self.tv.selection(), (c1, item2))
1526
1527        self.tv.selection_toggle((c1, c3))
1528        self.assertEqual(self.tv.selection(), (c3, item2))
1529        self.tv.selection_toggle(item2)
1530        self.assertEqual(self.tv.selection(), (c3,))
1531
1532        self.tv.insert('', 'end', id='with spaces')
1533        self.tv.selection_set('with spaces')
1534        self.assertEqual(self.tv.selection(), ('with spaces',))
1535
1536        self.tv.insert('', 'end', id='{brace')
1537        self.tv.selection_set('{brace')
1538        self.assertEqual(self.tv.selection(), ('{brace',))
1539
1540        if have_unicode:
1541            self.tv.insert('', 'end', id=u(r'unicode\u20ac'))
1542            self.tv.selection_set(u(r'unicode\u20ac'))
1543            self.assertEqual(self.tv.selection(), (u(r'unicode\u20ac'),))
1544
1545        self.tv.insert('', 'end', id='bytes\xe2\x82\xac')
1546        self.tv.selection_set('bytes\xe2\x82\xac')
1547        self.assertEqual(self.tv.selection(),
1548                         (u(r'bytes\u20ac') if have_unicode else
1549                          'bytes\xe2\x82\xac',))
1550
1551
1552    def test_set(self):
1553        self.tv['columns'] = ['A', 'B']
1554        item = self.tv.insert('', 'end', values=['a', 'b'])
1555        self.assertEqual(self.tv.set(item), {'A': 'a', 'B': 'b'})
1556
1557        self.tv.set(item, 'B', 'a')
1558        self.assertEqual(self.tv.item(item, values=None),
1559                         ('a', 'a') if self.wantobjects else 'a a')
1560
1561        self.tv['columns'] = ['B']
1562        self.assertEqual(self.tv.set(item), {'B': 'a'})
1563
1564        self.tv.set(item, 'B', 'b')
1565        self.assertEqual(self.tv.set(item, column='B'), 'b')
1566        self.assertEqual(self.tv.item(item, values=None),
1567                         ('b', 'a') if self.wantobjects else 'b a')
1568
1569        self.tv.set(item, 'B', 123)
1570        self.assertEqual(self.tv.set(item, 'B'),
1571                         123 if self.wantobjects else '123')
1572        self.assertEqual(self.tv.item(item, values=None),
1573                         (123, 'a') if self.wantobjects else '123 a')
1574        self.assertEqual(self.tv.set(item),
1575                         {'B': 123} if self.wantobjects else {'B': '123'})
1576
1577        # inexistent column
1578        self.assertRaises(tkinter.TclError, self.tv.set, item, 'A')
1579        self.assertRaises(tkinter.TclError, self.tv.set, item, 'A', 'b')
1580
1581        # inexistent item
1582        self.assertRaises(tkinter.TclError, self.tv.set, 'notme')
1583
1584
1585    def test_tag_bind(self):
1586        events = []
1587        item1 = self.tv.insert('', 'end', tags=['call'])
1588        item2 = self.tv.insert('', 'end', tags=['call'])
1589        self.tv.tag_bind('call', '<ButtonPress-1>',
1590            lambda evt: events.append(1))
1591        self.tv.tag_bind('call', '<ButtonRelease-1>',
1592            lambda evt: events.append(2))
1593
1594        self.tv.pack()
1595        self.tv.wait_visibility()
1596        self.tv.update()
1597
1598        pos_y = set()
1599        found = set()
1600        for i in range(0, 100, 10):
1601            if len(found) == 2: # item1 and item2 already found
1602                break
1603            item_id = self.tv.identify_row(i)
1604            if item_id and item_id not in found:
1605                pos_y.add(i)
1606                found.add(item_id)
1607
1608        self.assertEqual(len(pos_y), 2) # item1 and item2 y pos
1609        for y in pos_y:
1610            simulate_mouse_click(self.tv, 0, y)
1611
1612        # by now there should be 4 things in the events list, since each
1613        # item had a bind for two events that were simulated above
1614        self.assertEqual(len(events), 4)
1615        for evt in zip(events[::2], events[1::2]):
1616            self.assertEqual(evt, (1, 2))
1617
1618
1619    def test_tag_configure(self):
1620        # Just testing parameter passing for now
1621        self.assertRaises(TypeError, self.tv.tag_configure)
1622        self.assertRaises(tkinter.TclError, self.tv.tag_configure,
1623            'test', sky='blue')
1624        self.tv.tag_configure('test', foreground='blue')
1625        self.assertEqual(str(self.tv.tag_configure('test', 'foreground')),
1626            'blue')
1627        self.assertEqual(str(self.tv.tag_configure('test', foreground=None)),
1628            'blue')
1629        self.assertIsInstance(self.tv.tag_configure('test'), dict)
1630
1631    def test_tag_has(self):
1632        item1 = self.tv.insert('', 'end', text='Item 1', tags=['tag1'])
1633        item2 = self.tv.insert('', 'end', text='Item 2', tags=['tag2'])
1634        self.assertRaises(TypeError, self.tv.tag_has)
1635        self.assertRaises(TclError, self.tv.tag_has, 'tag1', 'non-existing')
1636        self.assertTrue(self.tv.tag_has('tag1', item1))
1637        self.assertFalse(self.tv.tag_has('tag1', item2))
1638        self.assertFalse(self.tv.tag_has('tag2', item1))
1639        self.assertTrue(self.tv.tag_has('tag2', item2))
1640        self.assertFalse(self.tv.tag_has('tag3', item1))
1641        self.assertFalse(self.tv.tag_has('tag3', item2))
1642        self.assertEqual(self.tv.tag_has('tag1'), (item1,))
1643        self.assertEqual(self.tv.tag_has('tag2'), (item2,))
1644        self.assertEqual(self.tv.tag_has('tag3'), ())
1645
1646
1647@add_standard_options(StandardTtkOptionsTests)
1648class SeparatorTest(AbstractWidgetTest, unittest.TestCase):
1649    OPTIONS = (
1650        'class', 'cursor', 'orient', 'style', 'takefocus',
1651        # 'state'?
1652    )
1653    default_orient = 'horizontal'
1654
1655    def create(self, **kwargs):
1656        return ttk.Separator(self.root, **kwargs)
1657
1658
1659@add_standard_options(StandardTtkOptionsTests)
1660class SizegripTest(AbstractWidgetTest, unittest.TestCase):
1661    OPTIONS = (
1662        'class', 'cursor', 'style', 'takefocus',
1663        # 'state'?
1664    )
1665
1666    def create(self, **kwargs):
1667        return ttk.Sizegrip(self.root, **kwargs)
1668
1669
1670tests_gui = (
1671        ButtonTest, CheckbuttonTest, ComboboxTest, EntryTest,
1672        FrameTest, LabelFrameTest, LabelTest, MenubuttonTest,
1673        NotebookTest, PanedWindowTest, ProgressbarTest,
1674        RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest,
1675        SizegripTest, TreeviewTest, WidgetTest,
1676        )
1677
1678tests_gui = (
1679        TreeviewTest,
1680        )
1681
1682if __name__ == "__main__":
1683    run_unittest(*tests_gui)
1684