• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import unittest
2import tkinter
3from tkinter import TclError
4import os
5from test.support import requires
6
7from test.test_tkinter.support import (requires_tk, tk_version,
8                                  get_tk_patchlevel, widget_eq,
9                                  AbstractDefaultRootTest)
10from test.test_tkinter.widget_tests import (
11    add_standard_options,
12    AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests)
13
14requires('gui')
15
16
17EXPECTED_SCREEN_DISTANCE_ERRMSG = '(bad|expected) screen distance (but got )?"{}"'
18EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG = '(bad|expected) screen distance (or "" but got )?"{}"'
19
20def float_round(x):
21    return float(round(x))
22
23
24class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
25    _conv_pad_pixels = False
26
27    def test_configure_class(self):
28        widget = self.create()
29        self.assertEqual(widget['class'],
30                         widget.__class__.__name__.title())
31        self.checkInvalidParam(widget, 'class', 'Foo',
32                errmsg="can't modify -class option after widget is created")
33        widget2 = self.create(class_='Foo')
34        self.assertEqual(widget2['class'], 'Foo')
35
36    def test_configure_colormap(self):
37        widget = self.create()
38        self.assertEqual(widget['colormap'], '')
39        self.checkInvalidParam(widget, 'colormap', 'new',
40                errmsg="can't modify -colormap option after widget is created")
41        widget2 = self.create(colormap='new')
42        self.assertEqual(widget2['colormap'], 'new')
43
44    def test_configure_container(self):
45        widget = self.create()
46        self.assertEqual(widget['container'], 0 if self.wantobjects else '0')
47        self.checkInvalidParam(widget, 'container', 1,
48                errmsg="can't modify -container option after widget is created")
49        widget2 = self.create(container=True)
50        self.assertEqual(widget2['container'], 1 if self.wantobjects else '1')
51
52    def test_configure_visual(self):
53        widget = self.create()
54        self.assertEqual(widget['visual'], '')
55        self.checkInvalidParam(widget, 'visual', 'default',
56                errmsg="can't modify -visual option after widget is created")
57        widget2 = self.create(visual='default')
58        self.assertEqual(widget2['visual'], 'default')
59
60
61@add_standard_options(StandardOptionsTests)
62class ToplevelTest(AbstractToplevelTest, unittest.TestCase):
63    OPTIONS = (
64        'background', 'backgroundimage', 'borderwidth',
65        'class', 'colormap', 'container', 'cursor', 'height',
66        'highlightbackground', 'highlightcolor', 'highlightthickness',
67        'menu', 'padx', 'pady', 'relief', 'screen',
68        'takefocus', 'tile', 'use', 'visual', 'width',
69    )
70
71    def create(self, **kwargs):
72        return tkinter.Toplevel(self.root, **kwargs)
73
74    def test_configure_menu(self):
75        widget = self.create()
76        menu = tkinter.Menu(self.root)
77        self.checkParam(widget, 'menu', menu, eq=widget_eq)
78        self.checkParam(widget, 'menu', '')
79
80    def test_configure_screen(self):
81        widget = self.create()
82        if widget._windowingsystem != 'x11':
83            self.skipTest('Not using Tk for X11')
84        self.assertEqual(widget['screen'], '')
85        try:
86            display = os.environ['DISPLAY']
87        except KeyError:
88            self.skipTest('No $DISPLAY set.')
89        self.checkInvalidParam(widget, 'screen', display,
90                errmsg="can't modify -screen option after widget is created")
91        widget2 = self.create(screen=display)
92        self.assertEqual(widget2['screen'], display)
93
94    def test_configure_use(self):
95        widget = self.create()
96        self.assertEqual(widget['use'], '')
97        parent = self.create(container=True)
98        wid = hex(parent.winfo_id())
99        with self.subTest(wid=wid):
100            widget2 = self.create(use=wid)
101            self.assertEqual(widget2['use'], wid)
102
103
104@add_standard_options(StandardOptionsTests)
105class FrameTest(AbstractToplevelTest, unittest.TestCase):
106    OPTIONS = (
107        'background', 'backgroundimage', 'borderwidth',
108        'class', 'colormap', 'container', 'cursor', 'height',
109        'highlightbackground', 'highlightcolor', 'highlightthickness',
110        'padx', 'pady', 'relief', 'takefocus', 'tile', 'visual', 'width',
111    )
112
113    def create(self, **kwargs):
114        return tkinter.Frame(self.root, **kwargs)
115
116
117@add_standard_options(StandardOptionsTests)
118class LabelFrameTest(AbstractToplevelTest, unittest.TestCase):
119    OPTIONS = (
120        'background', 'borderwidth',
121        'class', 'colormap', 'container', 'cursor',
122        'font', 'foreground', 'height',
123        'highlightbackground', 'highlightcolor', 'highlightthickness',
124        'labelanchor', 'labelwidget', 'padx', 'pady', 'relief',
125        'takefocus', 'text', 'visual', 'width',
126    )
127
128    def create(self, **kwargs):
129        return tkinter.LabelFrame(self.root, **kwargs)
130
131    def test_configure_labelanchor(self):
132        widget = self.create()
133        self.checkEnumParam(widget, 'labelanchor',
134                            'e', 'en', 'es', 'n', 'ne', 'nw',
135                            's', 'se', 'sw', 'w', 'wn', 'ws')
136        self.checkInvalidParam(widget, 'labelanchor', 'center')
137
138    def test_configure_labelwidget(self):
139        widget = self.create()
140        label = tkinter.Label(self.root, text='Mupp', name='foo')
141        self.checkParam(widget, 'labelwidget', label, expected='.foo')
142        label.destroy()
143
144
145class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests):
146    _conv_pixels = False
147    _clip_highlightthickness = tk_version >= (8, 7)
148    _clip_pad = tk_version >= (8, 7)
149    _clip_borderwidth = tk_version >= (8, 7)
150
151
152@add_standard_options(StandardOptionsTests)
153class LabelTest(AbstractLabelTest, unittest.TestCase):
154    OPTIONS = (
155        'activebackground', 'activeforeground', 'anchor',
156        'background', 'bitmap', 'borderwidth', 'compound', 'cursor',
157        'disabledforeground', 'font', 'foreground', 'height',
158        'highlightbackground', 'highlightcolor', 'highlightthickness',
159        'image', 'justify', 'padx', 'pady', 'relief', 'state',
160        'takefocus', 'text', 'textvariable',
161        'underline', 'width', 'wraplength',
162    )
163
164    def create(self, **kwargs):
165        return tkinter.Label(self.root, **kwargs)
166
167
168@add_standard_options(StandardOptionsTests)
169class ButtonTest(AbstractLabelTest, unittest.TestCase):
170    OPTIONS = (
171        'activebackground', 'activeforeground', 'anchor',
172        'background', 'bitmap', 'borderwidth',
173        'command', 'compound', 'cursor', 'default',
174        'disabledforeground', 'font', 'foreground', 'height',
175        'highlightbackground', 'highlightcolor', 'highlightthickness',
176        'image', 'justify', 'overrelief', 'padx', 'pady', 'relief',
177        'repeatdelay', 'repeatinterval',
178        'state', 'takefocus', 'text', 'textvariable',
179        'underline', 'width', 'wraplength')
180
181    def create(self, **kwargs):
182        return tkinter.Button(self.root, **kwargs)
183
184    def test_configure_default(self):
185        widget = self.create()
186        self.checkEnumParam(widget, 'default', 'active', 'disabled', 'normal')
187
188
189@add_standard_options(StandardOptionsTests)
190class CheckbuttonTest(AbstractLabelTest, unittest.TestCase):
191    OPTIONS = (
192        'activebackground', 'activeforeground', 'anchor',
193        'background', 'bitmap', 'borderwidth',
194        'command', 'compound', 'cursor',
195        'disabledforeground', 'font', 'foreground', 'height',
196        'highlightbackground', 'highlightcolor', 'highlightthickness',
197        'image', 'indicatoron', 'justify',
198        'offrelief', 'offvalue', 'onvalue', 'overrelief',
199        'padx', 'pady', 'relief', 'selectcolor', 'selectimage', 'state',
200        'takefocus', 'text', 'textvariable',
201        'tristateimage', 'tristatevalue',
202        'underline', 'variable', 'width', 'wraplength',
203    )
204
205    def create(self, **kwargs):
206        return tkinter.Checkbutton(self.root, **kwargs)
207
208
209    def test_configure_offvalue(self):
210        widget = self.create()
211        self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string')
212
213    def test_configure_onvalue(self):
214        widget = self.create()
215        self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string')
216
217    def test_unique_variables(self):
218        frames = []
219        buttons = []
220        for i in range(2):
221            f = tkinter.Frame(self.root)
222            f.pack()
223            frames.append(f)
224            for j in 'AB':
225                b = tkinter.Checkbutton(f, text=j)
226                b.pack()
227                buttons.append(b)
228        variables = [str(b['variable']) for b in buttons]
229        self.assertEqual(len(set(variables)), 4, variables)
230
231    def test_same_name(self):
232        f1 = tkinter.Frame(self.root)
233        f2 = tkinter.Frame(self.root)
234        b1 = tkinter.Checkbutton(f1, name='test', text='Test1')
235        b2 = tkinter.Checkbutton(f2, name='test', text='Test2')
236
237        v = tkinter.IntVar(self.root, name='test')
238        b1.select()
239        self.assertEqual(v.get(), 1)
240        b2.deselect()
241        self.assertEqual(v.get(), 0)
242
243
244@add_standard_options(StandardOptionsTests)
245class RadiobuttonTest(AbstractLabelTest, unittest.TestCase):
246    OPTIONS = (
247        'activebackground', 'activeforeground', 'anchor',
248        'background', 'bitmap', 'borderwidth',
249        'command', 'compound', 'cursor',
250        'disabledforeground', 'font', 'foreground', 'height',
251        'highlightbackground', 'highlightcolor', 'highlightthickness',
252        'image', 'indicatoron', 'justify', 'offrelief', 'overrelief',
253        'padx', 'pady', 'relief', 'selectcolor', 'selectimage', 'state',
254        'takefocus', 'text', 'textvariable',
255        'tristateimage', 'tristatevalue',
256        'underline', 'value', 'variable', 'width', 'wraplength',
257    )
258
259    def create(self, **kwargs):
260        return tkinter.Radiobutton(self.root, **kwargs)
261
262    def test_configure_value(self):
263        widget = self.create()
264        self.checkParams(widget, 'value', 1, 2.3, '', 'any string')
265
266
267@add_standard_options(StandardOptionsTests)
268class MenubuttonTest(AbstractLabelTest, unittest.TestCase):
269    OPTIONS = (
270        'activebackground', 'activeforeground', 'anchor',
271        'background', 'bitmap', 'borderwidth',
272        'compound', 'cursor', 'direction',
273        'disabledforeground', 'font', 'foreground', 'height',
274        'highlightbackground', 'highlightcolor', 'highlightthickness',
275        'image', 'indicatoron', 'justify', 'menu',
276        'padx', 'pady', 'relief', 'state',
277        'takefocus', 'text', 'textvariable',
278        'underline', 'width', 'wraplength',
279    )
280    _conv_pixels = round
281    _clip_highlightthickness = True
282    _clip_pad = True
283    _clip_borderwidth = False
284
285    def create(self, **kwargs):
286        return tkinter.Menubutton(self.root, **kwargs)
287
288    def test_configure_direction(self):
289        widget = self.create()
290        self.checkEnumParam(widget, 'direction',
291                'above', 'below', 'flush', 'left', 'right')
292
293    def test_configure_height(self):
294        widget = self.create()
295        self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=str)
296
297    def test_configure_image(self):
298        widget = self.create()
299        image = tkinter.PhotoImage(master=self.root, name='image1')
300        self.checkParam(widget, 'image', image, conv=str)
301        errmsg = 'image "spam" doesn\'t exist'
302        with self.assertRaises(tkinter.TclError) as cm:
303            widget['image'] = 'spam'
304        if errmsg is not None:
305            self.assertEqual(str(cm.exception), errmsg)
306        with self.assertRaises(tkinter.TclError) as cm:
307            widget.configure({'image': 'spam'})
308        if errmsg is not None:
309            self.assertEqual(str(cm.exception), errmsg)
310
311    def test_configure_menu(self):
312        widget = self.create()
313        menu = tkinter.Menu(widget, name='menu')
314        self.checkParam(widget, 'menu', menu, eq=widget_eq)
315        menu.destroy()
316
317    def test_configure_width(self):
318        widget = self.create()
319        self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=str)
320
321
322class OptionMenuTest(MenubuttonTest, unittest.TestCase):
323
324    def create(self, default='b', values=('a', 'b', 'c'), **kwargs):
325        return tkinter.OptionMenu(self.root, None, default, *values, **kwargs)
326
327    def test_bad_kwarg(self):
328        with self.assertRaisesRegex(TclError, r"^unknown option -image$"):
329            tkinter.OptionMenu(self.root, None, 'b', image='')
330
331
332@add_standard_options(IntegerSizeTests, StandardOptionsTests)
333class EntryTest(AbstractWidgetTest, unittest.TestCase):
334    OPTIONS = (
335        'background', 'borderwidth', 'cursor',
336        'disabledbackground', 'disabledforeground',
337        'exportselection', 'font', 'foreground',
338        'highlightbackground', 'highlightcolor', 'highlightthickness',
339        'insertbackground', 'insertborderwidth',
340        'insertofftime', 'insertontime', 'insertwidth',
341        'invalidcommand', 'justify', 'placeholder', 'placeholderforeground',
342        'readonlybackground', 'relief',
343        'selectbackground', 'selectborderwidth', 'selectforeground',
344        'show', 'state', 'takefocus', 'textvariable',
345        'validate', 'validatecommand', 'width', 'xscrollcommand',
346    )
347
348    def create(self, **kwargs):
349        return tkinter.Entry(self.root, **kwargs)
350
351    def test_configure_disabledbackground(self):
352        widget = self.create()
353        self.checkColorParam(widget, 'disabledbackground')
354
355    def test_configure_insertborderwidth(self):
356        widget = self.create(insertwidth=100)
357        self.checkPixelsParam(widget, 'insertborderwidth',
358                              0, 1.3, 2.6, 6, -2, '10p')
359        # insertborderwidth is bounded above by a half of insertwidth.
360        self.checkParam(widget, 'insertborderwidth', 60, expected=100//2)
361
362    def test_configure_insertwidth(self):
363        widget = self.create()
364        self.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, '10p')
365        self.checkParam(widget, 'insertwidth', 0.1, expected=2)
366        self.checkParam(widget, 'insertwidth', -2, expected=2)
367        self.checkParam(widget, 'insertwidth', 0.9, expected=1)
368
369    def test_configure_invalidcommand(self):
370        widget = self.create()
371        self.checkCommandParam(widget, 'invalidcommand')
372        self.checkCommandParam(widget, 'invcmd')
373
374    def test_configure_readonlybackground(self):
375        widget = self.create()
376        self.checkColorParam(widget, 'readonlybackground')
377
378    def test_configure_show(self):
379        widget = self.create()
380        self.checkParam(widget, 'show', '*')
381        self.checkParam(widget, 'show', '')
382        self.checkParam(widget, 'show', ' ')
383
384    def test_configure_state(self):
385        widget = self.create()
386        self.checkEnumParam(widget, 'state',
387                            'disabled', 'normal', 'readonly')
388
389    def test_configure_validate(self):
390        widget = self.create()
391        self.checkEnumParam(widget, 'validate',
392                'all', 'key', 'focus', 'focusin', 'focusout', 'none')
393
394    def test_configure_validatecommand(self):
395        widget = self.create()
396        self.checkCommandParam(widget, 'validatecommand')
397        self.checkCommandParam(widget, 'vcmd')
398
399    def test_selection_methods(self):
400        widget = self.create()
401        widget.insert(0, '12345')
402        self.assertFalse(widget.selection_present())
403        widget.selection_range(0, 'end')
404        self.assertEqual(widget.selection_get(), '12345')
405        self.assertTrue(widget.selection_present())
406        widget.selection_from(1)
407        widget.selection_to(2)
408        self.assertEqual(widget.selection_get(), '2')
409        widget.selection_range(3, 4)
410        self.assertEqual(widget.selection_get(), '4')
411        widget.selection_clear()
412        self.assertFalse(widget.selection_present())
413        widget.selection_range(0, 'end')
414        widget.selection_adjust(4)
415        self.assertEqual(widget.selection_get(), '1234')
416        widget.selection_adjust(1)
417        self.assertEqual(widget.selection_get(), '234')
418        widget.selection_adjust(5)
419        self.assertEqual(widget.selection_get(), '2345')
420        widget.selection_adjust(0)
421        self.assertEqual(widget.selection_get(), '12345')
422        widget.selection_adjust(0)
423
424
425@add_standard_options(StandardOptionsTests)
426class SpinboxTest(EntryTest, unittest.TestCase):
427    OPTIONS = (
428        'activebackground', 'background', 'borderwidth',
429        'buttonbackground', 'buttoncursor', 'buttondownrelief', 'buttonuprelief',
430        'command', 'cursor', 'disabledbackground', 'disabledforeground',
431        'exportselection', 'font', 'foreground', 'format', 'from',
432        'highlightbackground', 'highlightcolor', 'highlightthickness',
433        'increment',
434        'insertbackground', 'insertborderwidth',
435        'insertofftime', 'insertontime', 'insertwidth',
436        'invalidcommand', 'justify', 'placeholder', 'placeholderforeground',
437        'relief', 'readonlybackground', 'repeatdelay', 'repeatinterval',
438        'selectbackground', 'selectborderwidth', 'selectforeground',
439        'state', 'takefocus', 'textvariable', 'to',
440        'validate', 'validatecommand', 'values',
441        'width', 'wrap', 'xscrollcommand',
442    )
443
444    def create(self, **kwargs):
445        return tkinter.Spinbox(self.root, **kwargs)
446
447    test_configure_show = None
448
449    def test_configure_buttonbackground(self):
450        widget = self.create()
451        self.checkColorParam(widget, 'buttonbackground')
452
453    def test_configure_buttoncursor(self):
454        widget = self.create()
455        self.checkCursorParam(widget, 'buttoncursor')
456
457    def test_configure_buttondownrelief(self):
458        widget = self.create()
459        self.checkReliefParam(widget, 'buttondownrelief')
460
461    def test_configure_buttonuprelief(self):
462        widget = self.create()
463        self.checkReliefParam(widget, 'buttonuprelief')
464
465    def test_configure_format(self):
466        widget = self.create()
467        self.checkParam(widget, 'format', '%2f')
468        self.checkParam(widget, 'format', '%2.2f')
469        self.checkParam(widget, 'format', '%.2f')
470        self.checkParam(widget, 'format', '%2.f')
471        self.checkInvalidParam(widget, 'format', '%2e-1f')
472        self.checkInvalidParam(widget, 'format', '2.2')
473        self.checkInvalidParam(widget, 'format', '%2.-2f')
474        self.checkParam(widget, 'format', '%-2.02f')
475        self.checkParam(widget, 'format', '% 2.02f')
476        self.checkParam(widget, 'format', '% -2.200f')
477        self.checkParam(widget, 'format', '%09.200f')
478        self.checkInvalidParam(widget, 'format', '%d')
479
480    def test_configure_from(self):
481        widget = self.create()
482        self.checkParam(widget, 'to', 100.0)
483        self.checkFloatParam(widget, 'from', -10, 10.2, 11.7)
484        if tk_version >= (8, 7):
485            self.checkFloatParam(widget, 'from', 200, expected=100)
486        else:
487            self.checkInvalidParam(
488                    widget, 'from', 200,
489                    errmsg='-to value must be greater than -from value')
490
491    def test_configure_increment(self):
492        widget = self.create()
493        self.checkFloatParam(widget, 'increment', -1, 1, 10.2, 12.8, 0)
494
495    def test_configure_to(self):
496        widget = self.create()
497        self.checkParam(widget, 'from', -100.0)
498        self.checkFloatParam(widget, 'to', -10, 10.2, 11.7)
499        if tk_version >= (8, 7):
500            self.checkFloatParam(widget, 'to', -200, expected=-100)
501        else:
502            self.checkInvalidParam(
503                    widget, 'to', -200,
504                    errmsg='-to value must be greater than -from value')
505
506    def test_configure_values(self):
507        # XXX
508        widget = self.create()
509        self.assertEqual(widget['values'], '')
510        self.checkParam(widget, 'values', 'mon tue wed thur')
511        self.checkParam(widget, 'values', ('mon', 'tue', 'wed', 'thur'),
512                        expected='mon tue wed thur')
513        self.checkParam(widget, 'values', (42, 3.14, '', 'any string'),
514                        expected='42 3.14 {} {any string}')
515        self.checkParam(widget, 'values', '')
516
517    def test_configure_wrap(self):
518        widget = self.create()
519        self.checkBooleanParam(widget, 'wrap')
520
521    def test_bbox(self):
522        widget = self.create()
523        self.assertIsBoundingBox(widget.bbox(0))
524        self.assertRaises(tkinter.TclError, widget.bbox, 'noindex')
525        self.assertRaises(tkinter.TclError, widget.bbox, None)
526        self.assertRaises(TypeError, widget.bbox)
527        self.assertRaises(TypeError, widget.bbox, 0, 1)
528
529    def test_selection_methods(self):
530        widget = self.create()
531        widget.insert(0, '12345')
532        self.assertFalse(widget.selection_present())
533        widget.selection_range(0, 'end')
534        self.assertEqual(widget.selection_get(), '12345')
535        self.assertTrue(widget.selection_present())
536        widget.selection_from(1)
537        widget.selection_to(2)
538        self.assertEqual(widget.selection_get(), '2')
539        widget.selection_range(3, 4)
540        self.assertEqual(widget.selection_get(), '4')
541        widget.selection_clear()
542        self.assertFalse(widget.selection_present())
543        widget.selection_range(0, 'end')
544        widget.selection_adjust(4)
545        self.assertEqual(widget.selection_get(), '1234')
546        widget.selection_adjust(1)
547        self.assertEqual(widget.selection_get(), '234')
548        widget.selection_adjust(5)
549        self.assertEqual(widget.selection_get(), '2345')
550        widget.selection_adjust(0)
551        self.assertEqual(widget.selection_get(), '12345')
552
553    def test_selection_element(self):
554        widget = self.create()
555        self.assertEqual(widget.selection_element(), "none")
556        widget.selection_element("buttonup")
557        self.assertEqual(widget.selection_element(), "buttonup")
558        widget.selection_element("buttondown")
559        self.assertEqual(widget.selection_element(), "buttondown")
560
561
562@add_standard_options(StandardOptionsTests)
563class TextTest(AbstractWidgetTest, unittest.TestCase):
564    OPTIONS = (
565        'autoseparators', 'background', 'blockcursor', 'borderwidth',
566        'cursor', 'endline', 'exportselection',
567        'font', 'foreground', 'height',
568        'highlightbackground', 'highlightcolor', 'highlightthickness',
569        'inactiveselectbackground', 'insertbackground', 'insertborderwidth',
570        'insertofftime', 'insertontime', 'insertunfocussed', 'insertwidth',
571        'maxundo', 'padx', 'pady', 'relief',
572        'selectbackground', 'selectborderwidth', 'selectforeground',
573        'setgrid', 'spacing1', 'spacing2', 'spacing3', 'startline', 'state',
574        'tabs', 'tabstyle', 'takefocus', 'undo', 'width', 'wrap',
575        'xscrollcommand', 'yscrollcommand',
576    )
577
578    def create(self, **kwargs):
579        return tkinter.Text(self.root, **kwargs)
580
581    def test_configure_autoseparators(self):
582        widget = self.create()
583        self.checkBooleanParam(widget, 'autoseparators')
584
585    def test_configure_blockcursor(self):
586        widget = self.create()
587        self.checkBooleanParam(widget, 'blockcursor')
588
589    def test_configure_endline(self):
590        widget = self.create()
591        text = '\n'.join('Line %d' for i in range(100))
592        widget.insert('end', text)
593        self.checkParam(widget, 'endline', 200, expected='')
594        self.checkParam(widget, 'endline', -10, expected='')
595        self.checkInvalidParam(widget, 'endline', 'spam',
596                errmsg='expected integer but got "spam"')
597        self.checkParam(widget, 'endline', 50)
598        self.checkParam(widget, 'startline', 15)
599        self.checkInvalidParam(widget, 'endline', 10,
600                errmsg='-startline must be less than or equal to -endline')
601
602    def test_configure_height(self):
603        widget = self.create()
604        self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, '3c')
605        self.checkParam(widget, 'height', -100, expected=1)
606        self.checkParam(widget, 'height', 0, expected=1)
607
608    def test_configure_maxundo(self):
609        widget = self.create()
610        self.checkIntegerParam(widget, 'maxundo', 0, 5, -1)
611
612    def test_configure_inactiveselectbackground(self):
613        widget = self.create()
614        self.checkColorParam(widget, 'inactiveselectbackground')
615
616    @requires_tk(8, 6)
617    def test_configure_insertunfocussed(self):
618        widget = self.create()
619        self.checkEnumParam(widget, 'insertunfocussed',
620                            'hollow', 'none', 'solid')
621
622    def test_configure_selectborderwidth(self):
623        widget = self.create()
624        self.checkPixelsParam(widget, 'selectborderwidth',
625                              1.3, 2.6, -2, '10p', conv=False)
626
627    def test_configure_spacing1(self):
628        widget = self.create()
629        self.checkPixelsParam(widget, 'spacing1', 20, 21.4, 22.6, '0.5c')
630        self.checkParam(widget, 'spacing1', -5, expected=0)
631
632    def test_configure_spacing2(self):
633        widget = self.create()
634        self.checkPixelsParam(widget, 'spacing2', 5, 6.4, 7.6, '0.1c')
635        self.checkParam(widget, 'spacing2', -1, expected=0)
636
637    def test_configure_spacing3(self):
638        widget = self.create()
639        self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, '0.5c')
640        self.checkParam(widget, 'spacing3', -10, expected=0)
641
642    def test_configure_startline(self):
643        widget = self.create()
644        text = '\n'.join('Line %d' for i in range(100))
645        widget.insert('end', text)
646        self.checkParam(widget, 'startline', 200, expected='')
647        self.checkParam(widget, 'startline', -10, expected='')
648        self.checkInvalidParam(widget, 'startline', 'spam',
649                errmsg='expected integer but got "spam"')
650        self.checkParam(widget, 'startline', 10)
651        self.checkParam(widget, 'endline', 50)
652        self.checkInvalidParam(widget, 'startline', 70,
653                errmsg='-startline must be less than or equal to -endline')
654
655    def test_configure_state(self):
656        widget = self.create()
657        self.checkEnumParam(widget, 'state', 'disabled', 'normal')
658
659    def test_configure_tabs(self):
660        widget = self.create()
661        self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i'))
662        self.checkParam(widget, 'tabs', '10.2 20.7 1i 2i',
663                        expected=(10.2, 20.7, '1i', '2i')
664                                 if get_tk_patchlevel(self.root) >= (8, 6, 14)
665                                 else ('10.2', '20.7', '1i', '2i'))
666        self.checkParam(widget, 'tabs', '2c left 4c 6c center',
667                        expected=('2c', 'left', '4c', '6c', 'center'))
668        self.checkInvalidParam(widget, 'tabs', 'spam',
669                errmsg=EXPECTED_SCREEN_DISTANCE_ERRMSG.format('spam'))
670
671    def test_configure_tabstyle(self):
672        widget = self.create()
673        self.checkEnumParam(widget, 'tabstyle', 'tabular', 'wordprocessor')
674
675    def test_configure_undo(self):
676        widget = self.create()
677        self.checkBooleanParam(widget, 'undo')
678
679    def test_configure_width(self):
680        widget = self.create()
681        self.checkIntegerParam(widget, 'width', 402)
682        self.checkParam(widget, 'width', -402, expected=1)
683        self.checkParam(widget, 'width', 0, expected=1)
684
685    def test_configure_wrap(self):
686        widget = self.create()
687        self.checkEnumParam(widget, 'wrap', 'char', 'none', 'word')
688
689    def test_bbox(self):
690        widget = self.create()
691        self.assertIsBoundingBox(widget.bbox('1.1'))
692        self.assertIsNone(widget.bbox('end'))
693        self.assertRaises(tkinter.TclError, widget.bbox, 'noindex')
694        self.assertRaises(tkinter.TclError, widget.bbox, None)
695        self.assertRaises(TypeError, widget.bbox)
696        self.assertRaises(TypeError, widget.bbox, '1.1', 'end')
697
698
699@add_standard_options(PixelSizeTests, StandardOptionsTests)
700class CanvasTest(AbstractWidgetTest, unittest.TestCase):
701    OPTIONS = (
702        'background', 'borderwidth',
703        'closeenough', 'confine', 'cursor', 'height',
704        'highlightbackground', 'highlightcolor', 'highlightthickness',
705        'insertbackground', 'insertborderwidth',
706        'insertofftime', 'insertontime', 'insertwidth',
707        'offset', 'relief', 'scrollregion',
708        'selectbackground', 'selectborderwidth', 'selectforeground',
709        'state', 'takefocus',
710        'xscrollcommand', 'xscrollincrement',
711        'yscrollcommand', 'yscrollincrement', 'width',
712    )
713
714    _conv_pixels = round
715    _stringify = True
716
717    def create(self, **kwargs):
718        return tkinter.Canvas(self.root, **kwargs)
719
720    def test_configure_closeenough(self):
721        widget = self.create()
722        self.checkFloatParam(widget, 'closeenough', 24, 2.4, 3.6, -3,
723                             conv=float)
724
725    def test_configure_confine(self):
726        widget = self.create()
727        self.checkBooleanParam(widget, 'confine')
728
729    def test_configure_offset(self):
730        widget = self.create()
731        self.assertEqual(widget['offset'], '0,0')
732        self.checkParams(widget, 'offset',
733                'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center')
734        self.checkParam(widget, 'offset', '10,20')
735        self.checkParam(widget, 'offset', '#5,6')
736        self.checkInvalidParam(widget, 'offset', 'spam')
737
738    def test_configure_scrollregion(self):
739        widget = self.create()
740        self.checkParam(widget, 'scrollregion', '0 0 200 150')
741        self.checkParam(widget, 'scrollregion', (0, 0, 200, 150),
742                        expected='0 0 200 150')
743        self.checkParam(widget, 'scrollregion', '')
744        self.checkInvalidParam(widget, 'scrollregion', 'spam',
745                               errmsg='bad scrollRegion "spam"')
746        self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 'spam'))
747        self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200))
748        self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 150, 0))
749
750    def test_configure_state(self):
751        widget = self.create()
752        self.checkEnumParam(widget, 'state', 'disabled', 'normal',
753                errmsg='bad state value "{}": must be normal or disabled')
754
755    def test_configure_xscrollincrement(self):
756        widget = self.create()
757        self.checkPixelsParam(widget, 'xscrollincrement',
758                              40, 0, 41.2, 43.6, -40, '0.5i')
759
760    def test_configure_yscrollincrement(self):
761        widget = self.create()
762        self.checkPixelsParam(widget, 'yscrollincrement',
763                              10, 0, 11.2, 13.6, -10, '0.1i')
764
765    def _test_option_joinstyle(self, c, factory):
766        for joinstyle in 'bevel', 'miter', 'round':
767            i = factory(joinstyle=joinstyle)
768            self.assertEqual(c.itemcget(i, 'joinstyle'), joinstyle)
769        self.assertRaises(TclError, factory, joinstyle='spam')
770
771    def _test_option_smooth(self, c, factory):
772        for smooth in 1, True, '1', 'true', 'yes', 'on':
773            i = factory(smooth=smooth)
774            self.assertEqual(c.itemcget(i, 'smooth'), 'true')
775        for smooth in 0, False, '0', 'false', 'no', 'off':
776            i = factory(smooth=smooth)
777            self.assertEqual(c.itemcget(i, 'smooth'), '0')
778        i = factory(smooth=True, splinestep=30)
779        self.assertEqual(c.itemcget(i, 'smooth'), 'true')
780        self.assertEqual(c.itemcget(i, 'splinestep'), '30')
781        i = factory(smooth='raw', splinestep=30)
782        self.assertEqual(c.itemcget(i, 'smooth'), 'raw')
783        self.assertEqual(c.itemcget(i, 'splinestep'), '30')
784        self.assertRaises(TclError, factory, smooth='spam')
785
786    def test_create_rectangle(self):
787        c = self.create()
788        i1 = c.create_rectangle(20, 30, 60, 10)
789        self.assertEqual(c.coords(i1), [20.0, 10.0, 60.0, 30.0])
790        self.assertEqual(c.bbox(i1), (19, 9, 61, 31))
791
792        i2 = c.create_rectangle([21, 31, 61, 11])
793        self.assertEqual(c.coords(i2), [21.0, 11.0, 61.0, 31.0])
794        self.assertEqual(c.bbox(i2), (20, 10, 62, 32))
795
796        i3 = c.create_rectangle((22, 32), (62, 12))
797        self.assertEqual(c.coords(i3), [22.0, 12.0, 62.0, 32.0])
798        self.assertEqual(c.bbox(i3), (21, 11, 63, 33))
799
800        i4 = c.create_rectangle([(23, 33), (63, 13)])
801        self.assertEqual(c.coords(i4), [23.0, 13.0, 63.0, 33.0])
802        self.assertEqual(c.bbox(i4), (22, 12, 64, 34))
803
804        self.assertRaises(TclError, c.create_rectangle, 20, 30, 60)
805        self.assertRaises(TclError, c.create_rectangle, [20, 30, 60])
806        self.assertRaises(TclError, c.create_rectangle, 20, 30, 40, 50, 60, 10)
807        self.assertRaises(TclError, c.create_rectangle, [20, 30, 40, 50, 60, 10])
808        self.assertRaises(TclError, c.create_rectangle, 20, 30)
809        self.assertRaises(TclError, c.create_rectangle, [20, 30])
810        self.assertRaises(IndexError, c.create_rectangle)
811        self.assertRaises(IndexError, c.create_rectangle, [])
812
813    def test_create_line(self):
814        c = self.create()
815        i1 = c.create_line(20, 30, 40, 50, 60, 10)
816        self.assertEqual(c.coords(i1), [20.0, 30.0, 40.0, 50.0, 60.0, 10.0])
817        self.assertEqual(c.bbox(i1), (18, 8, 62, 52))
818        self.assertEqual(c.itemcget(i1, 'arrow'), 'none')
819        self.assertEqual(c.itemcget(i1, 'arrowshape'), '8 10 3')
820        self.assertEqual(c.itemcget(i1, 'capstyle'), 'butt')
821        self.assertEqual(c.itemcget(i1, 'joinstyle'), 'round')
822        self.assertEqual(c.itemcget(i1, 'smooth'), '0')
823        self.assertEqual(c.itemcget(i1, 'splinestep'), '12')
824
825        i2 = c.create_line([21, 31, 41, 51, 61, 11])
826        self.assertEqual(c.coords(i2), [21.0, 31.0, 41.0, 51.0, 61.0, 11.0])
827        self.assertEqual(c.bbox(i2), (19, 9, 63, 53))
828
829        i3 = c.create_line((22, 32), (42, 52), (62, 12))
830        self.assertEqual(c.coords(i3), [22.0, 32.0, 42.0, 52.0, 62.0, 12.0])
831        self.assertEqual(c.bbox(i3), (20, 10, 64, 54))
832
833        i4 = c.create_line([(23, 33), (43, 53), (63, 13)])
834        self.assertEqual(c.coords(i4), [23.0, 33.0, 43.0, 53.0, 63.0, 13.0])
835        self.assertEqual(c.bbox(i4), (21, 11, 65, 55))
836
837        self.assertRaises(TclError, c.create_line, 20, 30, 60)
838        self.assertRaises(TclError, c.create_line, [20, 30, 60])
839        self.assertRaises(TclError, c.create_line, 20, 30)
840        self.assertRaises(TclError, c.create_line, [20, 30])
841        self.assertRaises(IndexError, c.create_line)
842        self.assertRaises(IndexError, c.create_line, [])
843
844        for arrow in 'none', 'first', 'last', 'both':
845            i = c.create_line(20, 30, 60, 10, arrow=arrow)
846            self.assertEqual(c.itemcget(i, 'arrow'), arrow)
847        i = c.create_line(20, 30, 60, 10, arrow='first', arrowshape=[10, 15, 5])
848        self.assertEqual(c.itemcget(i, 'arrowshape'), '10 15 5')
849        self.assertRaises(TclError, c.create_line, 20, 30, 60, 10, arrow='spam')
850
851        for capstyle in 'butt', 'projecting', 'round':
852            i = c.create_line(20, 30, 60, 10, capstyle=capstyle)
853            self.assertEqual(c.itemcget(i, 'capstyle'), capstyle)
854        self.assertRaises(TclError, c.create_line, 20, 30, 60, 10, capstyle='spam')
855
856        self._test_option_joinstyle(c,
857                lambda **kwargs: c.create_line(20, 30, 40, 50, 60, 10, **kwargs))
858        self._test_option_smooth(c,
859                lambda **kwargs: c.create_line(20, 30, 60, 10, **kwargs))
860
861    def test_create_polygon(self):
862        c = self.create()
863        tk87 = tk_version >= (8, 7)
864        # In Tk < 8.7 polygons are filled, but has no outline by default.
865        # This affects its size, so always explicitly specify outline.
866        i1 = c.create_polygon(20, 30, 40, 50, 60, 10, outline='red')
867        self.assertEqual(c.coords(i1), [20.0, 30.0, 40.0, 50.0, 60.0, 10.0])
868        self.assertEqual(c.bbox(i1), (18, 8, 62, 52))
869        self.assertEqual(c.itemcget(i1, 'joinstyle'), 'round')
870        self.assertEqual(c.itemcget(i1, 'smooth'), '0')
871        self.assertEqual(c.itemcget(i1, 'splinestep'), '12')
872
873        i2 = c.create_polygon([21, 31, 41, 51, 61, 11], outline='red')
874        self.assertEqual(c.coords(i2), [21.0, 31.0, 41.0, 51.0, 61.0, 11.0])
875        self.assertEqual(c.bbox(i2), (19, 9, 63, 53))
876
877        i3 = c.create_polygon((22, 32), (42, 52), (62, 12), outline='red')
878        self.assertEqual(c.coords(i3), [22.0, 32.0, 42.0, 52.0, 62.0, 12.0])
879        self.assertEqual(c.bbox(i3), (20, 10, 64, 54))
880
881        i4 = c.create_polygon([(23, 33), (43, 53), (63, 13)], outline='red')
882        self.assertEqual(c.coords(i4), [23.0, 33.0, 43.0, 53.0, 63.0, 13.0])
883        self.assertEqual(c.bbox(i4), (21, 11, 65, 55))
884
885        self.assertRaises(TclError, c.create_polygon, 20, 30, 60)
886        self.assertRaises(TclError, c.create_polygon, [20, 30, 60])
887        self.assertRaises(IndexError, c.create_polygon)
888        self.assertRaises(IndexError, c.create_polygon, [])
889
890        self._test_option_joinstyle(c,
891                lambda **kwargs: c.create_polygon(20, 30, 40, 50, 60, 10, **kwargs))
892        self._test_option_smooth(c,
893                lambda **kwargs: c.create_polygon(20, 30, 40, 50, 60, 10, **kwargs))
894
895    def test_coords(self):
896        c = self.create()
897        i = c.create_line(20, 30, 40, 50, 60, 10, tags='x')
898        self.assertEqual(c.coords(i), [20.0, 30.0, 40.0, 50.0, 60.0, 10.0])
899        self.assertEqual(c.coords('x'), [20.0, 30.0, 40.0, 50.0, 60.0, 10.0])
900        self.assertEqual(c.bbox(i), (18, 8, 62, 52))
901
902        c.coords(i, 50, 60, 70, 80, 90, 40)
903        self.assertEqual(c.coords(i), [50.0, 60.0, 70.0, 80.0, 90.0, 40.0])
904        self.assertEqual(c.bbox(i), (48, 38, 92, 82))
905
906        c.coords(i, [21, 31, 41, 51, 61, 11])
907        self.assertEqual(c.coords(i), [21.0, 31.0, 41.0, 51.0, 61.0, 11.0])
908
909        c.coords(i, (22, 32), (42, 52), (62, 12))
910        self.assertEqual(c.coords(i), [22.0, 32.0, 42.0, 52.0, 62.0, 12.0])
911
912        c.coords(i, [(23, 33), (43, 53), (63, 13)])
913        self.assertEqual(c.coords(i), [23.0, 33.0, 43.0, 53.0, 63.0, 13.0])
914
915        c.coords(i, 20, 30, 60, 10)
916        self.assertEqual(c.coords(i), [20.0, 30.0, 60.0, 10.0])
917        self.assertEqual(c.bbox(i), (18, 8, 62, 32))
918
919        self.assertRaises(TclError, c.coords, i, 20, 30, 60)
920        self.assertRaises(TclError, c.coords, i, [20, 30, 60])
921        self.assertRaises(TclError, c.coords, i, 20, 30)
922        self.assertRaises(TclError, c.coords, i, [20, 30])
923
924        c.coords(i, '20', '30c', '60i', '10p')
925        coords = c.coords(i)
926        self.assertIsInstance(coords, list)
927        self.assertEqual(len(coords), 4)
928        self.assertEqual(coords[0], 20)
929        for i in range(4):
930            self.assertIsInstance(coords[i], float)
931
932    @requires_tk(8, 6)
933    def test_moveto(self):
934        widget = self.create()
935        i1 = widget.create_rectangle(1, 1, 20, 20, tags='group')
936        i2 = widget.create_rectangle(30, 30, 50, 70, tags='group')
937        x1, y1, _, _ = widget.bbox(i1)
938        x2, y2, _, _ = widget.bbox(i2)
939        widget.moveto('group', 200, 100)
940        x1_2, y1_2, _, _ = widget.bbox(i1)
941        x2_2, y2_2, _, _ = widget.bbox(i2)
942        self.assertEqual(x1_2, 200)
943        self.assertEqual(y1_2, 100)
944        self.assertEqual(x2 - x1, x2_2 - x1_2)
945        self.assertEqual(y2 - y1, y2_2 - y1_2)
946        widget.tag_lower(i2, i1)
947        widget.moveto('group', y=50)
948        x1_3, y1_3, _, _ = widget.bbox(i1)
949        x2_3, y2_3, _, _ = widget.bbox(i2)
950        self.assertEqual(y2_3, 50)
951        self.assertEqual(x2_3, x2_2)
952        self.assertEqual(x2_2 - x1_2, x2_3 - x1_3)
953        self.assertEqual(y2_2 - y1_2, y2_3 - y1_3)
954
955
956@add_standard_options(IntegerSizeTests, StandardOptionsTests)
957class ListboxTest(AbstractWidgetTest, unittest.TestCase):
958    OPTIONS = (
959        'activestyle', 'background', 'borderwidth', 'cursor',
960        'disabledforeground', 'exportselection',
961        'font', 'foreground', 'height',
962        'highlightbackground', 'highlightcolor', 'highlightthickness',
963        'justify', 'listvariable', 'relief',
964        'selectbackground', 'selectborderwidth', 'selectforeground',
965        'selectmode', 'setgrid', 'state',
966        'takefocus', 'width', 'xscrollcommand', 'yscrollcommand',
967    )
968
969    def create(self, **kwargs):
970        return tkinter.Listbox(self.root, **kwargs)
971
972    def test_configure_activestyle(self):
973        widget = self.create()
974        self.checkEnumParam(widget, 'activestyle',
975                            'dotbox', 'none', 'underline')
976
977    test_configure_justify = requires_tk(8, 6, 5)(StandardOptionsTests.test_configure_justify)
978
979    def test_configure_listvariable(self):
980        widget = self.create()
981        var = tkinter.DoubleVar(self.root)
982        self.checkVariableParam(widget, 'listvariable', var)
983
984    def test_configure_selectmode(self):
985        widget = self.create()
986        self.checkParam(widget, 'selectmode', 'single')
987        self.checkParam(widget, 'selectmode', 'browse')
988        self.checkParam(widget, 'selectmode', 'multiple')
989        self.checkParam(widget, 'selectmode', 'extended')
990
991    def test_configure_state(self):
992        widget = self.create()
993        self.checkEnumParam(widget, 'state', 'disabled', 'normal')
994
995    def test_itemconfigure(self):
996        widget = self.create()
997        with self.assertRaisesRegex(TclError, 'item number "0" out of range'):
998            widget.itemconfigure(0)
999        colors = 'red orange yellow green blue white violet'.split()
1000        widget.insert('end', *colors)
1001        for i, color in enumerate(colors):
1002            widget.itemconfigure(i, background=color)
1003        with self.assertRaises(TypeError):
1004            widget.itemconfigure()
1005        with self.assertRaisesRegex(TclError, 'bad listbox index "red"'):
1006            widget.itemconfigure('red')
1007        if get_tk_patchlevel(self.root) >= (8, 6, 14):
1008            prefix = ('background', '', '', '')
1009        else:
1010            prefix = ('background', 'background', 'Background', '')
1011        self.assertEqual(widget.itemconfigure(0, 'background'),
1012                         (*prefix, 'red'))
1013        self.assertEqual(widget.itemconfigure('end', 'background'),
1014                         (*prefix, 'violet'))
1015        self.assertEqual(widget.itemconfigure('@0,0', 'background'),
1016                         (*prefix, 'red'))
1017
1018        d = widget.itemconfigure(0)
1019        self.assertIsInstance(d, dict)
1020        for k, v in d.items():
1021            self.assertIn(len(v), (2, 5))
1022            if len(v) == 5:
1023                self.assertEqual(v, widget.itemconfigure(0, k))
1024                self.assertEqual(v[4], widget.itemcget(0, k))
1025
1026    def check_itemconfigure(self, name, value):
1027        widget = self.create()
1028        widget.insert('end', 'a', 'b', 'c', 'd')
1029        widget.itemconfigure(0, **{name: value})
1030        self.assertEqual(widget.itemconfigure(0, name)[4], value)
1031        self.assertEqual(widget.itemcget(0, name), value)
1032        with self.assertRaisesRegex(TclError, 'unknown color name "spam"'):
1033            widget.itemconfigure(0, **{name: 'spam'})
1034
1035    def test_itemconfigure_background(self):
1036        self.check_itemconfigure('background', '#ff0000')
1037
1038    def test_itemconfigure_bg(self):
1039        self.check_itemconfigure('bg', '#ff0000')
1040
1041    def test_itemconfigure_fg(self):
1042        self.check_itemconfigure('fg', '#110022')
1043
1044    def test_itemconfigure_foreground(self):
1045        self.check_itemconfigure('foreground', '#110022')
1046
1047    def test_itemconfigure_selectbackground(self):
1048        self.check_itemconfigure('selectbackground', '#110022')
1049
1050    def test_itemconfigure_selectforeground(self):
1051        self.check_itemconfigure('selectforeground', '#654321')
1052
1053    def test_box(self):
1054        lb = self.create()
1055        lb.insert(0, *('el%d' % i for i in range(8)))
1056        lb.pack()
1057        self.assertIsBoundingBox(lb.bbox(0))
1058        self.assertIsNone(lb.bbox(-1))
1059        self.assertIsNone(lb.bbox(10))
1060        self.assertRaises(TclError, lb.bbox, 'noindex')
1061        self.assertRaises(TclError, lb.bbox, None)
1062        self.assertRaises(TypeError, lb.bbox)
1063        self.assertRaises(TypeError, lb.bbox, 0, 1)
1064
1065    def test_curselection(self):
1066        lb = self.create()
1067        lb.insert(0, *('el%d' % i for i in range(8)))
1068        lb.selection_clear(0, tkinter.END)
1069        lb.selection_set(2, 4)
1070        lb.selection_set(6)
1071        self.assertEqual(lb.curselection(), (2, 3, 4, 6))
1072        self.assertRaises(TypeError, lb.curselection, 0)
1073
1074    def test_get(self):
1075        lb = self.create()
1076        lb.insert(0, *('el%d' % i for i in range(8)))
1077        self.assertEqual(lb.get(0), 'el0')
1078        self.assertEqual(lb.get(3), 'el3')
1079        self.assertEqual(lb.get('end'), 'el7')
1080        self.assertEqual(lb.get(8), '')
1081        self.assertEqual(lb.get(-1), '')
1082        self.assertEqual(lb.get(3, 5), ('el3', 'el4', 'el5'))
1083        self.assertEqual(lb.get(5, 'end'), ('el5', 'el6', 'el7'))
1084        self.assertEqual(lb.get(5, 0), ())
1085        self.assertEqual(lb.get(0, 0), ('el0',))
1086        self.assertRaises(TclError, lb.get, 'noindex')
1087        self.assertRaises(TclError, lb.get, None)
1088        self.assertRaises(TypeError, lb.get)
1089        self.assertRaises(TclError, lb.get, 'end', 'noindex')
1090        self.assertRaises(TypeError, lb.get, 1, 2, 3)
1091        self.assertRaises(TclError, lb.get, 2.4)
1092
1093
1094@add_standard_options(PixelSizeTests, StandardOptionsTests)
1095class ScaleTest(AbstractWidgetTest, unittest.TestCase):
1096    OPTIONS = (
1097        'activebackground', 'background', 'bigincrement', 'borderwidth',
1098        'command', 'cursor', 'digits', 'font', 'foreground', 'from',
1099        'highlightbackground', 'highlightcolor', 'highlightthickness',
1100        'label', 'length', 'orient', 'relief',
1101        'repeatdelay', 'repeatinterval',
1102        'resolution', 'showvalue', 'sliderlength', 'sliderrelief', 'state',
1103        'takefocus', 'tickinterval', 'to', 'troughcolor', 'variable', 'width',
1104    )
1105    default_orient = 'vertical'
1106
1107    def create(self, **kwargs):
1108        return tkinter.Scale(self.root, **kwargs)
1109
1110    def test_configure_bigincrement(self):
1111        widget = self.create()
1112        self.checkFloatParam(widget, 'bigincrement', 12.4, 23.6, -5)
1113
1114    def test_configure_digits(self):
1115        widget = self.create()
1116        self.checkIntegerParam(widget, 'digits', 5, 0)
1117
1118    def test_configure_from(self):
1119        widget = self.create()
1120        conv = float if get_tk_patchlevel(self.root) >= (8, 6, 10) else float_round
1121        self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=conv)
1122
1123    def test_configure_label(self):
1124        widget = self.create()
1125        self.checkParam(widget, 'label', 'any string')
1126        self.checkParam(widget, 'label', '')
1127
1128    def test_configure_length(self):
1129        widget = self.create()
1130        self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i')
1131
1132    def test_configure_resolution(self):
1133        widget = self.create()
1134        self.checkFloatParam(widget, 'resolution', 4.2, 0, 6.7, -2)
1135
1136    def test_configure_showvalue(self):
1137        widget = self.create()
1138        self.checkBooleanParam(widget, 'showvalue')
1139
1140    def test_configure_sliderlength(self):
1141        widget = self.create()
1142        self.checkPixelsParam(widget, 'sliderlength',
1143                              10, 11.2, 15.6, -3, '3m')
1144
1145    def test_configure_sliderrelief(self):
1146        widget = self.create()
1147        self.checkReliefParam(widget, 'sliderrelief')
1148
1149    def test_configure_tickinterval(self):
1150        widget = self.create()
1151        self.checkFloatParam(widget, 'tickinterval', 1, 4.3, 7.6, 0,
1152                             conv=float_round)
1153        self.checkParam(widget, 'tickinterval', -2, expected=2,
1154                        conv=float_round)
1155
1156    def test_configure_to(self):
1157        widget = self.create()
1158        self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10,
1159                             conv=float_round)
1160
1161
1162@add_standard_options(PixelSizeTests, StandardOptionsTests)
1163class ScrollbarTest(AbstractWidgetTest, unittest.TestCase):
1164    OPTIONS = (
1165        'activebackground', 'activerelief',
1166        'background', 'borderwidth',
1167        'command', 'cursor', 'elementborderwidth',
1168        'highlightbackground', 'highlightcolor', 'highlightthickness',
1169        'jump', 'orient', 'relief',
1170        'repeatdelay', 'repeatinterval',
1171        'takefocus', 'troughcolor', 'width',
1172    )
1173    _conv_pixels = round
1174    _stringify = True
1175    default_orient = 'vertical'
1176
1177    def create(self, **kwargs):
1178        return tkinter.Scrollbar(self.root, **kwargs)
1179
1180    def test_configure_elementborderwidth(self):
1181        widget = self.create()
1182        self.checkPixelsParam(widget, 'elementborderwidth', 4.3, 5.6, '1m')
1183        expected = self._default_pixels if tk_version >= (8, 7) else -2
1184        self.checkParam(widget, 'elementborderwidth', -2, expected=expected)
1185
1186    def test_configure_orient(self):
1187        widget = self.create()
1188        self.checkEnumParam(widget, 'orient', 'vertical', 'horizontal',
1189                            fullname='orientation', allow_empty=True)
1190
1191    def test_activate(self):
1192        sb = self.create()
1193        for e in ('arrow1', 'slider', 'arrow2'):
1194            sb.activate(e)
1195            self.assertEqual(sb.activate(), e)
1196        sb.activate('')
1197        self.assertIsNone(sb.activate())
1198        self.assertRaises(TypeError, sb.activate, 'arrow1', 'arrow2')
1199
1200    def test_set(self):
1201        sb = self.create()
1202        sb.set(0.2, 0.4)
1203        self.assertEqual(sb.get(), (0.2, 0.4))
1204        self.assertRaises(TclError, sb.set, 'abc', 'def')
1205        self.assertRaises(TclError, sb.set, 0.6, 'def')
1206        self.assertRaises(TclError, sb.set, 0.6, None)
1207        self.assertRaises(TypeError, sb.set, 0.6)
1208        self.assertRaises(TypeError, sb.set, 0.6, 0.7, 0.8)
1209
1210
1211@add_standard_options(StandardOptionsTests)
1212class PanedWindowTest(AbstractWidgetTest, unittest.TestCase):
1213    OPTIONS = (
1214        'background', 'borderwidth', 'cursor',
1215        'handlepad', 'handlesize', 'height',
1216        'opaqueresize', 'orient',
1217        'proxybackground', 'proxyborderwidth', 'proxyrelief',
1218        'relief',
1219        'sashcursor', 'sashpad', 'sashrelief', 'sashwidth',
1220        'showhandle', 'width',
1221    )
1222    default_orient = 'horizontal'
1223
1224    def create(self, **kwargs):
1225        return tkinter.PanedWindow(self.root, **kwargs)
1226
1227    def test_configure_handlepad(self):
1228        widget = self.create()
1229        self.checkPixelsParam(widget, 'handlepad', 5, 6.4, 7.6, -3, '1m')
1230
1231    def test_configure_handlesize(self):
1232        widget = self.create()
1233        self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m',
1234                              conv=False)
1235
1236    def test_configure_height(self):
1237        widget = self.create()
1238        self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i',
1239                              conv=False)
1240
1241    def test_configure_opaqueresize(self):
1242        widget = self.create()
1243        self.checkBooleanParam(widget, 'opaqueresize')
1244
1245    @requires_tk(8, 6, 5)
1246    def test_configure_proxybackground(self):
1247        widget = self.create()
1248        self.checkColorParam(widget, 'proxybackground')
1249
1250    @requires_tk(8, 6, 5)
1251    def test_configure_proxyborderwidth(self):
1252        widget = self.create()
1253        self.checkPixelsParam(widget, 'proxyborderwidth',
1254                              0, 1.3, 2.9, 6, -2, '10p',
1255                              conv=False)
1256
1257    @requires_tk(8, 6, 5)
1258    def test_configure_proxyrelief(self):
1259        widget = self.create()
1260        self.checkReliefParam(widget, 'proxyrelief',
1261                              allow_empty=(tk_version >= (8, 7)))
1262
1263    def test_configure_sashcursor(self):
1264        widget = self.create()
1265        self.checkCursorParam(widget, 'sashcursor')
1266
1267    def test_configure_sashpad(self):
1268        widget = self.create()
1269        self.checkPixelsParam(widget, 'sashpad', 8, 1.3, 2.6, -2, '2m')
1270
1271    def test_configure_sashrelief(self):
1272        widget = self.create()
1273        self.checkReliefParam(widget, 'sashrelief')
1274
1275    def test_configure_sashwidth(self):
1276        widget = self.create()
1277        self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m',
1278                              conv=False)
1279
1280    def test_configure_showhandle(self):
1281        widget = self.create()
1282        self.checkBooleanParam(widget, 'showhandle')
1283
1284    def test_configure_width(self):
1285        widget = self.create()
1286        self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i',
1287                              conv=False)
1288
1289    def create2(self):
1290        p = self.create()
1291        b = tkinter.Button(p)
1292        c = tkinter.Button(p)
1293        p.add(b)
1294        p.add(c)
1295        return p, b, c
1296
1297    def test_paneconfigure(self):
1298        p, b, c = self.create2()
1299        self.assertRaises(TypeError, p.paneconfigure)
1300        d = p.paneconfigure(b)
1301        self.assertIsInstance(d, dict)
1302        for k, v in d.items():
1303            self.assertEqual(len(v), 5)
1304            self.assertEqual(v, p.paneconfigure(b, k))
1305            self.assertEqual(v[4], p.panecget(b, k))
1306
1307    def check_paneconfigure(self, p, b, name, value, expected):
1308        if not self.wantobjects:
1309            expected = str(expected)
1310        p.paneconfigure(b, **{name: value})
1311        self.assertEqual(p.paneconfigure(b, name)[4], expected)
1312        self.assertEqual(p.panecget(b, name), expected)
1313
1314    def check_paneconfigure_bad(self, p, b, name, msg):
1315        with self.assertRaisesRegex(TclError, msg):
1316            p.paneconfigure(b, **{name: 'badValue'})
1317
1318    def test_paneconfigure_after(self):
1319        p, b, c = self.create2()
1320        self.check_paneconfigure(p, b, 'after', c, str(c))
1321        self.check_paneconfigure_bad(p, b, 'after',
1322                                     'bad window path name "badValue"')
1323
1324    def test_paneconfigure_before(self):
1325        p, b, c = self.create2()
1326        self.check_paneconfigure(p, b, 'before', c, str(c))
1327        self.check_paneconfigure_bad(p, b, 'before',
1328                                     'bad window path name "badValue"')
1329
1330    def test_paneconfigure_height(self):
1331        p, b, c = self.create2()
1332        self.check_paneconfigure(p, b, 'height', 10, 10)
1333        self.check_paneconfigure_bad(p, b, 'height',
1334                EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG.format('badValue'))
1335
1336    def test_paneconfigure_hide(self):
1337        p, b, c = self.create2()
1338        self.check_paneconfigure(p, b, 'hide', False, 0)
1339        self.check_paneconfigure_bad(p, b, 'hide',
1340                                     'expected boolean value but got "badValue"')
1341
1342    def test_paneconfigure_minsize(self):
1343        p, b, c = self.create2()
1344        self.check_paneconfigure(p, b, 'minsize', 10, 10)
1345        self.check_paneconfigure_bad(p, b, 'minsize',
1346                EXPECTED_SCREEN_DISTANCE_ERRMSG.format('badValue'))
1347
1348    def test_paneconfigure_padx(self):
1349        p, b, c = self.create2()
1350        self.check_paneconfigure(p, b, 'padx', 1.3, 1)
1351        self.check_paneconfigure_bad(p, b, 'padx',
1352                EXPECTED_SCREEN_DISTANCE_ERRMSG.format('badValue'))
1353
1354    def test_paneconfigure_pady(self):
1355        p, b, c = self.create2()
1356        self.check_paneconfigure(p, b, 'pady', 1.3, 1)
1357        self.check_paneconfigure_bad(p, b, 'pady',
1358                EXPECTED_SCREEN_DISTANCE_ERRMSG.format('badValue'))
1359
1360    def test_paneconfigure_sticky(self):
1361        p, b, c = self.create2()
1362        self.check_paneconfigure(p, b, 'sticky', 'nsew', 'nesw')
1363        self.check_paneconfigure_bad(p, b, 'sticky',
1364                                     'bad stickyness value "badValue": must '
1365                                     'be a string containing zero or more of '
1366                                     'n, e, s, and w')
1367
1368    def test_paneconfigure_stretch(self):
1369        p, b, c = self.create2()
1370        self.check_paneconfigure(p, b, 'stretch', 'alw', 'always')
1371        self.check_paneconfigure_bad(p, b, 'stretch',
1372                                     'bad stretch "badValue": must be '
1373                                     'always, first, last, middle, or never')
1374
1375    def test_paneconfigure_width(self):
1376        p, b, c = self.create2()
1377        self.check_paneconfigure(p, b, 'width', 10, 10)
1378        self.check_paneconfigure_bad(p, b, 'width',
1379                EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG.format('badValue'))
1380
1381
1382@add_standard_options(StandardOptionsTests)
1383class MenuTest(AbstractWidgetTest, unittest.TestCase):
1384    OPTIONS = (
1385        'activebackground', 'activeborderwidth', 'activeforeground',
1386        'activerelief',
1387        'background', 'borderwidth', 'cursor',
1388        'disabledforeground', 'font', 'foreground',
1389        'postcommand', 'relief', 'selectcolor', 'takefocus',
1390        'tearoff', 'tearoffcommand', 'title', 'type',
1391    )
1392    _conv_pixels = False
1393
1394    def create(self, **kwargs):
1395        return tkinter.Menu(self.root, **kwargs)
1396
1397    def test_indexcommand_none(self):
1398        widget = self.create()
1399        i = widget.index('none')
1400        self.assertIsNone(i)
1401
1402    test_configure_activerelief = requires_tk(8, 7)(StandardOptionsTests.test_configure_activerelief)
1403
1404    def test_configure_postcommand(self):
1405        widget = self.create()
1406        self.checkCommandParam(widget, 'postcommand')
1407
1408    def test_configure_tearoff(self):
1409        widget = self.create()
1410        self.checkBooleanParam(widget, 'tearoff')
1411
1412    def test_configure_tearoffcommand(self):
1413        widget = self.create()
1414        self.checkCommandParam(widget, 'tearoffcommand')
1415
1416    def test_configure_title(self):
1417        widget = self.create()
1418        self.checkParam(widget, 'title', 'any string')
1419
1420    def test_configure_type(self):
1421        widget = self.create()
1422        values = ('normal', 'tearoff', 'menubar')
1423        self.checkEnumParam(widget, 'type', *values,
1424                            allow_empty=tk_version < (8, 7),
1425                            sort=tk_version >= (8, 7))
1426
1427    def test_entryconfigure(self):
1428        m1 = self.create()
1429        m1.add_command(label='test')
1430        self.assertRaises(TypeError, m1.entryconfigure)
1431        with self.assertRaisesRegex(TclError, 'bad menu entry index "foo"'):
1432            m1.entryconfigure('foo')
1433        d = m1.entryconfigure(1)
1434        self.assertIsInstance(d, dict)
1435        for k, v in d.items():
1436            self.assertIsInstance(k, str)
1437            self.assertIsInstance(v, tuple)
1438            self.assertEqual(len(v), 5)
1439            self.assertEqual(v[0], k)
1440            self.assertEqual(m1.entrycget(1, k), v[4])
1441        m1.destroy()
1442
1443    def test_entryconfigure_label(self):
1444        m1 = self.create()
1445        m1.add_command(label='test')
1446        self.assertEqual(m1.entrycget(1, 'label'), 'test')
1447        m1.entryconfigure(1, label='changed')
1448        self.assertEqual(m1.entrycget(1, 'label'), 'changed')
1449
1450    def test_entryconfigure_variable(self):
1451        m1 = self.create()
1452        v1 = tkinter.BooleanVar(self.root)
1453        v2 = tkinter.BooleanVar(self.root)
1454        m1.add_checkbutton(variable=v1, onvalue=True, offvalue=False,
1455                           label='Nonsense')
1456        self.assertEqual(str(m1.entrycget(1, 'variable')), str(v1))
1457        m1.entryconfigure(1, variable=v2)
1458        self.assertEqual(str(m1.entrycget(1, 'variable')), str(v2))
1459
1460
1461@add_standard_options(PixelSizeTests, StandardOptionsTests)
1462class MessageTest(AbstractWidgetTest, unittest.TestCase):
1463    OPTIONS = (
1464        'anchor', 'aspect', 'background', 'borderwidth',
1465        'cursor', 'font', 'foreground',
1466        'highlightbackground', 'highlightcolor', 'highlightthickness',
1467        'justify', 'padx', 'pady', 'relief',
1468        'takefocus', 'text', 'textvariable', 'width',
1469    )
1470    _conv_pad_pixels = False
1471    if tk_version >= (8, 7):
1472        _conv_pixels = False
1473    _clip_pad = tk_version >= (8, 7)
1474    _clip_borderwidth = tk_version >= (8, 7)
1475
1476    def create(self, **kwargs):
1477        return tkinter.Message(self.root, **kwargs)
1478
1479    def test_configure_aspect(self):
1480        widget = self.create()
1481        self.checkIntegerParam(widget, 'aspect', 250, 0, -300)
1482
1483    def test_configure_padx(self):
1484        widget = self.create()
1485        self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m',
1486                              conv=self._conv_pad_pixels)
1487        expected = self._default_pixels if self._clip_pad else -2
1488        self.checkParam(widget, 'padx', -2, expected=expected)
1489
1490    def test_configure_pady(self):
1491        widget = self.create()
1492        self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m',
1493                              conv=self._conv_pad_pixels)
1494        expected = self._default_pixels if self._clip_pad else -2
1495        self.checkParam(widget, 'pady', -2, expected=expected)
1496
1497    def test_configure_width(self):
1498        widget = self.create()
1499        self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, 0, '5i')
1500        expected = 0 if tk_version >= (8, 7) else -402
1501        self.checkParam(widget, 'width', -402, expected=expected)
1502
1503
1504class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
1505
1506    def test_frame(self):
1507        self._test_widget(tkinter.Frame)
1508
1509    def test_label(self):
1510        self._test_widget(tkinter.Label)
1511
1512
1513if __name__ == '__main__':
1514    unittest.main()
1515