summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/test_tk.py20
-rw-r--r--Lib/test/test_tkinter/README14
-rw-r--r--Lib/test/test_tkinter/__init__.py18
-rw-r--r--Lib/test/test_tkinter/__main__.py4
-rw-r--r--Lib/test/test_tkinter/support.py135
-rw-r--r--Lib/test/test_tkinter/test_colorchooser.py68
-rw-r--r--Lib/test/test_tkinter/test_font.py163
-rw-r--r--Lib/test/test_tkinter/test_geometry_managers.py899
-rw-r--r--Lib/test/test_tkinter/test_images.py380
-rw-r--r--Lib/test/test_tkinter/test_loadtk.py46
-rw-r--r--Lib/test/test_tkinter/test_messagebox.py36
-rw-r--r--Lib/test/test_tkinter/test_misc.py430
-rw-r--r--Lib/test/test_tkinter/test_simpledialog.py35
-rw-r--r--Lib/test/test_tkinter/test_text.py45
-rw-r--r--Lib/test/test_tkinter/test_variables.py342
-rw-r--r--Lib/test/test_tkinter/test_widgets.py1292
-rw-r--r--Lib/test/test_tkinter/widget_tests.py519
-rw-r--r--Lib/test/test_ttk/__init__.py (renamed from Lib/test/test_ttk_guionly.py)15
-rw-r--r--Lib/test/test_ttk/__main__.py4
-rw-r--r--Lib/test/test_ttk/test_extensions.py328
-rw-r--r--Lib/test/test_ttk/test_style.py181
-rw-r--r--Lib/test/test_ttk/test_widgets.py1858
22 files changed, 6804 insertions, 28 deletions
diff --git a/Lib/test/test_tk.py b/Lib/test/test_tk.py
deleted file mode 100644
index 8f90cba..0000000
--- a/Lib/test/test_tk.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import unittest
-from test import support
-from test.support import import_helper
-from test.support import check_sanitizer
-
-if check_sanitizer(address=True, memory=True):
- raise unittest.SkipTest("Tests involvin libX11 can SEGFAULT on ASAN/MSAN builds")
-
-# Skip test if _tkinter wasn't built.
-import_helper.import_module('_tkinter')
-
-# Skip test if tk cannot be initialized.
-support.requires('gui')
-
-def load_tests(loader, tests, pattern):
- return loader.discover('tkinter.test.test_tkinter')
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/Lib/test/test_tkinter/README b/Lib/test/test_tkinter/README
new file mode 100644
index 0000000..79cd16c
--- /dev/null
+++ b/Lib/test/test_tkinter/README
@@ -0,0 +1,14 @@
+Writing new tests
+=================
+
+Precaution
+----------
+
+ New tests should always use only one Tk window at once, like all the
+ current tests do. This means that you have to destroy the current window
+ before creating another one, and clean up after the test. The motivation
+ behind this is that some tests may depend on having its window focused
+ while it is running to work properly, and it may be hard to force focus
+ on your window across platforms (right now only test_traversal at
+ test_ttk.test_widgets.NotebookTest depends on this).
+
diff --git a/Lib/test/test_tkinter/__init__.py b/Lib/test/test_tkinter/__init__.py
new file mode 100644
index 0000000..edcb449
--- /dev/null
+++ b/Lib/test/test_tkinter/__init__.py
@@ -0,0 +1,18 @@
+import os.path
+import unittest
+from test import support
+from test.support import import_helper
+
+
+if support.check_sanitizer(address=True, memory=True):
+ raise unittest.SkipTest("Tests involving libX11 can SEGFAULT on ASAN/MSAN builds")
+
+# Skip test if _tkinter wasn't built.
+import_helper.import_module('_tkinter')
+
+# Skip test if tk cannot be initialized.
+support.requires('gui')
+
+
+def load_tests(*args):
+ return support.load_package_tests(os.path.dirname(__file__), *args)
diff --git a/Lib/test/test_tkinter/__main__.py b/Lib/test/test_tkinter/__main__.py
new file mode 100644
index 0000000..40a23a2
--- /dev/null
+++ b/Lib/test/test_tkinter/__main__.py
@@ -0,0 +1,4 @@
+from . import load_tests
+import unittest
+
+unittest.main()
diff --git a/Lib/test/test_tkinter/support.py b/Lib/test/test_tkinter/support.py
new file mode 100644
index 0000000..9154eba
--- /dev/null
+++ b/Lib/test/test_tkinter/support.py
@@ -0,0 +1,135 @@
+import functools
+import tkinter
+import unittest
+
+class AbstractTkTest:
+
+ @classmethod
+ def setUpClass(cls):
+ cls._old_support_default_root = tkinter._support_default_root
+ destroy_default_root()
+ tkinter.NoDefaultRoot()
+ cls.root = tkinter.Tk()
+ cls.wantobjects = cls.root.wantobjects()
+ # De-maximize main window.
+ # Some window managers can maximize new windows.
+ cls.root.wm_state('normal')
+ try:
+ cls.root.wm_attributes('-zoomed', False)
+ except tkinter.TclError:
+ pass
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.update_idletasks()
+ cls.root.destroy()
+ del cls.root
+ tkinter._default_root = None
+ tkinter._support_default_root = cls._old_support_default_root
+
+ def setUp(self):
+ self.root.deiconify()
+
+ def tearDown(self):
+ for w in self.root.winfo_children():
+ w.destroy()
+ self.root.withdraw()
+
+
+class AbstractDefaultRootTest:
+
+ def setUp(self):
+ self._old_support_default_root = tkinter._support_default_root
+ destroy_default_root()
+ tkinter._support_default_root = True
+ self.wantobjects = tkinter.wantobjects
+
+ def tearDown(self):
+ destroy_default_root()
+ tkinter._default_root = None
+ tkinter._support_default_root = self._old_support_default_root
+
+ def _test_widget(self, constructor):
+ # no master passing
+ x = constructor()
+ self.assertIsNotNone(tkinter._default_root)
+ self.assertIs(x.master, tkinter._default_root)
+ self.assertIs(x.tk, tkinter._default_root.tk)
+ x.destroy()
+ destroy_default_root()
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, constructor)
+ self.assertFalse(hasattr(tkinter, '_default_root'))
+
+
+def destroy_default_root():
+ if getattr(tkinter, '_default_root', None):
+ tkinter._default_root.update_idletasks()
+ tkinter._default_root.destroy()
+ tkinter._default_root = None
+
+def simulate_mouse_click(widget, x, y):
+ """Generate proper events to click at the x, y position (tries to act
+ like an X server)."""
+ widget.event_generate('<Enter>', x=0, y=0)
+ widget.event_generate('<Motion>', x=x, y=y)
+ widget.event_generate('<ButtonPress-1>', x=x, y=y)
+ widget.event_generate('<ButtonRelease-1>', x=x, y=y)
+
+
+import _tkinter
+tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.')))
+
+def requires_tcl(*version):
+ if len(version) <= 2:
+ return unittest.skipUnless(tcl_version >= version,
+ 'requires Tcl version >= ' + '.'.join(map(str, version)))
+
+ def deco(test):
+ @functools.wraps(test)
+ def newtest(self):
+ if get_tk_patchlevel() < version:
+ self.skipTest('requires Tcl version >= ' +
+ '.'.join(map(str, version)))
+ test(self)
+ return newtest
+ return deco
+
+_tk_patchlevel = None
+def get_tk_patchlevel():
+ global _tk_patchlevel
+ if _tk_patchlevel is None:
+ tcl = tkinter.Tcl()
+ _tk_patchlevel = tcl.info_patchlevel()
+ return _tk_patchlevel
+
+units = {
+ 'c': 72 / 2.54, # centimeters
+ 'i': 72, # inches
+ 'm': 72 / 25.4, # millimeters
+ 'p': 1, # points
+}
+
+def pixels_conv(value):
+ return float(value[:-1]) * units[value[-1:]]
+
+def tcl_obj_eq(actual, expected):
+ if actual == expected:
+ return True
+ if isinstance(actual, _tkinter.Tcl_Obj):
+ if isinstance(expected, str):
+ return str(actual) == expected
+ if isinstance(actual, tuple):
+ if isinstance(expected, tuple):
+ return (len(actual) == len(expected) and
+ all(tcl_obj_eq(act, exp)
+ for act, exp in zip(actual, expected)))
+ return False
+
+def widget_eq(actual, expected):
+ if actual == expected:
+ return True
+ if isinstance(actual, (str, tkinter.Widget)):
+ if isinstance(expected, (str, tkinter.Widget)):
+ return str(actual) == str(expected)
+ return False
diff --git a/Lib/test/test_tkinter/test_colorchooser.py b/Lib/test/test_tkinter/test_colorchooser.py
new file mode 100644
index 0000000..9bba213
--- /dev/null
+++ b/Lib/test/test_tkinter/test_colorchooser.py
@@ -0,0 +1,68 @@
+import unittest
+import tkinter
+from test.support import requires, swap_attr
+from test.test_tkinter.support import AbstractDefaultRootTest, AbstractTkTest
+from tkinter import colorchooser
+from tkinter.colorchooser import askcolor
+from tkinter.commondialog import Dialog
+
+requires('gui')
+
+
+class ChooserTest(AbstractTkTest, unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ AbstractTkTest.setUpClass.__func__(cls)
+ cls.cc = colorchooser.Chooser(initialcolor='dark blue slate')
+
+ def test_fixoptions(self):
+ cc = self.cc
+ cc._fixoptions()
+ self.assertEqual(cc.options['initialcolor'], 'dark blue slate')
+
+ cc.options['initialcolor'] = '#D2D269691E1E'
+ cc._fixoptions()
+ self.assertEqual(cc.options['initialcolor'], '#D2D269691E1E')
+
+ cc.options['initialcolor'] = (210, 105, 30)
+ cc._fixoptions()
+ self.assertEqual(cc.options['initialcolor'], '#d2691e')
+
+ def test_fixresult(self):
+ cc = self.cc
+ self.assertEqual(cc._fixresult(self.root, ()), (None, None))
+ self.assertEqual(cc._fixresult(self.root, ''), (None, None))
+ self.assertEqual(cc._fixresult(self.root, 'chocolate'),
+ ((210, 105, 30), 'chocolate'))
+ self.assertEqual(cc._fixresult(self.root, '#4a3c8c'),
+ ((74, 60, 140), '#4a3c8c'))
+
+
+class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
+
+ def test_askcolor(self):
+ def test_callback(dialog, master):
+ nonlocal ismapped
+ master.update()
+ ismapped = master.winfo_ismapped()
+ raise ZeroDivisionError
+
+ with swap_attr(Dialog, '_test_callback', test_callback):
+ ismapped = None
+ self.assertRaises(ZeroDivisionError, askcolor)
+ #askcolor()
+ self.assertEqual(ismapped, False)
+
+ root = tkinter.Tk()
+ ismapped = None
+ self.assertRaises(ZeroDivisionError, askcolor)
+ self.assertEqual(ismapped, True)
+ root.destroy()
+
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, askcolor)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_tkinter/test_font.py b/Lib/test/test_tkinter/test_font.py
new file mode 100644
index 0000000..563707d
--- /dev/null
+++ b/Lib/test/test_tkinter/test_font.py
@@ -0,0 +1,163 @@
+import unittest
+import tkinter
+from tkinter import font
+from test.support import requires, gc_collect, ALWAYS_EQ
+from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest
+
+requires('gui')
+
+fontname = "TkDefaultFont"
+
+class FontTest(AbstractTkTest, unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ AbstractTkTest.setUpClass.__func__(cls)
+ try:
+ cls.font = font.Font(root=cls.root, name=fontname, exists=True)
+ except tkinter.TclError:
+ cls.font = font.Font(root=cls.root, name=fontname, exists=False)
+
+ def test_configure(self):
+ options = self.font.configure()
+ self.assertGreaterEqual(set(options),
+ {'family', 'size', 'weight', 'slant', 'underline', 'overstrike'})
+ for key in options:
+ self.assertEqual(self.font.cget(key), options[key])
+ self.assertEqual(self.font[key], options[key])
+ for key in 'family', 'weight', 'slant':
+ self.assertIsInstance(options[key], str)
+ self.assertIsInstance(self.font.cget(key), str)
+ self.assertIsInstance(self.font[key], str)
+ sizetype = int if self.wantobjects else str
+ for key in 'size', 'underline', 'overstrike':
+ self.assertIsInstance(options[key], sizetype)
+ self.assertIsInstance(self.font.cget(key), sizetype)
+ self.assertIsInstance(self.font[key], sizetype)
+
+ def test_unicode_family(self):
+ family = 'MS \u30b4\u30b7\u30c3\u30af'
+ try:
+ f = font.Font(root=self.root, family=family, exists=True)
+ except tkinter.TclError:
+ f = font.Font(root=self.root, family=family, exists=False)
+ self.assertEqual(f.cget('family'), family)
+ del f
+ gc_collect()
+
+ def test_actual(self):
+ options = self.font.actual()
+ self.assertGreaterEqual(set(options),
+ {'family', 'size', 'weight', 'slant', 'underline', 'overstrike'})
+ for key in options:
+ self.assertEqual(self.font.actual(key), options[key])
+ for key in 'family', 'weight', 'slant':
+ self.assertIsInstance(options[key], str)
+ self.assertIsInstance(self.font.actual(key), str)
+ sizetype = int if self.wantobjects else str
+ for key in 'size', 'underline', 'overstrike':
+ self.assertIsInstance(options[key], sizetype)
+ self.assertIsInstance(self.font.actual(key), sizetype)
+
+ def test_name(self):
+ self.assertEqual(self.font.name, fontname)
+ self.assertEqual(str(self.font), fontname)
+
+ def test_equality(self):
+ font1 = font.Font(root=self.root, name=fontname, exists=True)
+ font2 = font.Font(root=self.root, name=fontname, exists=True)
+ self.assertIsNot(font1, font2)
+ self.assertEqual(font1, font2)
+ self.assertNotEqual(font1, font1.copy())
+
+ self.assertNotEqual(font1, 0)
+ self.assertEqual(font1, ALWAYS_EQ)
+
+ root2 = tkinter.Tk()
+ self.addCleanup(root2.destroy)
+ font3 = font.Font(root=root2, name=fontname, exists=True)
+ self.assertEqual(str(font1), str(font3))
+ self.assertNotEqual(font1, font3)
+
+ def test_measure(self):
+ self.assertIsInstance(self.font.measure('abc'), int)
+
+ def test_metrics(self):
+ metrics = self.font.metrics()
+ self.assertGreaterEqual(set(metrics),
+ {'ascent', 'descent', 'linespace', 'fixed'})
+ for key in metrics:
+ self.assertEqual(self.font.metrics(key), metrics[key])
+ self.assertIsInstance(metrics[key], int)
+ self.assertIsInstance(self.font.metrics(key), int)
+
+ def test_families(self):
+ families = font.families(self.root)
+ self.assertIsInstance(families, tuple)
+ self.assertTrue(families)
+ for family in families:
+ self.assertIsInstance(family, str)
+ self.assertTrue(family)
+
+ def test_names(self):
+ names = font.names(self.root)
+ self.assertIsInstance(names, tuple)
+ self.assertTrue(names)
+ for name in names:
+ self.assertIsInstance(name, str)
+ self.assertTrue(name)
+ self.assertIn(fontname, names)
+
+ def test_nametofont(self):
+ testfont = font.nametofont(fontname, root=self.root)
+ self.assertIsInstance(testfont, font.Font)
+ self.assertEqual(testfont.name, fontname)
+
+ def test_repr(self):
+ self.assertEqual(
+ repr(self.font), f'<tkinter.font.Font object {fontname!r}>'
+ )
+
+
+class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
+
+ def test_families(self):
+ self.assertRaises(RuntimeError, font.families)
+ root = tkinter.Tk()
+ families = font.families()
+ self.assertIsInstance(families, tuple)
+ self.assertTrue(families)
+ for family in families:
+ self.assertIsInstance(family, str)
+ self.assertTrue(family)
+ root.destroy()
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, font.families)
+
+ def test_names(self):
+ self.assertRaises(RuntimeError, font.names)
+ root = tkinter.Tk()
+ names = font.names()
+ self.assertIsInstance(names, tuple)
+ self.assertTrue(names)
+ for name in names:
+ self.assertIsInstance(name, str)
+ self.assertTrue(name)
+ self.assertIn(fontname, names)
+ root.destroy()
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, font.names)
+
+ def test_nametofont(self):
+ self.assertRaises(RuntimeError, font.nametofont, fontname)
+ root = tkinter.Tk()
+ testfont = font.nametofont(fontname)
+ self.assertIsInstance(testfont, font.Font)
+ self.assertEqual(testfont.name, fontname)
+ root.destroy()
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, font.nametofont, fontname)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_tkinter/test_geometry_managers.py b/Lib/test/test_tkinter/test_geometry_managers.py
new file mode 100644
index 0000000..3663048
--- /dev/null
+++ b/Lib/test/test_tkinter/test_geometry_managers.py
@@ -0,0 +1,899 @@
+import unittest
+import re
+import tkinter
+from tkinter import TclError
+from test.support import requires
+
+from test.test_tkinter.support import pixels_conv
+from test.test_tkinter.widget_tests import AbstractWidgetTest
+
+requires('gui')
+
+
+class PackTest(AbstractWidgetTest, unittest.TestCase):
+
+ test_keys = None
+
+ def create2(self):
+ pack = tkinter.Toplevel(self.root, name='pack')
+ pack.wm_geometry('300x200+0+0')
+ pack.wm_minsize(1, 1)
+ a = tkinter.Frame(pack, name='a', width=20, height=40, bg='red')
+ b = tkinter.Frame(pack, name='b', width=50, height=30, bg='blue')
+ c = tkinter.Frame(pack, name='c', width=80, height=80, bg='green')
+ d = tkinter.Frame(pack, name='d', width=40, height=30, bg='yellow')
+ return pack, a, b, c, d
+
+ def test_pack_configure_after(self):
+ pack, a, b, c, d = self.create2()
+ with self.assertRaisesRegex(TclError, 'window "%s" isn\'t packed' % b):
+ a.pack_configure(after=b)
+ with self.assertRaisesRegex(TclError, 'bad window path name ".foo"'):
+ a.pack_configure(after='.foo')
+ a.pack_configure(side='top')
+ b.pack_configure(side='top')
+ c.pack_configure(side='top')
+ d.pack_configure(side='top')
+ self.assertEqual(pack.pack_slaves(), [a, b, c, d])
+ a.pack_configure(after=b)
+ self.assertEqual(pack.pack_slaves(), [b, a, c, d])
+ a.pack_configure(after=a)
+ self.assertEqual(pack.pack_slaves(), [b, a, c, d])
+
+ def test_pack_configure_anchor(self):
+ pack, a, b, c, d = self.create2()
+ def check(anchor, geom):
+ a.pack_configure(side='top', ipadx=5, padx=10, ipady=15, pady=20,
+ expand=True, anchor=anchor)
+ self.root.update()
+ self.assertEqual(a.winfo_geometry(), geom)
+ check('n', '30x70+135+20')
+ check('ne', '30x70+260+20')
+ check('e', '30x70+260+65')
+ check('se', '30x70+260+110')
+ check('s', '30x70+135+110')
+ check('sw', '30x70+10+110')
+ check('w', '30x70+10+65')
+ check('nw', '30x70+10+20')
+ check('center', '30x70+135+65')
+
+ def test_pack_configure_before(self):
+ pack, a, b, c, d = self.create2()
+ with self.assertRaisesRegex(TclError, 'window "%s" isn\'t packed' % b):
+ a.pack_configure(before=b)
+ with self.assertRaisesRegex(TclError, 'bad window path name ".foo"'):
+ a.pack_configure(before='.foo')
+ a.pack_configure(side='top')
+ b.pack_configure(side='top')
+ c.pack_configure(side='top')
+ d.pack_configure(side='top')
+ self.assertEqual(pack.pack_slaves(), [a, b, c, d])
+ a.pack_configure(before=d)
+ self.assertEqual(pack.pack_slaves(), [b, c, a, d])
+ a.pack_configure(before=a)
+ self.assertEqual(pack.pack_slaves(), [b, c, a, d])
+
+ def test_pack_configure_expand(self):
+ pack, a, b, c, d = self.create2()
+ def check(*geoms):
+ self.root.update()
+ self.assertEqual(a.winfo_geometry(), geoms[0])
+ self.assertEqual(b.winfo_geometry(), geoms[1])
+ self.assertEqual(c.winfo_geometry(), geoms[2])
+ self.assertEqual(d.winfo_geometry(), geoms[3])
+ a.pack_configure(side='left')
+ b.pack_configure(side='top')
+ c.pack_configure(side='right')
+ d.pack_configure(side='bottom')
+ check('20x40+0+80', '50x30+135+0', '80x80+220+75', '40x30+100+170')
+ a.pack_configure(side='left', expand='yes')
+ b.pack_configure(side='top', expand='on')
+ c.pack_configure(side='right', expand=True)
+ d.pack_configure(side='bottom', expand=1)
+ check('20x40+40+80', '50x30+175+35', '80x80+180+110', '40x30+100+135')
+ a.pack_configure(side='left', expand='yes', fill='both')
+ b.pack_configure(side='top', expand='on', fill='both')
+ c.pack_configure(side='right', expand=True, fill='both')
+ d.pack_configure(side='bottom', expand=1, fill='both')
+ check('100x200+0+0', '200x100+100+0', '160x100+140+100', '40x100+100+100')
+
+ def test_pack_configure_in(self):
+ pack, a, b, c, d = self.create2()
+ a.pack_configure(side='top')
+ b.pack_configure(side='top')
+ c.pack_configure(side='top')
+ d.pack_configure(side='top')
+ a.pack_configure(in_=pack)
+ self.assertEqual(pack.pack_slaves(), [b, c, d, a])
+ a.pack_configure(in_=c)
+ self.assertEqual(pack.pack_slaves(), [b, c, d])
+ self.assertEqual(c.pack_slaves(), [a])
+ with self.assertRaisesRegex(TclError,
+ 'can\'t pack %s inside itself' % (a,)):
+ a.pack_configure(in_=a)
+ with self.assertRaisesRegex(TclError, 'bad window path name ".foo"'):
+ a.pack_configure(in_='.foo')
+
+ def test_pack_configure_padx_ipadx_fill(self):
+ pack, a, b, c, d = self.create2()
+ def check(geom1, geom2, **kwargs):
+ a.pack_forget()
+ b.pack_forget()
+ a.pack_configure(**kwargs)
+ b.pack_configure(expand=True, fill='both')
+ self.root.update()
+ self.assertEqual(a.winfo_geometry(), geom1)
+ self.assertEqual(b.winfo_geometry(), geom2)
+ check('20x40+260+80', '240x200+0+0', side='right', padx=20)
+ check('20x40+250+80', '240x200+0+0', side='right', padx=(10, 30))
+ check('60x40+240+80', '240x200+0+0', side='right', ipadx=20)
+ check('30x40+260+80', '250x200+0+0', side='right', ipadx=5, padx=10)
+ check('20x40+260+80', '240x200+0+0', side='right', padx=20, fill='x')
+ check('20x40+249+80', '240x200+0+0',
+ side='right', padx=(9, 31), fill='x')
+ check('60x40+240+80', '240x200+0+0', side='right', ipadx=20, fill='x')
+ check('30x40+260+80', '250x200+0+0',
+ side='right', ipadx=5, padx=10, fill='x')
+ check('30x40+255+80', '250x200+0+0',
+ side='right', ipadx=5, padx=(5, 15), fill='x')
+ check('20x40+140+0', '300x160+0+40', side='top', padx=20)
+ check('20x40+120+0', '300x160+0+40', side='top', padx=(0, 40))
+ check('60x40+120+0', '300x160+0+40', side='top', ipadx=20)
+ check('30x40+135+0', '300x160+0+40', side='top', ipadx=5, padx=10)
+ check('30x40+130+0', '300x160+0+40', side='top', ipadx=5, padx=(5, 15))
+ check('260x40+20+0', '300x160+0+40', side='top', padx=20, fill='x')
+ check('260x40+25+0', '300x160+0+40',
+ side='top', padx=(25, 15), fill='x')
+ check('300x40+0+0', '300x160+0+40', side='top', ipadx=20, fill='x')
+ check('280x40+10+0', '300x160+0+40',
+ side='top', ipadx=5, padx=10, fill='x')
+ check('280x40+5+0', '300x160+0+40',
+ side='top', ipadx=5, padx=(5, 15), fill='x')
+ a.pack_configure(padx='1c')
+ self.assertEqual(a.pack_info()['padx'],
+ self._str(pack.winfo_pixels('1c')))
+ a.pack_configure(ipadx='1c')
+ self.assertEqual(a.pack_info()['ipadx'],
+ self._str(pack.winfo_pixels('1c')))
+
+ def test_pack_configure_pady_ipady_fill(self):
+ pack, a, b, c, d = self.create2()
+ def check(geom1, geom2, **kwargs):
+ a.pack_forget()
+ b.pack_forget()
+ a.pack_configure(**kwargs)
+ b.pack_configure(expand=True, fill='both')
+ self.root.update()
+ self.assertEqual(a.winfo_geometry(), geom1)
+ self.assertEqual(b.winfo_geometry(), geom2)
+ check('20x40+280+80', '280x200+0+0', side='right', pady=20)
+ check('20x40+280+70', '280x200+0+0', side='right', pady=(10, 30))
+ check('20x80+280+60', '280x200+0+0', side='right', ipady=20)
+ check('20x50+280+75', '280x200+0+0', side='right', ipady=5, pady=10)
+ check('20x40+280+80', '280x200+0+0', side='right', pady=20, fill='x')
+ check('20x40+280+69', '280x200+0+0',
+ side='right', pady=(9, 31), fill='x')
+ check('20x80+280+60', '280x200+0+0', side='right', ipady=20, fill='x')
+ check('20x50+280+75', '280x200+0+0',
+ side='right', ipady=5, pady=10, fill='x')
+ check('20x50+280+70', '280x200+0+0',
+ side='right', ipady=5, pady=(5, 15), fill='x')
+ check('20x40+140+20', '300x120+0+80', side='top', pady=20)
+ check('20x40+140+0', '300x120+0+80', side='top', pady=(0, 40))
+ check('20x80+140+0', '300x120+0+80', side='top', ipady=20)
+ check('20x50+140+10', '300x130+0+70', side='top', ipady=5, pady=10)
+ check('20x50+140+5', '300x130+0+70', side='top', ipady=5, pady=(5, 15))
+ check('300x40+0+20', '300x120+0+80', side='top', pady=20, fill='x')
+ check('300x40+0+25', '300x120+0+80',
+ side='top', pady=(25, 15), fill='x')
+ check('300x80+0+0', '300x120+0+80', side='top', ipady=20, fill='x')
+ check('300x50+0+10', '300x130+0+70',
+ side='top', ipady=5, pady=10, fill='x')
+ check('300x50+0+5', '300x130+0+70',
+ side='top', ipady=5, pady=(5, 15), fill='x')
+ a.pack_configure(pady='1c')
+ self.assertEqual(a.pack_info()['pady'],
+ self._str(pack.winfo_pixels('1c')))
+ a.pack_configure(ipady='1c')
+ self.assertEqual(a.pack_info()['ipady'],
+ self._str(pack.winfo_pixels('1c')))
+
+ def test_pack_configure_side(self):
+ pack, a, b, c, d = self.create2()
+ def check(side, geom1, geom2):
+ a.pack_configure(side=side)
+ self.assertEqual(a.pack_info()['side'], side)
+ b.pack_configure(expand=True, fill='both')
+ self.root.update()
+ self.assertEqual(a.winfo_geometry(), geom1)
+ self.assertEqual(b.winfo_geometry(), geom2)
+ check('top', '20x40+140+0', '300x160+0+40')
+ check('bottom', '20x40+140+160', '300x160+0+0')
+ check('left', '20x40+0+80', '280x200+20+0')
+ check('right', '20x40+280+80', '280x200+0+0')
+
+ def test_pack_forget(self):
+ pack, a, b, c, d = self.create2()
+ a.pack_configure()
+ b.pack_configure()
+ c.pack_configure()
+ self.assertEqual(pack.pack_slaves(), [a, b, c])
+ b.pack_forget()
+ self.assertEqual(pack.pack_slaves(), [a, c])
+ b.pack_forget()
+ self.assertEqual(pack.pack_slaves(), [a, c])
+ d.pack_forget()
+
+ def test_pack_info(self):
+ pack, a, b, c, d = self.create2()
+ with self.assertRaisesRegex(TclError, 'window "%s" isn\'t packed' % a):
+ a.pack_info()
+ a.pack_configure()
+ b.pack_configure(side='right', in_=a, anchor='s', expand=True, fill='x',
+ ipadx=5, padx=10, ipady=2, pady=(5, 15))
+ info = a.pack_info()
+ self.assertIsInstance(info, dict)
+ self.assertEqual(info['anchor'], 'center')
+ self.assertEqual(info['expand'], self._str(0))
+ self.assertEqual(info['fill'], 'none')
+ self.assertEqual(info['in'], pack)
+ self.assertEqual(info['ipadx'], self._str(0))
+ self.assertEqual(info['ipady'], self._str(0))
+ self.assertEqual(info['padx'], self._str(0))
+ self.assertEqual(info['pady'], self._str(0))
+ self.assertEqual(info['side'], 'top')
+ info = b.pack_info()
+ self.assertIsInstance(info, dict)
+ self.assertEqual(info['anchor'], 's')
+ self.assertEqual(info['expand'], self._str(1))
+ self.assertEqual(info['fill'], 'x')
+ self.assertEqual(info['in'], a)
+ self.assertEqual(info['ipadx'], self._str(5))
+ self.assertEqual(info['ipady'], self._str(2))
+ self.assertEqual(info['padx'], self._str(10))
+ self.assertEqual(info['pady'], self._str((5, 15)))
+ self.assertEqual(info['side'], 'right')
+
+ def test_pack_propagate(self):
+ pack, a, b, c, d = self.create2()
+ pack.configure(width=300, height=200)
+ a.pack_configure()
+ pack.pack_propagate(False)
+ self.root.update()
+ self.assertEqual(pack.winfo_reqwidth(), 300)
+ self.assertEqual(pack.winfo_reqheight(), 200)
+ pack.pack_propagate(True)
+ self.root.update()
+ self.assertEqual(pack.winfo_reqwidth(), 20)
+ self.assertEqual(pack.winfo_reqheight(), 40)
+
+ def test_pack_slaves(self):
+ pack, a, b, c, d = self.create2()
+ self.assertEqual(pack.pack_slaves(), [])
+ a.pack_configure()
+ self.assertEqual(pack.pack_slaves(), [a])
+ b.pack_configure()
+ self.assertEqual(pack.pack_slaves(), [a, b])
+
+
+class PlaceTest(AbstractWidgetTest, unittest.TestCase):
+
+ test_keys = None
+
+ def create2(self):
+ t = tkinter.Toplevel(self.root, width=300, height=200, bd=0)
+ t.wm_geometry('300x200+0+0')
+ f = tkinter.Frame(t, width=154, height=84, bd=2, relief='raised')
+ f.place_configure(x=48, y=38)
+ f2 = tkinter.Frame(t, width=30, height=60, bd=2, relief='raised')
+ self.root.update()
+ return t, f, f2
+
+ def test_place_configure_in(self):
+ t, f, f2 = self.create2()
+ self.assertEqual(f2.winfo_manager(), '')
+ with self.assertRaisesRegex(TclError, "can't place %s relative to "
+ "itself" % re.escape(str(f2))):
+ f2.place_configure(in_=f2)
+ self.assertEqual(f2.winfo_manager(), '')
+ with self.assertRaisesRegex(TclError, 'bad window path name'):
+ f2.place_configure(in_='spam')
+ f2.place_configure(in_=f)
+ self.assertEqual(f2.winfo_manager(), 'place')
+
+ def test_place_configure_x(self):
+ t, f, f2 = self.create2()
+ f2.place_configure(in_=f)
+ self.assertEqual(f2.place_info()['x'], '0')
+ self.root.update()
+ self.assertEqual(f2.winfo_x(), 50)
+ f2.place_configure(x=100)
+ self.assertEqual(f2.place_info()['x'], '100')
+ self.root.update()
+ self.assertEqual(f2.winfo_x(), 150)
+ f2.place_configure(x=-10, relx=1)
+ self.assertEqual(f2.place_info()['x'], '-10')
+ self.root.update()
+ self.assertEqual(f2.winfo_x(), 190)
+ with self.assertRaisesRegex(TclError, 'bad screen distance "spam"'):
+ f2.place_configure(in_=f, x='spam')
+
+ def test_place_configure_y(self):
+ t, f, f2 = self.create2()
+ f2.place_configure(in_=f)
+ self.assertEqual(f2.place_info()['y'], '0')
+ self.root.update()
+ self.assertEqual(f2.winfo_y(), 40)
+ f2.place_configure(y=50)
+ self.assertEqual(f2.place_info()['y'], '50')
+ self.root.update()
+ self.assertEqual(f2.winfo_y(), 90)
+ f2.place_configure(y=-10, rely=1)
+ self.assertEqual(f2.place_info()['y'], '-10')
+ self.root.update()
+ self.assertEqual(f2.winfo_y(), 110)
+ with self.assertRaisesRegex(TclError, 'bad screen distance "spam"'):
+ f2.place_configure(in_=f, y='spam')
+
+ def test_place_configure_relx(self):
+ t, f, f2 = self.create2()
+ f2.place_configure(in_=f)
+ self.assertEqual(f2.place_info()['relx'], '0')
+ self.root.update()
+ self.assertEqual(f2.winfo_x(), 50)
+ f2.place_configure(relx=0.5)
+ self.assertEqual(f2.place_info()['relx'], '0.5')
+ self.root.update()
+ self.assertEqual(f2.winfo_x(), 125)
+ f2.place_configure(relx=1)
+ self.assertEqual(f2.place_info()['relx'], '1')
+ self.root.update()
+ self.assertEqual(f2.winfo_x(), 200)
+ with self.assertRaisesRegex(TclError, 'expected floating-point number '
+ 'but got "spam"'):
+ f2.place_configure(in_=f, relx='spam')
+
+ def test_place_configure_rely(self):
+ t, f, f2 = self.create2()
+ f2.place_configure(in_=f)
+ self.assertEqual(f2.place_info()['rely'], '0')
+ self.root.update()
+ self.assertEqual(f2.winfo_y(), 40)
+ f2.place_configure(rely=0.5)
+ self.assertEqual(f2.place_info()['rely'], '0.5')
+ self.root.update()
+ self.assertEqual(f2.winfo_y(), 80)
+ f2.place_configure(rely=1)
+ self.assertEqual(f2.place_info()['rely'], '1')
+ self.root.update()
+ self.assertEqual(f2.winfo_y(), 120)
+ with self.assertRaisesRegex(TclError, 'expected floating-point number '
+ 'but got "spam"'):
+ f2.place_configure(in_=f, rely='spam')
+
+ def test_place_configure_anchor(self):
+ f = tkinter.Frame(self.root)
+ with self.assertRaisesRegex(TclError, 'bad anchor "j"'):
+ f.place_configure(anchor='j')
+ with self.assertRaisesRegex(TclError, 'ambiguous anchor ""'):
+ f.place_configure(anchor='')
+ for value in 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center':
+ f.place_configure(anchor=value)
+ self.assertEqual(f.place_info()['anchor'], value)
+
+ def test_place_configure_width(self):
+ t, f, f2 = self.create2()
+ f2.place_configure(in_=f, width=120)
+ self.root.update()
+ self.assertEqual(f2.winfo_width(), 120)
+ f2.place_configure(width='')
+ self.root.update()
+ self.assertEqual(f2.winfo_width(), 30)
+ with self.assertRaisesRegex(TclError, 'bad screen distance "abcd"'):
+ f2.place_configure(width='abcd')
+
+ def test_place_configure_height(self):
+ t, f, f2 = self.create2()
+ f2.place_configure(in_=f, height=120)
+ self.root.update()
+ self.assertEqual(f2.winfo_height(), 120)
+ f2.place_configure(height='')
+ self.root.update()
+ self.assertEqual(f2.winfo_height(), 60)
+ with self.assertRaisesRegex(TclError, 'bad screen distance "abcd"'):
+ f2.place_configure(height='abcd')
+
+ def test_place_configure_relwidth(self):
+ t, f, f2 = self.create2()
+ f2.place_configure(in_=f, relwidth=0.5)
+ self.root.update()
+ self.assertEqual(f2.winfo_width(), 75)
+ f2.place_configure(relwidth='')
+ self.root.update()
+ self.assertEqual(f2.winfo_width(), 30)
+ with self.assertRaisesRegex(TclError, 'expected floating-point number '
+ 'but got "abcd"'):
+ f2.place_configure(relwidth='abcd')
+
+ def test_place_configure_relheight(self):
+ t, f, f2 = self.create2()
+ f2.place_configure(in_=f, relheight=0.5)
+ self.root.update()
+ self.assertEqual(f2.winfo_height(), 40)
+ f2.place_configure(relheight='')
+ self.root.update()
+ self.assertEqual(f2.winfo_height(), 60)
+ with self.assertRaisesRegex(TclError, 'expected floating-point number '
+ 'but got "abcd"'):
+ f2.place_configure(relheight='abcd')
+
+ def test_place_configure_bordermode(self):
+ f = tkinter.Frame(self.root)
+ with self.assertRaisesRegex(TclError, 'bad bordermode "j"'):
+ f.place_configure(bordermode='j')
+ with self.assertRaisesRegex(TclError, 'ambiguous bordermode ""'):
+ f.place_configure(bordermode='')
+ for value in 'inside', 'outside', 'ignore':
+ f.place_configure(bordermode=value)
+ self.assertEqual(f.place_info()['bordermode'], value)
+
+ def test_place_forget(self):
+ foo = tkinter.Frame(self.root)
+ foo.place_configure(width=50, height=50)
+ self.root.update()
+ foo.place_forget()
+ self.root.update()
+ self.assertFalse(foo.winfo_ismapped())
+ with self.assertRaises(TypeError):
+ foo.place_forget(0)
+
+ def test_place_info(self):
+ t, f, f2 = self.create2()
+ f2.place_configure(in_=f, x=1, y=2, width=3, height=4,
+ relx=0.1, rely=0.2, relwidth=0.3, relheight=0.4,
+ anchor='se', bordermode='outside')
+ info = f2.place_info()
+ self.assertIsInstance(info, dict)
+ self.assertEqual(info['x'], '1')
+ self.assertEqual(info['y'], '2')
+ self.assertEqual(info['width'], '3')
+ self.assertEqual(info['height'], '4')
+ self.assertEqual(info['relx'], '0.1')
+ self.assertEqual(info['rely'], '0.2')
+ self.assertEqual(info['relwidth'], '0.3')
+ self.assertEqual(info['relheight'], '0.4')
+ self.assertEqual(info['anchor'], 'se')
+ self.assertEqual(info['bordermode'], 'outside')
+ self.assertEqual(info['x'], '1')
+ self.assertEqual(info['x'], '1')
+ with self.assertRaises(TypeError):
+ f2.place_info(0)
+
+ def test_place_slaves(self):
+ foo = tkinter.Frame(self.root)
+ bar = tkinter.Frame(self.root)
+ self.assertEqual(foo.place_slaves(), [])
+ bar.place_configure(in_=foo)
+ self.assertEqual(foo.place_slaves(), [bar])
+ with self.assertRaises(TypeError):
+ foo.place_slaves(0)
+
+
+class GridTest(AbstractWidgetTest, unittest.TestCase):
+
+ test_keys = None
+
+ def tearDown(self):
+ cols, rows = self.root.grid_size()
+ for i in range(cols + 1):
+ self.root.grid_columnconfigure(i, weight=0, minsize=0, pad=0, uniform='')
+ for i in range(rows + 1):
+ self.root.grid_rowconfigure(i, weight=0, minsize=0, pad=0, uniform='')
+ self.root.grid_propagate(1)
+ self.root.grid_anchor('nw')
+ super().tearDown()
+
+ def test_grid_configure(self):
+ b = tkinter.Button(self.root)
+ self.assertEqual(b.grid_info(), {})
+ b.grid_configure()
+ self.assertEqual(b.grid_info()['in'], self.root)
+ self.assertEqual(b.grid_info()['column'], self._str(0))
+ self.assertEqual(b.grid_info()['row'], self._str(0))
+ b.grid_configure({'column': 1}, row=2)
+ self.assertEqual(b.grid_info()['column'], self._str(1))
+ self.assertEqual(b.grid_info()['row'], self._str(2))
+
+ def test_grid_configure_column(self):
+ b = tkinter.Button(self.root)
+ with self.assertRaisesRegex(TclError, 'bad column value "-1": '
+ 'must be a non-negative integer'):
+ b.grid_configure(column=-1)
+ b.grid_configure(column=2)
+ self.assertEqual(b.grid_info()['column'], self._str(2))
+
+ def test_grid_configure_columnspan(self):
+ b = tkinter.Button(self.root)
+ with self.assertRaisesRegex(TclError, 'bad columnspan value "0": '
+ 'must be a positive integer'):
+ b.grid_configure(columnspan=0)
+ b.grid_configure(columnspan=2)
+ self.assertEqual(b.grid_info()['columnspan'], self._str(2))
+
+ def test_grid_configure_in(self):
+ f = tkinter.Frame(self.root)
+ b = tkinter.Button(self.root)
+ self.assertEqual(b.grid_info(), {})
+ b.grid_configure()
+ self.assertEqual(b.grid_info()['in'], self.root)
+ b.grid_configure(in_=f)
+ self.assertEqual(b.grid_info()['in'], f)
+ b.grid_configure({'in': self.root})
+ self.assertEqual(b.grid_info()['in'], self.root)
+
+ def test_grid_configure_ipadx(self):
+ b = tkinter.Button(self.root)
+ with self.assertRaisesRegex(TclError, 'bad ipadx value "-1": '
+ 'must be positive screen distance'):
+ b.grid_configure(ipadx=-1)
+ b.grid_configure(ipadx=1)
+ self.assertEqual(b.grid_info()['ipadx'], self._str(1))
+ b.grid_configure(ipadx='.5c')
+ self.assertEqual(b.grid_info()['ipadx'],
+ self._str(round(pixels_conv('.5c') * self.scaling)))
+
+ def test_grid_configure_ipady(self):
+ b = tkinter.Button(self.root)
+ with self.assertRaisesRegex(TclError, 'bad ipady value "-1": '
+ 'must be positive screen distance'):
+ b.grid_configure(ipady=-1)
+ b.grid_configure(ipady=1)
+ self.assertEqual(b.grid_info()['ipady'], self._str(1))
+ b.grid_configure(ipady='.5c')
+ self.assertEqual(b.grid_info()['ipady'],
+ self._str(round(pixels_conv('.5c') * self.scaling)))
+
+ def test_grid_configure_padx(self):
+ b = tkinter.Button(self.root)
+ with self.assertRaisesRegex(TclError, 'bad pad value "-1": '
+ 'must be positive screen distance'):
+ b.grid_configure(padx=-1)
+ b.grid_configure(padx=1)
+ self.assertEqual(b.grid_info()['padx'], self._str(1))
+ b.grid_configure(padx=(10, 5))
+ self.assertEqual(b.grid_info()['padx'], self._str((10, 5)))
+ b.grid_configure(padx='.5c')
+ self.assertEqual(b.grid_info()['padx'],
+ self._str(round(pixels_conv('.5c') * self.scaling)))
+
+ def test_grid_configure_pady(self):
+ b = tkinter.Button(self.root)
+ with self.assertRaisesRegex(TclError, 'bad pad value "-1": '
+ 'must be positive screen distance'):
+ b.grid_configure(pady=-1)
+ b.grid_configure(pady=1)
+ self.assertEqual(b.grid_info()['pady'], self._str(1))
+ b.grid_configure(pady=(10, 5))
+ self.assertEqual(b.grid_info()['pady'], self._str((10, 5)))
+ b.grid_configure(pady='.5c')
+ self.assertEqual(b.grid_info()['pady'],
+ self._str(round(pixels_conv('.5c') * self.scaling)))
+
+ def test_grid_configure_row(self):
+ b = tkinter.Button(self.root)
+ with self.assertRaisesRegex(TclError, 'bad (row|grid) value "-1": '
+ 'must be a non-negative integer'):
+ b.grid_configure(row=-1)
+ b.grid_configure(row=2)
+ self.assertEqual(b.grid_info()['row'], self._str(2))
+
+ def test_grid_configure_rownspan(self):
+ b = tkinter.Button(self.root)
+ with self.assertRaisesRegex(TclError, 'bad rowspan value "0": '
+ 'must be a positive integer'):
+ b.grid_configure(rowspan=0)
+ b.grid_configure(rowspan=2)
+ self.assertEqual(b.grid_info()['rowspan'], self._str(2))
+
+ def test_grid_configure_sticky(self):
+ f = tkinter.Frame(self.root, bg='red')
+ with self.assertRaisesRegex(TclError, 'bad stickyness value "glue"'):
+ f.grid_configure(sticky='glue')
+ f.grid_configure(sticky='ne')
+ self.assertEqual(f.grid_info()['sticky'], 'ne')
+ f.grid_configure(sticky='n,s,e,w')
+ self.assertEqual(f.grid_info()['sticky'], 'nesw')
+
+ def test_grid_columnconfigure(self):
+ with self.assertRaises(TypeError):
+ self.root.grid_columnconfigure()
+ self.assertEqual(self.root.grid_columnconfigure(0),
+ {'minsize': 0, 'pad': 0, 'uniform': None, 'weight': 0})
+ with self.assertRaisesRegex(TclError, 'bad option "-foo"'):
+ self.root.grid_columnconfigure(0, 'foo')
+ self.root.grid_columnconfigure((0, 3), weight=2)
+ with self.assertRaisesRegex(TclError,
+ 'must specify a single element on retrieval'):
+ self.root.grid_columnconfigure((0, 3))
+ b = tkinter.Button(self.root)
+ b.grid_configure(column=0, row=0)
+ self.root.grid_columnconfigure('all', weight=3)
+ with self.assertRaisesRegex(TclError, 'expected integer but got "all"'):
+ self.root.grid_columnconfigure('all')
+ self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 3)
+ self.assertEqual(self.root.grid_columnconfigure(3, 'weight'), 2)
+ self.assertEqual(self.root.grid_columnconfigure(265, 'weight'), 0)
+ self.root.grid_columnconfigure(b, weight=4)
+ self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 4)
+
+ def test_grid_columnconfigure_minsize(self):
+ with self.assertRaisesRegex(TclError, 'bad screen distance "foo"'):
+ self.root.grid_columnconfigure(0, minsize='foo')
+ self.root.grid_columnconfigure(0, minsize=10)
+ self.assertEqual(self.root.grid_columnconfigure(0, 'minsize'), 10)
+ self.assertEqual(self.root.grid_columnconfigure(0)['minsize'], 10)
+
+ def test_grid_columnconfigure_weight(self):
+ with self.assertRaisesRegex(TclError, 'expected integer but got "bad"'):
+ self.root.grid_columnconfigure(0, weight='bad')
+ with self.assertRaisesRegex(TclError, 'invalid arg "-weight": '
+ 'should be non-negative'):
+ self.root.grid_columnconfigure(0, weight=-3)
+ self.root.grid_columnconfigure(0, weight=3)
+ self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 3)
+ self.assertEqual(self.root.grid_columnconfigure(0)['weight'], 3)
+
+ def test_grid_columnconfigure_pad(self):
+ with self.assertRaisesRegex(TclError, 'bad screen distance "foo"'):
+ self.root.grid_columnconfigure(0, pad='foo')
+ with self.assertRaisesRegex(TclError, 'invalid arg "-pad": '
+ 'should be non-negative'):
+ self.root.grid_columnconfigure(0, pad=-3)
+ self.root.grid_columnconfigure(0, pad=3)
+ self.assertEqual(self.root.grid_columnconfigure(0, 'pad'), 3)
+ self.assertEqual(self.root.grid_columnconfigure(0)['pad'], 3)
+
+ def test_grid_columnconfigure_uniform(self):
+ self.root.grid_columnconfigure(0, uniform='foo')
+ self.assertEqual(self.root.grid_columnconfigure(0, 'uniform'), 'foo')
+ self.assertEqual(self.root.grid_columnconfigure(0)['uniform'], 'foo')
+
+ def test_grid_rowconfigure(self):
+ with self.assertRaises(TypeError):
+ self.root.grid_rowconfigure()
+ self.assertEqual(self.root.grid_rowconfigure(0),
+ {'minsize': 0, 'pad': 0, 'uniform': None, 'weight': 0})
+ with self.assertRaisesRegex(TclError, 'bad option "-foo"'):
+ self.root.grid_rowconfigure(0, 'foo')
+ self.root.grid_rowconfigure((0, 3), weight=2)
+ with self.assertRaisesRegex(TclError,
+ 'must specify a single element on retrieval'):
+ self.root.grid_rowconfigure((0, 3))
+ b = tkinter.Button(self.root)
+ b.grid_configure(column=0, row=0)
+ self.root.grid_rowconfigure('all', weight=3)
+ with self.assertRaisesRegex(TclError, 'expected integer but got "all"'):
+ self.root.grid_rowconfigure('all')
+ self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 3)
+ self.assertEqual(self.root.grid_rowconfigure(3, 'weight'), 2)
+ self.assertEqual(self.root.grid_rowconfigure(265, 'weight'), 0)
+ self.root.grid_rowconfigure(b, weight=4)
+ self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 4)
+
+ def test_grid_rowconfigure_minsize(self):
+ with self.assertRaisesRegex(TclError, 'bad screen distance "foo"'):
+ self.root.grid_rowconfigure(0, minsize='foo')
+ self.root.grid_rowconfigure(0, minsize=10)
+ self.assertEqual(self.root.grid_rowconfigure(0, 'minsize'), 10)
+ self.assertEqual(self.root.grid_rowconfigure(0)['minsize'], 10)
+
+ def test_grid_rowconfigure_weight(self):
+ with self.assertRaisesRegex(TclError, 'expected integer but got "bad"'):
+ self.root.grid_rowconfigure(0, weight='bad')
+ with self.assertRaisesRegex(TclError, 'invalid arg "-weight": '
+ 'should be non-negative'):
+ self.root.grid_rowconfigure(0, weight=-3)
+ self.root.grid_rowconfigure(0, weight=3)
+ self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 3)
+ self.assertEqual(self.root.grid_rowconfigure(0)['weight'], 3)
+
+ def test_grid_rowconfigure_pad(self):
+ with self.assertRaisesRegex(TclError, 'bad screen distance "foo"'):
+ self.root.grid_rowconfigure(0, pad='foo')
+ with self.assertRaisesRegex(TclError, 'invalid arg "-pad": '
+ 'should be non-negative'):
+ self.root.grid_rowconfigure(0, pad=-3)
+ self.root.grid_rowconfigure(0, pad=3)
+ self.assertEqual(self.root.grid_rowconfigure(0, 'pad'), 3)
+ self.assertEqual(self.root.grid_rowconfigure(0)['pad'], 3)
+
+ def test_grid_rowconfigure_uniform(self):
+ self.root.grid_rowconfigure(0, uniform='foo')
+ self.assertEqual(self.root.grid_rowconfigure(0, 'uniform'), 'foo')
+ self.assertEqual(self.root.grid_rowconfigure(0)['uniform'], 'foo')
+
+ def test_grid_forget(self):
+ b = tkinter.Button(self.root)
+ c = tkinter.Button(self.root)
+ b.grid_configure(row=2, column=2, rowspan=2, columnspan=2,
+ padx=3, pady=4, sticky='ns')
+ self.assertEqual(self.root.grid_slaves(), [b])
+ b.grid_forget()
+ c.grid_forget()
+ self.assertEqual(self.root.grid_slaves(), [])
+ self.assertEqual(b.grid_info(), {})
+ b.grid_configure(row=0, column=0)
+ info = b.grid_info()
+ self.assertEqual(info['row'], self._str(0))
+ self.assertEqual(info['column'], self._str(0))
+ self.assertEqual(info['rowspan'], self._str(1))
+ self.assertEqual(info['columnspan'], self._str(1))
+ self.assertEqual(info['padx'], self._str(0))
+ self.assertEqual(info['pady'], self._str(0))
+ self.assertEqual(info['sticky'], '')
+
+ def test_grid_remove(self):
+ b = tkinter.Button(self.root)
+ c = tkinter.Button(self.root)
+ b.grid_configure(row=2, column=2, rowspan=2, columnspan=2,
+ padx=3, pady=4, sticky='ns')
+ self.assertEqual(self.root.grid_slaves(), [b])
+ b.grid_remove()
+ c.grid_remove()
+ self.assertEqual(self.root.grid_slaves(), [])
+ self.assertEqual(b.grid_info(), {})
+ b.grid_configure(row=0, column=0)
+ info = b.grid_info()
+ self.assertEqual(info['row'], self._str(0))
+ self.assertEqual(info['column'], self._str(0))
+ self.assertEqual(info['rowspan'], self._str(2))
+ self.assertEqual(info['columnspan'], self._str(2))
+ self.assertEqual(info['padx'], self._str(3))
+ self.assertEqual(info['pady'], self._str(4))
+ self.assertEqual(info['sticky'], 'ns')
+
+ def test_grid_info(self):
+ b = tkinter.Button(self.root)
+ self.assertEqual(b.grid_info(), {})
+ b.grid_configure(row=2, column=2, rowspan=2, columnspan=2,
+ padx=3, pady=4, sticky='ns')
+ info = b.grid_info()
+ self.assertIsInstance(info, dict)
+ self.assertEqual(info['in'], self.root)
+ self.assertEqual(info['row'], self._str(2))
+ self.assertEqual(info['column'], self._str(2))
+ self.assertEqual(info['rowspan'], self._str(2))
+ self.assertEqual(info['columnspan'], self._str(2))
+ self.assertEqual(info['padx'], self._str(3))
+ self.assertEqual(info['pady'], self._str(4))
+ self.assertEqual(info['sticky'], 'ns')
+
+ def test_grid_anchor(self):
+ with self.assertRaisesRegex(TclError, 'bad anchor "x"'):
+ self.root.grid_anchor('x')
+ with self.assertRaisesRegex(TclError, 'ambiguous anchor ""'):
+ self.root.grid_anchor('')
+ with self.assertRaises(TypeError):
+ self.root.grid_anchor('se', 'nw')
+ self.root.grid_anchor('se')
+ self.assertEqual(self.root.tk.call('grid', 'anchor', self.root), 'se')
+
+ def test_grid_bbox(self):
+ self.assertEqual(self.root.grid_bbox(), (0, 0, 0, 0))
+ self.assertEqual(self.root.grid_bbox(0, 0), (0, 0, 0, 0))
+ self.assertEqual(self.root.grid_bbox(0, 0, 1, 1), (0, 0, 0, 0))
+ with self.assertRaisesRegex(TclError, 'expected integer but got "x"'):
+ self.root.grid_bbox('x', 0)
+ with self.assertRaisesRegex(TclError, 'expected integer but got "x"'):
+ self.root.grid_bbox(0, 'x')
+ with self.assertRaisesRegex(TclError, 'expected integer but got "x"'):
+ self.root.grid_bbox(0, 0, 'x', 0)
+ with self.assertRaisesRegex(TclError, 'expected integer but got "x"'):
+ self.root.grid_bbox(0, 0, 0, 'x')
+ with self.assertRaises(TypeError):
+ self.root.grid_bbox(0, 0, 0, 0, 0)
+ t = self.root
+ # de-maximize
+ t.wm_geometry('1x1+0+0')
+ t.wm_geometry('')
+ f1 = tkinter.Frame(t, width=75, height=75, bg='red')
+ f2 = tkinter.Frame(t, width=90, height=90, bg='blue')
+ f1.grid_configure(row=0, column=0)
+ f2.grid_configure(row=1, column=1)
+ self.root.update()
+ self.assertEqual(t.grid_bbox(), (0, 0, 165, 165))
+ self.assertEqual(t.grid_bbox(0, 0), (0, 0, 75, 75))
+ self.assertEqual(t.grid_bbox(0, 0, 1, 1), (0, 0, 165, 165))
+ self.assertEqual(t.grid_bbox(1, 1), (75, 75, 90, 90))
+ self.assertEqual(t.grid_bbox(10, 10, 0, 0), (0, 0, 165, 165))
+ self.assertEqual(t.grid_bbox(-2, -2, -1, -1), (0, 0, 0, 0))
+ self.assertEqual(t.grid_bbox(10, 10, 12, 12), (165, 165, 0, 0))
+
+ def test_grid_location(self):
+ with self.assertRaises(TypeError):
+ self.root.grid_location()
+ with self.assertRaises(TypeError):
+ self.root.grid_location(0)
+ with self.assertRaises(TypeError):
+ self.root.grid_location(0, 0, 0)
+ with self.assertRaisesRegex(TclError, 'bad screen distance "x"'):
+ self.root.grid_location('x', 'y')
+ with self.assertRaisesRegex(TclError, 'bad screen distance "y"'):
+ self.root.grid_location('1c', 'y')
+ t = self.root
+ # de-maximize
+ t.wm_geometry('1x1+0+0')
+ t.wm_geometry('')
+ f = tkinter.Frame(t, width=200, height=100,
+ highlightthickness=0, bg='red')
+ self.assertEqual(f.grid_location(10, 10), (-1, -1))
+ f.grid_configure()
+ self.root.update()
+ self.assertEqual(t.grid_location(-10, -10), (-1, -1))
+ self.assertEqual(t.grid_location(-10, 0), (-1, 0))
+ self.assertEqual(t.grid_location(-1, 0), (-1, 0))
+ self.assertEqual(t.grid_location(0, -10), (0, -1))
+ self.assertEqual(t.grid_location(0, -1), (0, -1))
+ self.assertEqual(t.grid_location(0, 0), (0, 0))
+ self.assertEqual(t.grid_location(200, 0), (0, 0))
+ self.assertEqual(t.grid_location(201, 0), (1, 0))
+ self.assertEqual(t.grid_location(0, 100), (0, 0))
+ self.assertEqual(t.grid_location(0, 101), (0, 1))
+ self.assertEqual(t.grid_location(201, 101), (1, 1))
+
+ def test_grid_propagate(self):
+ self.assertEqual(self.root.grid_propagate(), True)
+ with self.assertRaises(TypeError):
+ self.root.grid_propagate(False, False)
+ self.root.grid_propagate(False)
+ self.assertFalse(self.root.grid_propagate())
+ f = tkinter.Frame(self.root, width=100, height=100, bg='red')
+ f.grid_configure(row=0, column=0)
+ self.root.update()
+ self.assertEqual(f.winfo_width(), 100)
+ self.assertEqual(f.winfo_height(), 100)
+ f.grid_propagate(False)
+ g = tkinter.Frame(self.root, width=75, height=85, bg='green')
+ g.grid_configure(in_=f, row=0, column=0)
+ self.root.update()
+ self.assertEqual(f.winfo_width(), 100)
+ self.assertEqual(f.winfo_height(), 100)
+ f.grid_propagate(True)
+ self.root.update()
+ self.assertEqual(f.winfo_width(), 75)
+ self.assertEqual(f.winfo_height(), 85)
+
+ def test_grid_size(self):
+ with self.assertRaises(TypeError):
+ self.root.grid_size(0)
+ self.assertEqual(self.root.grid_size(), (0, 0))
+ f = tkinter.Scale(self.root)
+ f.grid_configure(row=0, column=0)
+ self.assertEqual(self.root.grid_size(), (1, 1))
+ f.grid_configure(row=4, column=5)
+ self.assertEqual(self.root.grid_size(), (6, 5))
+
+ def test_grid_slaves(self):
+ self.assertEqual(self.root.grid_slaves(), [])
+ a = tkinter.Label(self.root)
+ a.grid_configure(row=0, column=1)
+ b = tkinter.Label(self.root)
+ b.grid_configure(row=1, column=0)
+ c = tkinter.Label(self.root)
+ c.grid_configure(row=1, column=1)
+ d = tkinter.Label(self.root)
+ d.grid_configure(row=1, column=1)
+ self.assertEqual(self.root.grid_slaves(), [d, c, b, a])
+ self.assertEqual(self.root.grid_slaves(row=0), [a])
+ self.assertEqual(self.root.grid_slaves(row=1), [d, c, b])
+ self.assertEqual(self.root.grid_slaves(column=0), [b])
+ self.assertEqual(self.root.grid_slaves(column=1), [d, c, a])
+ self.assertEqual(self.root.grid_slaves(row=1, column=1), [d, c])
+
+
+tests_gui = (
+ PackTest, PlaceTest, GridTest,
+)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Lib/test/test_tkinter/test_images.py b/Lib/test/test_tkinter/test_images.py
new file mode 100644
index 0000000..b6f8b79
--- /dev/null
+++ b/Lib/test/test_tkinter/test_images.py
@@ -0,0 +1,380 @@
+import unittest
+import tkinter
+from test import support
+from test.support import os_helper
+from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest, requires_tcl
+
+support.requires('gui')
+
+
+class MiscTest(AbstractTkTest, unittest.TestCase):
+
+ def test_image_types(self):
+ image_types = self.root.image_types()
+ self.assertIsInstance(image_types, tuple)
+ self.assertIn('photo', image_types)
+ self.assertIn('bitmap', image_types)
+
+ def test_image_names(self):
+ image_names = self.root.image_names()
+ self.assertIsInstance(image_names, tuple)
+
+
+class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
+
+ def test_image_types(self):
+ self.assertRaises(RuntimeError, tkinter.image_types)
+ root = tkinter.Tk()
+ image_types = tkinter.image_types()
+ self.assertIsInstance(image_types, tuple)
+ self.assertIn('photo', image_types)
+ self.assertIn('bitmap', image_types)
+ root.destroy()
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, tkinter.image_types)
+
+ def test_image_names(self):
+ self.assertRaises(RuntimeError, tkinter.image_names)
+ root = tkinter.Tk()
+ image_names = tkinter.image_names()
+ self.assertIsInstance(image_names, tuple)
+ root.destroy()
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, tkinter.image_names)
+
+ def test_image_create_bitmap(self):
+ self.assertRaises(RuntimeError, tkinter.BitmapImage)
+ root = tkinter.Tk()
+ image = tkinter.BitmapImage()
+ self.assertIn(image.name, tkinter.image_names())
+ root.destroy()
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, tkinter.BitmapImage)
+
+ def test_image_create_photo(self):
+ self.assertRaises(RuntimeError, tkinter.PhotoImage)
+ root = tkinter.Tk()
+ image = tkinter.PhotoImage()
+ self.assertIn(image.name, tkinter.image_names())
+ root.destroy()
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, tkinter.PhotoImage)
+
+
+class BitmapImageTest(AbstractTkTest, unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ AbstractTkTest.setUpClass.__func__(cls)
+ cls.testfile = support.findfile('python.xbm', subdir='imghdrdata')
+
+ def test_create_from_file(self):
+ image = tkinter.BitmapImage('::img::test', master=self.root,
+ foreground='yellow', background='blue',
+ file=self.testfile)
+ self.assertEqual(str(image), '::img::test')
+ self.assertEqual(image.type(), 'bitmap')
+ self.assertEqual(image.width(), 16)
+ self.assertEqual(image.height(), 16)
+ self.assertIn('::img::test', self.root.image_names())
+ del image
+ support.gc_collect() # For PyPy or other GCs.
+ self.assertNotIn('::img::test', self.root.image_names())
+
+ def test_create_from_data(self):
+ with open(self.testfile, 'rb') as f:
+ data = f.read()
+ image = tkinter.BitmapImage('::img::test', master=self.root,
+ foreground='yellow', background='blue',
+ data=data)
+ self.assertEqual(str(image), '::img::test')
+ self.assertEqual(image.type(), 'bitmap')
+ self.assertEqual(image.width(), 16)
+ self.assertEqual(image.height(), 16)
+ self.assertIn('::img::test', self.root.image_names())
+ del image
+ support.gc_collect() # For PyPy or other GCs.
+ self.assertNotIn('::img::test', self.root.image_names())
+
+ def assertEqualStrList(self, actual, expected):
+ self.assertIsInstance(actual, str)
+ self.assertEqual(self.root.splitlist(actual), expected)
+
+ def test_configure_data(self):
+ image = tkinter.BitmapImage('::img::test', master=self.root)
+ self.assertEqual(image['data'], '-data {} {} {} {}')
+ with open(self.testfile, 'rb') as f:
+ data = f.read()
+ image.configure(data=data)
+ self.assertEqualStrList(image['data'],
+ ('-data', '', '', '', data.decode('ascii')))
+ self.assertEqual(image.width(), 16)
+ self.assertEqual(image.height(), 16)
+
+ self.assertEqual(image['maskdata'], '-maskdata {} {} {} {}')
+ image.configure(maskdata=data)
+ self.assertEqualStrList(image['maskdata'],
+ ('-maskdata', '', '', '', data.decode('ascii')))
+
+ def test_configure_file(self):
+ image = tkinter.BitmapImage('::img::test', master=self.root)
+ self.assertEqual(image['file'], '-file {} {} {} {}')
+ image.configure(file=self.testfile)
+ self.assertEqualStrList(image['file'],
+ ('-file', '', '', '',self.testfile))
+ self.assertEqual(image.width(), 16)
+ self.assertEqual(image.height(), 16)
+
+ self.assertEqual(image['maskfile'], '-maskfile {} {} {} {}')
+ image.configure(maskfile=self.testfile)
+ self.assertEqualStrList(image['maskfile'],
+ ('-maskfile', '', '', '', self.testfile))
+
+ def test_configure_background(self):
+ image = tkinter.BitmapImage('::img::test', master=self.root)
+ self.assertEqual(image['background'], '-background {} {} {} {}')
+ image.configure(background='blue')
+ self.assertEqual(image['background'], '-background {} {} {} blue')
+
+ def test_configure_foreground(self):
+ image = tkinter.BitmapImage('::img::test', master=self.root)
+ self.assertEqual(image['foreground'],
+ '-foreground {} {} #000000 #000000')
+ image.configure(foreground='yellow')
+ self.assertEqual(image['foreground'],
+ '-foreground {} {} #000000 yellow')
+
+
+class PhotoImageTest(AbstractTkTest, unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ AbstractTkTest.setUpClass.__func__(cls)
+ cls.testfile = support.findfile('python.gif', subdir='imghdrdata')
+
+ def create(self):
+ return tkinter.PhotoImage('::img::test', master=self.root,
+ file=self.testfile)
+
+ def colorlist(self, *args):
+ if tkinter.TkVersion >= 8.6 and self.wantobjects:
+ return args
+ else:
+ return tkinter._join(args)
+
+ def check_create_from_file(self, ext):
+ testfile = support.findfile('python.' + ext, subdir='imghdrdata')
+ image = tkinter.PhotoImage('::img::test', master=self.root,
+ file=testfile)
+ self.assertEqual(str(image), '::img::test')
+ self.assertEqual(image.type(), 'photo')
+ self.assertEqual(image.width(), 16)
+ self.assertEqual(image.height(), 16)
+ self.assertEqual(image['data'], '')
+ self.assertEqual(image['file'], testfile)
+ self.assertIn('::img::test', self.root.image_names())
+ del image
+ support.gc_collect() # For PyPy or other GCs.
+ self.assertNotIn('::img::test', self.root.image_names())
+
+ def check_create_from_data(self, ext):
+ testfile = support.findfile('python.' + ext, subdir='imghdrdata')
+ with open(testfile, 'rb') as f:
+ data = f.read()
+ image = tkinter.PhotoImage('::img::test', master=self.root,
+ data=data)
+ self.assertEqual(str(image), '::img::test')
+ self.assertEqual(image.type(), 'photo')
+ self.assertEqual(image.width(), 16)
+ self.assertEqual(image.height(), 16)
+ self.assertEqual(image['data'], data if self.wantobjects
+ else data.decode('latin1'))
+ self.assertEqual(image['file'], '')
+ self.assertIn('::img::test', self.root.image_names())
+ del image
+ support.gc_collect() # For PyPy or other GCs.
+ self.assertNotIn('::img::test', self.root.image_names())
+
+ def test_create_from_ppm_file(self):
+ self.check_create_from_file('ppm')
+
+ def test_create_from_ppm_data(self):
+ self.check_create_from_data('ppm')
+
+ def test_create_from_pgm_file(self):
+ self.check_create_from_file('pgm')
+
+ def test_create_from_pgm_data(self):
+ self.check_create_from_data('pgm')
+
+ def test_create_from_gif_file(self):
+ self.check_create_from_file('gif')
+
+ def test_create_from_gif_data(self):
+ self.check_create_from_data('gif')
+
+ @requires_tcl(8, 6)
+ def test_create_from_png_file(self):
+ self.check_create_from_file('png')
+
+ @requires_tcl(8, 6)
+ def test_create_from_png_data(self):
+ self.check_create_from_data('png')
+
+ def test_configure_data(self):
+ image = tkinter.PhotoImage('::img::test', master=self.root)
+ self.assertEqual(image['data'], '')
+ with open(self.testfile, 'rb') as f:
+ data = f.read()
+ image.configure(data=data)
+ self.assertEqual(image['data'], data if self.wantobjects
+ else data.decode('latin1'))
+ self.assertEqual(image.width(), 16)
+ self.assertEqual(image.height(), 16)
+
+ def test_configure_format(self):
+ image = tkinter.PhotoImage('::img::test', master=self.root)
+ self.assertEqual(image['format'], '')
+ image.configure(file=self.testfile, format='gif')
+ self.assertEqual(image['format'], ('gif',) if self.wantobjects
+ else 'gif')
+ self.assertEqual(image.width(), 16)
+ self.assertEqual(image.height(), 16)
+
+ def test_configure_file(self):
+ image = tkinter.PhotoImage('::img::test', master=self.root)
+ self.assertEqual(image['file'], '')
+ image.configure(file=self.testfile)
+ self.assertEqual(image['file'], self.testfile)
+ self.assertEqual(image.width(), 16)
+ self.assertEqual(image.height(), 16)
+
+ def test_configure_gamma(self):
+ image = tkinter.PhotoImage('::img::test', master=self.root)
+ self.assertEqual(image['gamma'], '1.0')
+ image.configure(gamma=2.0)
+ self.assertEqual(image['gamma'], '2.0')
+
+ def test_configure_width_height(self):
+ image = tkinter.PhotoImage('::img::test', master=self.root)
+ self.assertEqual(image['width'], '0')
+ self.assertEqual(image['height'], '0')
+ image.configure(width=20)
+ image.configure(height=10)
+ self.assertEqual(image['width'], '20')
+ self.assertEqual(image['height'], '10')
+ self.assertEqual(image.width(), 20)
+ self.assertEqual(image.height(), 10)
+
+ def test_configure_palette(self):
+ image = tkinter.PhotoImage('::img::test', master=self.root)
+ self.assertEqual(image['palette'], '')
+ image.configure(palette=256)
+ self.assertEqual(image['palette'], '256')
+ image.configure(palette='3/4/2')
+ self.assertEqual(image['palette'], '3/4/2')
+
+ def test_blank(self):
+ image = self.create()
+ image.blank()
+ self.assertEqual(image.width(), 16)
+ self.assertEqual(image.height(), 16)
+ self.assertEqual(image.get(4, 6), self.colorlist(0, 0, 0))
+
+ def test_copy(self):
+ image = self.create()
+ image2 = image.copy()
+ self.assertEqual(image2.width(), 16)
+ self.assertEqual(image2.height(), 16)
+ self.assertEqual(image.get(4, 6), image.get(4, 6))
+
+ def test_subsample(self):
+ image = self.create()
+ image2 = image.subsample(2, 3)
+ self.assertEqual(image2.width(), 8)
+ self.assertEqual(image2.height(), 6)
+ self.assertEqual(image2.get(2, 2), image.get(4, 6))
+
+ image2 = image.subsample(2)
+ self.assertEqual(image2.width(), 8)
+ self.assertEqual(image2.height(), 8)
+ self.assertEqual(image2.get(2, 3), image.get(4, 6))
+
+ def test_zoom(self):
+ image = self.create()
+ image2 = image.zoom(2, 3)
+ self.assertEqual(image2.width(), 32)
+ self.assertEqual(image2.height(), 48)
+ self.assertEqual(image2.get(8, 18), image.get(4, 6))
+ self.assertEqual(image2.get(9, 20), image.get(4, 6))
+
+ image2 = image.zoom(2)
+ self.assertEqual(image2.width(), 32)
+ self.assertEqual(image2.height(), 32)
+ self.assertEqual(image2.get(8, 12), image.get(4, 6))
+ self.assertEqual(image2.get(9, 13), image.get(4, 6))
+
+ def test_put(self):
+ image = self.create()
+ image.put('{red green} {blue yellow}', to=(4, 6))
+ self.assertEqual(image.get(4, 6), self.colorlist(255, 0, 0))
+ self.assertEqual(image.get(5, 6),
+ self.colorlist(0, 128 if tkinter.TkVersion >= 8.6
+ else 255, 0))
+ self.assertEqual(image.get(4, 7), self.colorlist(0, 0, 255))
+ self.assertEqual(image.get(5, 7), self.colorlist(255, 255, 0))
+
+ image.put((('#f00', '#00ff00'), ('#000000fff', '#ffffffff0000')))
+ self.assertEqual(image.get(0, 0), self.colorlist(255, 0, 0))
+ self.assertEqual(image.get(1, 0), self.colorlist(0, 255, 0))
+ self.assertEqual(image.get(0, 1), self.colorlist(0, 0, 255))
+ self.assertEqual(image.get(1, 1), self.colorlist(255, 255, 0))
+
+ def test_get(self):
+ image = self.create()
+ self.assertEqual(image.get(4, 6), self.colorlist(62, 116, 162))
+ self.assertEqual(image.get(0, 0), self.colorlist(0, 0, 0))
+ self.assertEqual(image.get(15, 15), self.colorlist(0, 0, 0))
+ self.assertRaises(tkinter.TclError, image.get, -1, 0)
+ self.assertRaises(tkinter.TclError, image.get, 0, -1)
+ self.assertRaises(tkinter.TclError, image.get, 16, 15)
+ self.assertRaises(tkinter.TclError, image.get, 15, 16)
+
+ def test_write(self):
+ image = self.create()
+ self.addCleanup(os_helper.unlink, os_helper.TESTFN)
+
+ image.write(os_helper.TESTFN)
+ image2 = tkinter.PhotoImage('::img::test2', master=self.root,
+ format='ppm',
+ file=os_helper.TESTFN)
+ self.assertEqual(str(image2), '::img::test2')
+ self.assertEqual(image2.type(), 'photo')
+ self.assertEqual(image2.width(), 16)
+ self.assertEqual(image2.height(), 16)
+ self.assertEqual(image2.get(0, 0), image.get(0, 0))
+ self.assertEqual(image2.get(15, 8), image.get(15, 8))
+
+ image.write(os_helper.TESTFN, format='gif', from_coords=(4, 6, 6, 9))
+ image3 = tkinter.PhotoImage('::img::test3', master=self.root,
+ format='gif',
+ file=os_helper.TESTFN)
+ self.assertEqual(str(image3), '::img::test3')
+ self.assertEqual(image3.type(), 'photo')
+ self.assertEqual(image3.width(), 2)
+ self.assertEqual(image3.height(), 3)
+ self.assertEqual(image3.get(0, 0), image.get(4, 6))
+ self.assertEqual(image3.get(1, 2), image.get(5, 8))
+
+ def test_transparency(self):
+ image = self.create()
+ self.assertEqual(image.transparency_get(0, 0), True)
+ self.assertEqual(image.transparency_get(4, 6), False)
+ image.transparency_set(4, 6, True)
+ self.assertEqual(image.transparency_get(4, 6), True)
+ image.transparency_set(4, 6, False)
+ self.assertEqual(image.transparency_get(4, 6), False)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_tkinter/test_loadtk.py b/Lib/test/test_tkinter/test_loadtk.py
new file mode 100644
index 0000000..61b0eda
--- /dev/null
+++ b/Lib/test/test_tkinter/test_loadtk.py
@@ -0,0 +1,46 @@
+import os
+import sys
+import unittest
+import test.support as test_support
+from test.support import os_helper
+from tkinter import Tcl, TclError
+
+test_support.requires('gui')
+
+class TkLoadTest(unittest.TestCase):
+
+ @unittest.skipIf('DISPLAY' not in os.environ, 'No $DISPLAY set.')
+ def testLoadTk(self):
+ tcl = Tcl()
+ self.assertRaises(TclError,tcl.winfo_geometry)
+ tcl.loadtk()
+ self.assertEqual('1x1+0+0', tcl.winfo_geometry())
+ tcl.destroy()
+
+ def testLoadTkFailure(self):
+ old_display = None
+ if sys.platform.startswith(('win', 'darwin', 'cygwin')):
+ # no failure possible on windows?
+
+ # XXX Maybe on tk older than 8.4.13 it would be possible,
+ # see tkinter.h.
+ return
+ with os_helper.EnvironmentVarGuard() as env:
+ if 'DISPLAY' in os.environ:
+ del env['DISPLAY']
+ # on some platforms, deleting environment variables
+ # doesn't actually carry through to the process level
+ # because they don't support unsetenv
+ # If that's the case, abort.
+ with os.popen('echo $DISPLAY') as pipe:
+ display = pipe.read().strip()
+ if display:
+ return
+
+ tcl = Tcl()
+ self.assertRaises(TclError, tcl.winfo_geometry)
+ self.assertRaises(TclError, tcl.loadtk)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_tkinter/test_messagebox.py b/Lib/test/test_tkinter/test_messagebox.py
new file mode 100644
index 0000000..f41bdc9
--- /dev/null
+++ b/Lib/test/test_tkinter/test_messagebox.py
@@ -0,0 +1,36 @@
+import unittest
+import tkinter
+from test.support import requires, swap_attr
+from test.test_tkinter.support import AbstractDefaultRootTest
+from tkinter.commondialog import Dialog
+from tkinter.messagebox import showinfo
+
+requires('gui')
+
+
+class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
+
+ def test_showinfo(self):
+ def test_callback(dialog, master):
+ nonlocal ismapped
+ master.update()
+ ismapped = master.winfo_ismapped()
+ raise ZeroDivisionError
+
+ with swap_attr(Dialog, '_test_callback', test_callback):
+ ismapped = None
+ self.assertRaises(ZeroDivisionError, showinfo, "Spam", "Egg Information")
+ self.assertEqual(ismapped, False)
+
+ root = tkinter.Tk()
+ ismapped = None
+ self.assertRaises(ZeroDivisionError, showinfo, "Spam", "Egg Information")
+ self.assertEqual(ismapped, True)
+ root.destroy()
+
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, showinfo, "Spam", "Egg Information")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py
new file mode 100644
index 0000000..d1aca58
--- /dev/null
+++ b/Lib/test/test_tkinter/test_misc.py
@@ -0,0 +1,430 @@
+import functools
+import unittest
+import tkinter
+import enum
+from test import support
+from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest
+
+support.requires('gui')
+
+class MiscTest(AbstractTkTest, unittest.TestCase):
+
+ def test_all(self):
+ self.assertIn("Widget", tkinter.__all__)
+ # Check that variables from tkinter.constants are also in tkinter.__all__
+ self.assertIn("CASCADE", tkinter.__all__)
+ self.assertIsNotNone(tkinter.CASCADE)
+ # Check that sys, re, and constants are not in tkinter.__all__
+ self.assertNotIn("re", tkinter.__all__)
+ self.assertNotIn("sys", tkinter.__all__)
+ self.assertNotIn("constants", tkinter.__all__)
+ # Check that an underscored functions is not in tkinter.__all__
+ self.assertNotIn("_tkerror", tkinter.__all__)
+ # Check that wantobjects is not in tkinter.__all__
+ self.assertNotIn("wantobjects", tkinter.__all__)
+
+ def test_repr(self):
+ t = tkinter.Toplevel(self.root, name='top')
+ f = tkinter.Frame(t, name='child')
+ self.assertEqual(repr(f), '<tkinter.Frame object .top.child>')
+
+ def test_generated_names(self):
+ t = tkinter.Toplevel(self.root)
+ f = tkinter.Frame(t)
+ f2 = tkinter.Frame(t)
+ b = tkinter.Button(f2)
+ for name in str(b).split('.'):
+ self.assertFalse(name.isidentifier(), msg=repr(name))
+
+ def test_tk_setPalette(self):
+ root = self.root
+ root.tk_setPalette('black')
+ self.assertEqual(root['background'], 'black')
+ root.tk_setPalette('white')
+ self.assertEqual(root['background'], 'white')
+ self.assertRaisesRegex(tkinter.TclError,
+ '^unknown color name "spam"$',
+ root.tk_setPalette, 'spam')
+
+ root.tk_setPalette(background='black')
+ self.assertEqual(root['background'], 'black')
+ root.tk_setPalette(background='blue', highlightColor='yellow')
+ self.assertEqual(root['background'], 'blue')
+ self.assertEqual(root['highlightcolor'], 'yellow')
+ root.tk_setPalette(background='yellow', highlightColor='blue')
+ self.assertEqual(root['background'], 'yellow')
+ self.assertEqual(root['highlightcolor'], 'blue')
+ self.assertRaisesRegex(tkinter.TclError,
+ '^unknown color name "spam"$',
+ root.tk_setPalette, background='spam')
+ self.assertRaisesRegex(tkinter.TclError,
+ '^must specify a background color$',
+ root.tk_setPalette, spam='white')
+ self.assertRaisesRegex(tkinter.TclError,
+ '^must specify a background color$',
+ root.tk_setPalette, highlightColor='blue')
+
+ def test_after(self):
+ root = self.root
+
+ def callback(start=0, step=1):
+ nonlocal count
+ count = start + step
+
+ # Without function, sleeps for ms.
+ self.assertIsNone(root.after(1))
+
+ # Set up with callback with no args.
+ count = 0
+ timer1 = root.after(0, callback)
+ self.assertIn(timer1, root.tk.call('after', 'info'))
+ (script, _) = root.tk.splitlist(root.tk.call('after', 'info', timer1))
+ root.update() # Process all pending events.
+ self.assertEqual(count, 1)
+ with self.assertRaises(tkinter.TclError):
+ root.tk.call(script)
+
+ # Set up with callback with args.
+ count = 0
+ timer1 = root.after(0, callback, 42, 11)
+ root.update() # Process all pending events.
+ self.assertEqual(count, 53)
+
+ # Cancel before called.
+ timer1 = root.after(1000, callback)
+ self.assertIn(timer1, root.tk.call('after', 'info'))
+ (script, _) = root.tk.splitlist(root.tk.call('after', 'info', timer1))
+ root.after_cancel(timer1) # Cancel this event.
+ self.assertEqual(count, 53)
+ with self.assertRaises(tkinter.TclError):
+ root.tk.call(script)
+
+ # Call with a callable class
+ count = 0
+ timer1 = root.after(0, functools.partial(callback, 42, 11))
+ root.update() # Process all pending events.
+ self.assertEqual(count, 53)
+
+ def test_after_idle(self):
+ root = self.root
+
+ def callback(start=0, step=1):
+ nonlocal count
+ count = start + step
+
+ # Set up with callback with no args.
+ count = 0
+ idle1 = root.after_idle(callback)
+ self.assertIn(idle1, root.tk.call('after', 'info'))
+ (script, _) = root.tk.splitlist(root.tk.call('after', 'info', idle1))
+ root.update_idletasks() # Process all pending events.
+ self.assertEqual(count, 1)
+ with self.assertRaises(tkinter.TclError):
+ root.tk.call(script)
+
+ # Set up with callback with args.
+ count = 0
+ idle1 = root.after_idle(callback, 42, 11)
+ root.update_idletasks() # Process all pending events.
+ self.assertEqual(count, 53)
+
+ # Cancel before called.
+ idle1 = root.after_idle(callback)
+ self.assertIn(idle1, root.tk.call('after', 'info'))
+ (script, _) = root.tk.splitlist(root.tk.call('after', 'info', idle1))
+ root.after_cancel(idle1) # Cancel this event.
+ self.assertEqual(count, 53)
+ with self.assertRaises(tkinter.TclError):
+ root.tk.call(script)
+
+ def test_after_cancel(self):
+ root = self.root
+
+ def callback():
+ nonlocal count
+ count += 1
+
+ timer1 = root.after(5000, callback)
+ idle1 = root.after_idle(callback)
+
+ # No value for id raises a ValueError.
+ with self.assertRaises(ValueError):
+ root.after_cancel(None)
+
+ # Cancel timer event.
+ count = 0
+ (script, _) = root.tk.splitlist(root.tk.call('after', 'info', timer1))
+ root.tk.call(script)
+ self.assertEqual(count, 1)
+ root.after_cancel(timer1)
+ with self.assertRaises(tkinter.TclError):
+ root.tk.call(script)
+ self.assertEqual(count, 1)
+ with self.assertRaises(tkinter.TclError):
+ root.tk.call('after', 'info', timer1)
+
+ # Cancel same event - nothing happens.
+ root.after_cancel(timer1)
+
+ # Cancel idle event.
+ count = 0
+ (script, _) = root.tk.splitlist(root.tk.call('after', 'info', idle1))
+ root.tk.call(script)
+ self.assertEqual(count, 1)
+ root.after_cancel(idle1)
+ with self.assertRaises(tkinter.TclError):
+ root.tk.call(script)
+ self.assertEqual(count, 1)
+ with self.assertRaises(tkinter.TclError):
+ root.tk.call('after', 'info', idle1)
+
+ def test_clipboard(self):
+ root = self.root
+ root.clipboard_clear()
+ root.clipboard_append('Ùñî')
+ self.assertEqual(root.clipboard_get(), 'Ùñî')
+ root.clipboard_append('çōđě')
+ self.assertEqual(root.clipboard_get(), 'Ùñîçōđě')
+ root.clipboard_clear()
+ with self.assertRaises(tkinter.TclError):
+ root.clipboard_get()
+
+ def test_clipboard_astral(self):
+ root = self.root
+ root.clipboard_clear()
+ root.clipboard_append('𝔘𝔫𝔦')
+ self.assertEqual(root.clipboard_get(), '𝔘𝔫𝔦')
+ root.clipboard_append('𝔠𝔬𝔡𝔢')
+ self.assertEqual(root.clipboard_get(), '𝔘𝔫𝔦𝔠𝔬𝔡𝔢')
+ root.clipboard_clear()
+ with self.assertRaises(tkinter.TclError):
+ root.clipboard_get()
+
+ def test_winfo_rgb(self):
+
+ def assertApprox(col1, col2):
+ # A small amount of flexibility is required (bpo-45496)
+ # 33 is ~0.05% of 65535, which is a reasonable margin
+ for col1_channel, col2_channel in zip(col1, col2):
+ self.assertAlmostEqual(col1_channel, col2_channel, delta=33)
+
+ root = self.root
+ rgb = root.winfo_rgb
+
+ # Color name.
+ self.assertEqual(rgb('red'), (65535, 0, 0))
+ self.assertEqual(rgb('dark slate blue'), (18504, 15677, 35723))
+ # #RGB - extends each 4-bit hex value to be 16-bit.
+ self.assertEqual(rgb('#F0F'), (0xFFFF, 0x0000, 0xFFFF))
+ # #RRGGBB - extends each 8-bit hex value to be 16-bit.
+ assertApprox(rgb('#4a3c8c'), (0x4a4a, 0x3c3c, 0x8c8c))
+ # #RRRRGGGGBBBB
+ assertApprox(rgb('#dede14143939'), (0xdede, 0x1414, 0x3939))
+ # Invalid string.
+ with self.assertRaises(tkinter.TclError):
+ rgb('#123456789a')
+ # RGB triplet is invalid input.
+ with self.assertRaises(tkinter.TclError):
+ rgb((111, 78, 55))
+
+ def test_event_repr_defaults(self):
+ e = tkinter.Event()
+ e.serial = 12345
+ e.num = '??'
+ e.height = '??'
+ e.keycode = '??'
+ e.state = 0
+ e.time = 123456789
+ e.width = '??'
+ e.x = '??'
+ e.y = '??'
+ e.char = ''
+ e.keysym = '??'
+ e.keysym_num = '??'
+ e.type = '100'
+ e.widget = '??'
+ e.x_root = '??'
+ e.y_root = '??'
+ e.delta = 0
+ self.assertEqual(repr(e), '<100 event>')
+
+ def test_event_repr(self):
+ e = tkinter.Event()
+ e.serial = 12345
+ e.num = 3
+ e.focus = True
+ e.height = 200
+ e.keycode = 65
+ e.state = 0x30405
+ e.time = 123456789
+ e.width = 300
+ e.x = 10
+ e.y = 20
+ e.char = 'A'
+ e.send_event = True
+ e.keysym = 'Key-A'
+ e.keysym_num = ord('A')
+ e.type = tkinter.EventType.Configure
+ e.widget = '.text'
+ e.x_root = 1010
+ e.y_root = 1020
+ e.delta = -1
+ self.assertEqual(repr(e),
+ "<Configure event send_event=True"
+ " state=Shift|Control|Button3|0x30000"
+ " keysym=Key-A keycode=65 char='A'"
+ " num=3 delta=-1 focus=True"
+ " x=10 y=20 width=300 height=200>")
+
+ def test_eventtype_enum(self):
+ class CheckedEventType(enum.StrEnum):
+ KeyPress = '2'
+ Key = KeyPress
+ KeyRelease = '3'
+ ButtonPress = '4'
+ Button = ButtonPress
+ ButtonRelease = '5'
+ Motion = '6'
+ Enter = '7'
+ Leave = '8'
+ FocusIn = '9'
+ FocusOut = '10'
+ Keymap = '11' # undocumented
+ Expose = '12'
+ GraphicsExpose = '13' # undocumented
+ NoExpose = '14' # undocumented
+ Visibility = '15'
+ Create = '16'
+ Destroy = '17'
+ Unmap = '18'
+ Map = '19'
+ MapRequest = '20'
+ Reparent = '21'
+ Configure = '22'
+ ConfigureRequest = '23'
+ Gravity = '24'
+ ResizeRequest = '25'
+ Circulate = '26'
+ CirculateRequest = '27'
+ Property = '28'
+ SelectionClear = '29' # undocumented
+ SelectionRequest = '30' # undocumented
+ Selection = '31' # undocumented
+ Colormap = '32'
+ ClientMessage = '33' # undocumented
+ Mapping = '34' # undocumented
+ VirtualEvent = '35' # undocumented
+ Activate = '36'
+ Deactivate = '37'
+ MouseWheel = '38'
+ enum._test_simple_enum(CheckedEventType, tkinter.EventType)
+
+ def test_getboolean(self):
+ for v in 'true', 'yes', 'on', '1', 't', 'y', 1, True:
+ self.assertIs(self.root.getboolean(v), True)
+ for v in 'false', 'no', 'off', '0', 'f', 'n', 0, False:
+ self.assertIs(self.root.getboolean(v), False)
+ self.assertRaises(ValueError, self.root.getboolean, 'yea')
+ self.assertRaises(ValueError, self.root.getboolean, '')
+ self.assertRaises(TypeError, self.root.getboolean, None)
+ self.assertRaises(TypeError, self.root.getboolean, ())
+
+ def test_mainloop(self):
+ log = []
+ def callback():
+ log.append(1)
+ self.root.after(100, self.root.quit)
+ self.root.after(100, callback)
+ self.root.mainloop(1)
+ self.assertEqual(log, [])
+ self.root.mainloop(0)
+ self.assertEqual(log, [1])
+ self.assertTrue(self.root.winfo_exists())
+
+ def test_info_patchlevel(self):
+ vi = self.root.info_patchlevel()
+ f = tkinter.Frame(self.root)
+ self.assertEqual(f.info_patchlevel(), vi)
+ # The following is almost a copy of tests for sys.version_info.
+ self.assertIsInstance(vi[:], tuple)
+ self.assertEqual(len(vi), 5)
+ self.assertIsInstance(vi[0], int)
+ self.assertIsInstance(vi[1], int)
+ self.assertIsInstance(vi[2], int)
+ self.assertIn(vi[3], ("alpha", "beta", "candidate", "final"))
+ self.assertIsInstance(vi[4], int)
+ self.assertIsInstance(vi.major, int)
+ self.assertIsInstance(vi.minor, int)
+ self.assertIsInstance(vi.micro, int)
+ self.assertIn(vi.releaselevel, ("alpha", "beta", "final"))
+ self.assertIsInstance(vi.serial, int)
+ self.assertEqual(vi[0], vi.major)
+ self.assertEqual(vi[1], vi.minor)
+ self.assertEqual(vi[2], vi.micro)
+ self.assertEqual(vi[3], vi.releaselevel)
+ self.assertEqual(vi[4], vi.serial)
+ self.assertTrue(vi > (1,0,0))
+ if vi.releaselevel == 'final':
+ self.assertEqual(vi.serial, 0)
+ else:
+ self.assertEqual(vi.micro, 0)
+ self.assertTrue(str(vi).startswith(f'{vi.major}.{vi.minor}'))
+
+
+class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
+
+ def test_default_root(self):
+ self.assertIs(tkinter._support_default_root, True)
+ self.assertIsNone(tkinter._default_root)
+ root = tkinter.Tk()
+ root2 = tkinter.Tk()
+ root3 = tkinter.Tk()
+ self.assertIs(tkinter._default_root, root)
+ root2.destroy()
+ self.assertIs(tkinter._default_root, root)
+ root.destroy()
+ self.assertIsNone(tkinter._default_root)
+ root3.destroy()
+ self.assertIsNone(tkinter._default_root)
+
+ def test_no_default_root(self):
+ self.assertIs(tkinter._support_default_root, True)
+ self.assertIsNone(tkinter._default_root)
+ root = tkinter.Tk()
+ self.assertIs(tkinter._default_root, root)
+ tkinter.NoDefaultRoot()
+ self.assertIs(tkinter._support_default_root, False)
+ self.assertFalse(hasattr(tkinter, '_default_root'))
+ # repeated call is no-op
+ tkinter.NoDefaultRoot()
+ self.assertIs(tkinter._support_default_root, False)
+ self.assertFalse(hasattr(tkinter, '_default_root'))
+ root.destroy()
+ self.assertIs(tkinter._support_default_root, False)
+ self.assertFalse(hasattr(tkinter, '_default_root'))
+ root = tkinter.Tk()
+ self.assertIs(tkinter._support_default_root, False)
+ self.assertFalse(hasattr(tkinter, '_default_root'))
+ root.destroy()
+
+ def test_getboolean(self):
+ self.assertRaises(RuntimeError, tkinter.getboolean, '1')
+ root = tkinter.Tk()
+ self.assertIs(tkinter.getboolean('1'), True)
+ self.assertRaises(ValueError, tkinter.getboolean, 'yea')
+ root.destroy()
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, tkinter.getboolean, '1')
+
+ def test_mainloop(self):
+ self.assertRaises(RuntimeError, tkinter.mainloop)
+ root = tkinter.Tk()
+ root.after_idle(root.quit)
+ tkinter.mainloop()
+ root.destroy()
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, tkinter.mainloop)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_tkinter/test_simpledialog.py b/Lib/test/test_tkinter/test_simpledialog.py
new file mode 100644
index 0000000..502f7f7
--- /dev/null
+++ b/Lib/test/test_tkinter/test_simpledialog.py
@@ -0,0 +1,35 @@
+import unittest
+import tkinter
+from test.support import requires, swap_attr
+from test.test_tkinter.support import AbstractDefaultRootTest
+from tkinter.simpledialog import Dialog, askinteger
+
+requires('gui')
+
+
+class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
+
+ def test_askinteger(self):
+ @staticmethod
+ def mock_wait_window(w):
+ nonlocal ismapped
+ ismapped = w.master.winfo_ismapped()
+ w.destroy()
+
+ with swap_attr(Dialog, 'wait_window', mock_wait_window):
+ ismapped = None
+ askinteger("Go To Line", "Line number")
+ self.assertEqual(ismapped, False)
+
+ root = tkinter.Tk()
+ ismapped = None
+ askinteger("Go To Line", "Line number")
+ self.assertEqual(ismapped, True)
+ root.destroy()
+
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_tkinter/test_text.py b/Lib/test/test_tkinter/test_text.py
new file mode 100644
index 0000000..d1583f0
--- /dev/null
+++ b/Lib/test/test_tkinter/test_text.py
@@ -0,0 +1,45 @@
+import unittest
+import tkinter
+from test.support import requires
+from test.test_tkinter.support import AbstractTkTest
+
+requires('gui')
+
+class TextTest(AbstractTkTest, unittest.TestCase):
+
+ def setUp(self):
+ super().setUp()
+ self.text = tkinter.Text(self.root)
+
+ def test_debug(self):
+ text = self.text
+ olddebug = text.debug()
+ try:
+ text.debug(0)
+ self.assertEqual(text.debug(), 0)
+ text.debug(1)
+ self.assertEqual(text.debug(), 1)
+ finally:
+ text.debug(olddebug)
+ self.assertEqual(text.debug(), olddebug)
+
+ def test_search(self):
+ text = self.text
+
+ # pattern and index are obligatory arguments.
+ self.assertRaises(tkinter.TclError, text.search, None, '1.0')
+ self.assertRaises(tkinter.TclError, text.search, 'a', None)
+ self.assertRaises(tkinter.TclError, text.search, None, None)
+
+ # Invalid text index.
+ self.assertRaises(tkinter.TclError, text.search, '', 0)
+
+ # Check if we are getting the indices as strings -- you are likely
+ # to get Tcl_Obj under Tk 8.5 if Tkinter doesn't convert it.
+ text.insert('1.0', 'hi-test')
+ self.assertEqual(text.search('-test', '1.0', 'end'), '1.2')
+ self.assertEqual(text.search('test', '1.0', 'end'), '1.3')
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_tkinter/test_variables.py b/Lib/test/test_tkinter/test_variables.py
new file mode 100644
index 0000000..c1d232e
--- /dev/null
+++ b/Lib/test/test_tkinter/test_variables.py
@@ -0,0 +1,342 @@
+import unittest
+from test import support
+
+import gc
+import tkinter
+from tkinter import (Variable, StringVar, IntVar, DoubleVar, BooleanVar, Tcl,
+ TclError)
+from test.support import ALWAYS_EQ
+from test.test_tkinter.support import AbstractDefaultRootTest
+
+
+class Var(Variable):
+
+ _default = "default"
+ side_effect = False
+
+ def set(self, value):
+ self.side_effect = True
+ super().set(value)
+
+
+class TestBase(unittest.TestCase):
+
+ def setUp(self):
+ self.root = Tcl()
+
+ def tearDown(self):
+ del self.root
+
+
+class TestVariable(TestBase):
+
+ def info_exists(self, *args):
+ return self.root.getboolean(self.root.call("info", "exists", *args))
+
+ def test_default(self):
+ v = Variable(self.root)
+ self.assertEqual("", v.get())
+ self.assertRegex(str(v), r"^PY_VAR(\d+)$")
+
+ def test_name_and_value(self):
+ v = Variable(self.root, "sample string", "varname")
+ self.assertEqual("sample string", v.get())
+ self.assertEqual("varname", str(v))
+
+ def test___del__(self):
+ self.assertFalse(self.info_exists("varname"))
+ v = Variable(self.root, "sample string", "varname")
+ self.assertTrue(self.info_exists("varname"))
+ del v
+ support.gc_collect() # For PyPy or other GCs.
+ self.assertFalse(self.info_exists("varname"))
+
+ def test_dont_unset_not_existing(self):
+ self.assertFalse(self.info_exists("varname"))
+ v1 = Variable(self.root, name="name")
+ v2 = Variable(self.root, name="name")
+ del v1
+ support.gc_collect() # For PyPy or other GCs.
+ self.assertFalse(self.info_exists("name"))
+ # shouldn't raise exception
+ del v2
+ support.gc_collect() # For PyPy or other GCs.
+ self.assertFalse(self.info_exists("name"))
+
+ def test_equality(self):
+ # values doesn't matter, only class and name are checked
+ v1 = Variable(self.root, name="abc")
+ v2 = Variable(self.root, name="abc")
+ self.assertIsNot(v1, v2)
+ self.assertEqual(v1, v2)
+
+ v3 = Variable(self.root, name="cba")
+ self.assertNotEqual(v1, v3)
+
+ v4 = StringVar(self.root, name="abc")
+ self.assertEqual(str(v1), str(v4))
+ self.assertNotEqual(v1, v4)
+
+ V = type('Variable', (), {})
+ self.assertNotEqual(v1, V())
+
+ self.assertNotEqual(v1, object())
+ self.assertEqual(v1, ALWAYS_EQ)
+
+ root2 = tkinter.Tk()
+ self.addCleanup(root2.destroy)
+ v5 = Variable(root2, name="abc")
+ self.assertEqual(str(v1), str(v5))
+ self.assertNotEqual(v1, v5)
+
+ def test_invalid_name(self):
+ with self.assertRaises(TypeError):
+ Variable(self.root, name=123)
+
+ def test_null_in_name(self):
+ with self.assertRaises(ValueError):
+ Variable(self.root, name='var\x00name')
+ with self.assertRaises(ValueError):
+ self.root.globalsetvar('var\x00name', "value")
+ with self.assertRaises(ValueError):
+ self.root.globalsetvar(b'var\x00name', "value")
+ with self.assertRaises(ValueError):
+ self.root.setvar('var\x00name', "value")
+ with self.assertRaises(ValueError):
+ self.root.setvar(b'var\x00name', "value")
+
+ def test_initialize(self):
+ v = Var(self.root)
+ self.assertFalse(v.side_effect)
+ v.set("value")
+ self.assertTrue(v.side_effect)
+
+ def test_trace_old(self):
+ # Old interface
+ v = Variable(self.root)
+ vname = str(v)
+ trace = []
+ def read_tracer(*args):
+ trace.append(('read',) + args)
+ def write_tracer(*args):
+ trace.append(('write',) + args)
+ cb1 = v.trace_variable('r', read_tracer)
+ cb2 = v.trace_variable('wu', write_tracer)
+ self.assertEqual(sorted(v.trace_vinfo()), [('r', cb1), ('wu', cb2)])
+ self.assertEqual(trace, [])
+
+ v.set('spam')
+ self.assertEqual(trace, [('write', vname, '', 'w')])
+
+ trace = []
+ v.get()
+ self.assertEqual(trace, [('read', vname, '', 'r')])
+
+ trace = []
+ info = sorted(v.trace_vinfo())
+ v.trace_vdelete('w', cb1) # Wrong mode
+ self.assertEqual(sorted(v.trace_vinfo()), info)
+ with self.assertRaises(TclError):
+ v.trace_vdelete('r', 'spam') # Wrong command name
+ self.assertEqual(sorted(v.trace_vinfo()), info)
+ v.trace_vdelete('r', (cb1, 43)) # Wrong arguments
+ self.assertEqual(sorted(v.trace_vinfo()), info)
+ v.get()
+ self.assertEqual(trace, [('read', vname, '', 'r')])
+
+ trace = []
+ v.trace_vdelete('r', cb1)
+ self.assertEqual(v.trace_vinfo(), [('wu', cb2)])
+ v.get()
+ self.assertEqual(trace, [])
+
+ trace = []
+ del write_tracer
+ gc.collect()
+ v.set('eggs')
+ self.assertEqual(trace, [('write', vname, '', 'w')])
+
+ trace = []
+ del v
+ gc.collect()
+ self.assertEqual(trace, [('write', vname, '', 'u')])
+
+ def test_trace(self):
+ v = Variable(self.root)
+ vname = str(v)
+ trace = []
+ def read_tracer(*args):
+ trace.append(('read',) + args)
+ def write_tracer(*args):
+ trace.append(('write',) + args)
+ tr1 = v.trace_add('read', read_tracer)
+ tr2 = v.trace_add(['write', 'unset'], write_tracer)
+ self.assertEqual(sorted(v.trace_info()), [
+ (('read',), tr1),
+ (('write', 'unset'), tr2)])
+ self.assertEqual(trace, [])
+
+ v.set('spam')
+ self.assertEqual(trace, [('write', vname, '', 'write')])
+
+ trace = []
+ v.get()
+ self.assertEqual(trace, [('read', vname, '', 'read')])
+
+ trace = []
+ info = sorted(v.trace_info())
+ v.trace_remove('write', tr1) # Wrong mode
+ self.assertEqual(sorted(v.trace_info()), info)
+ with self.assertRaises(TclError):
+ v.trace_remove('read', 'spam') # Wrong command name
+ self.assertEqual(sorted(v.trace_info()), info)
+ v.get()
+ self.assertEqual(trace, [('read', vname, '', 'read')])
+
+ trace = []
+ v.trace_remove('read', tr1)
+ self.assertEqual(v.trace_info(), [(('write', 'unset'), tr2)])
+ v.get()
+ self.assertEqual(trace, [])
+
+ trace = []
+ del write_tracer
+ gc.collect()
+ v.set('eggs')
+ self.assertEqual(trace, [('write', vname, '', 'write')])
+
+ trace = []
+ del v
+ gc.collect()
+ self.assertEqual(trace, [('write', vname, '', 'unset')])
+
+
+class TestStringVar(TestBase):
+
+ def test_default(self):
+ v = StringVar(self.root)
+ self.assertEqual("", v.get())
+
+ def test_get(self):
+ v = StringVar(self.root, "abc", "name")
+ self.assertEqual("abc", v.get())
+ self.root.globalsetvar("name", "value")
+ self.assertEqual("value", v.get())
+
+ def test_get_null(self):
+ v = StringVar(self.root, "abc\x00def", "name")
+ self.assertEqual("abc\x00def", v.get())
+ self.root.globalsetvar("name", "val\x00ue")
+ self.assertEqual("val\x00ue", v.get())
+
+
+class TestIntVar(TestBase):
+
+ def test_default(self):
+ v = IntVar(self.root)
+ self.assertEqual(0, v.get())
+
+ def test_get(self):
+ v = IntVar(self.root, 123, "name")
+ self.assertEqual(123, v.get())
+ self.root.globalsetvar("name", "345")
+ self.assertEqual(345, v.get())
+ self.root.globalsetvar("name", "876.5")
+ self.assertEqual(876, v.get())
+
+ def test_invalid_value(self):
+ v = IntVar(self.root, name="name")
+ self.root.globalsetvar("name", "value")
+ with self.assertRaises((ValueError, TclError)):
+ v.get()
+
+
+class TestDoubleVar(TestBase):
+
+ def test_default(self):
+ v = DoubleVar(self.root)
+ self.assertEqual(0.0, v.get())
+
+ def test_get(self):
+ v = DoubleVar(self.root, 1.23, "name")
+ self.assertAlmostEqual(1.23, v.get())
+ self.root.globalsetvar("name", "3.45")
+ self.assertAlmostEqual(3.45, v.get())
+
+ def test_get_from_int(self):
+ v = DoubleVar(self.root, 1.23, "name")
+ self.assertAlmostEqual(1.23, v.get())
+ self.root.globalsetvar("name", "3.45")
+ self.assertAlmostEqual(3.45, v.get())
+ self.root.globalsetvar("name", "456")
+ self.assertAlmostEqual(456, v.get())
+
+ def test_invalid_value(self):
+ v = DoubleVar(self.root, name="name")
+ self.root.globalsetvar("name", "value")
+ with self.assertRaises((ValueError, TclError)):
+ v.get()
+
+
+class TestBooleanVar(TestBase):
+
+ def test_default(self):
+ v = BooleanVar(self.root)
+ self.assertIs(v.get(), False)
+
+ def test_get(self):
+ v = BooleanVar(self.root, True, "name")
+ self.assertIs(v.get(), True)
+ self.root.globalsetvar("name", "0")
+ self.assertIs(v.get(), False)
+ self.root.globalsetvar("name", 42 if self.root.wantobjects() else 1)
+ self.assertIs(v.get(), True)
+ self.root.globalsetvar("name", 0)
+ self.assertIs(v.get(), False)
+ self.root.globalsetvar("name", "on")
+ self.assertIs(v.get(), True)
+
+ def test_set(self):
+ true = 1 if self.root.wantobjects() else "1"
+ false = 0 if self.root.wantobjects() else "0"
+ v = BooleanVar(self.root, name="name")
+ v.set(True)
+ self.assertEqual(self.root.globalgetvar("name"), true)
+ v.set("0")
+ self.assertEqual(self.root.globalgetvar("name"), false)
+ v.set(42)
+ self.assertEqual(self.root.globalgetvar("name"), true)
+ v.set(0)
+ self.assertEqual(self.root.globalgetvar("name"), false)
+ v.set("on")
+ self.assertEqual(self.root.globalgetvar("name"), true)
+
+ def test_invalid_value_domain(self):
+ false = 0 if self.root.wantobjects() else "0"
+ v = BooleanVar(self.root, name="name")
+ with self.assertRaises(TclError):
+ v.set("value")
+ self.assertEqual(self.root.globalgetvar("name"), false)
+ self.root.globalsetvar("name", "value")
+ with self.assertRaises(ValueError):
+ v.get()
+ self.root.globalsetvar("name", "1.0")
+ with self.assertRaises(ValueError):
+ v.get()
+
+
+class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
+
+ def test_variable(self):
+ self.assertRaises(RuntimeError, Variable)
+ root = tkinter.Tk()
+ v = Variable()
+ v.set("value")
+ self.assertEqual(v.get(), "value")
+ root.destroy()
+ tkinter.NoDefaultRoot()
+ self.assertRaises(RuntimeError, Variable)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_tkinter/test_widgets.py b/Lib/test/test_tkinter/test_widgets.py
new file mode 100644
index 0000000..1cddbe1
--- /dev/null
+++ b/Lib/test/test_tkinter/test_widgets.py
@@ -0,0 +1,1292 @@
+import unittest
+import tkinter
+from tkinter import TclError
+import os
+from test.support import requires
+
+from test.test_tkinter.support import (requires_tcl,
+ get_tk_patchlevel, widget_eq,
+ AbstractDefaultRootTest)
+from test.test_tkinter.widget_tests import (
+ add_standard_options,
+ AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests,
+ setUpModule)
+
+requires('gui')
+
+
+def float_round(x):
+ return float(round(x))
+
+
+class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
+ _conv_pad_pixels = False
+
+ def test_configure_class(self):
+ widget = self.create()
+ self.assertEqual(widget['class'],
+ widget.__class__.__name__.title())
+ self.checkInvalidParam(widget, 'class', 'Foo',
+ errmsg="can't modify -class option after widget is created")
+ widget2 = self.create(class_='Foo')
+ self.assertEqual(widget2['class'], 'Foo')
+
+ def test_configure_colormap(self):
+ widget = self.create()
+ self.assertEqual(widget['colormap'], '')
+ self.checkInvalidParam(widget, 'colormap', 'new',
+ errmsg="can't modify -colormap option after widget is created")
+ widget2 = self.create(colormap='new')
+ self.assertEqual(widget2['colormap'], 'new')
+
+ def test_configure_container(self):
+ widget = self.create()
+ self.assertEqual(widget['container'], 0 if self.wantobjects else '0')
+ self.checkInvalidParam(widget, 'container', 1,
+ errmsg="can't modify -container option after widget is created")
+ widget2 = self.create(container=True)
+ self.assertEqual(widget2['container'], 1 if self.wantobjects else '1')
+
+ def test_configure_visual(self):
+ widget = self.create()
+ self.assertEqual(widget['visual'], '')
+ self.checkInvalidParam(widget, 'visual', 'default',
+ errmsg="can't modify -visual option after widget is created")
+ widget2 = self.create(visual='default')
+ self.assertEqual(widget2['visual'], 'default')
+
+
+@add_standard_options(StandardOptionsTests)
+class ToplevelTest(AbstractToplevelTest, unittest.TestCase):
+ OPTIONS = (
+ 'background', 'borderwidth',
+ 'class', 'colormap', 'container', 'cursor', 'height',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'menu', 'padx', 'pady', 'relief', 'screen',
+ 'takefocus', 'use', 'visual', 'width',
+ )
+
+ def create(self, **kwargs):
+ return tkinter.Toplevel(self.root, **kwargs)
+
+ def test_configure_menu(self):
+ widget = self.create()
+ menu = tkinter.Menu(self.root)
+ self.checkParam(widget, 'menu', menu, eq=widget_eq)
+ self.checkParam(widget, 'menu', '')
+
+ def test_configure_screen(self):
+ widget = self.create()
+ self.assertEqual(widget['screen'], '')
+ try:
+ display = os.environ['DISPLAY']
+ except KeyError:
+ self.skipTest('No $DISPLAY set.')
+ self.checkInvalidParam(widget, 'screen', display,
+ errmsg="can't modify -screen option after widget is created")
+ widget2 = self.create(screen=display)
+ self.assertEqual(widget2['screen'], display)
+
+ def test_configure_use(self):
+ widget = self.create()
+ self.assertEqual(widget['use'], '')
+ parent = self.create(container=True)
+ wid = hex(parent.winfo_id())
+ with self.subTest(wid=wid):
+ widget2 = self.create(use=wid)
+ self.assertEqual(widget2['use'], wid)
+
+
+@add_standard_options(StandardOptionsTests)
+class FrameTest(AbstractToplevelTest, unittest.TestCase):
+ OPTIONS = (
+ 'background', 'borderwidth',
+ 'class', 'colormap', 'container', 'cursor', 'height',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'padx', 'pady', 'relief', 'takefocus', 'visual', 'width',
+ )
+
+ def create(self, **kwargs):
+ return tkinter.Frame(self.root, **kwargs)
+
+
+@add_standard_options(StandardOptionsTests)
+class LabelFrameTest(AbstractToplevelTest, unittest.TestCase):
+ OPTIONS = (
+ 'background', 'borderwidth',
+ 'class', 'colormap', 'container', 'cursor',
+ 'font', 'foreground', 'height',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'labelanchor', 'labelwidget', 'padx', 'pady', 'relief',
+ 'takefocus', 'text', 'visual', 'width',
+ )
+
+ def create(self, **kwargs):
+ return tkinter.LabelFrame(self.root, **kwargs)
+
+ def test_configure_labelanchor(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'labelanchor',
+ 'e', 'en', 'es', 'n', 'ne', 'nw',
+ 's', 'se', 'sw', 'w', 'wn', 'ws')
+ self.checkInvalidParam(widget, 'labelanchor', 'center')
+
+ def test_configure_labelwidget(self):
+ widget = self.create()
+ label = tkinter.Label(self.root, text='Mupp', name='foo')
+ self.checkParam(widget, 'labelwidget', label, expected='.foo')
+ label.destroy()
+
+
+class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests):
+ _conv_pixels = False
+
+ def test_configure_highlightthickness(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'highlightthickness',
+ 0, 1.3, 2.6, 6, -2, '10p')
+
+
+@add_standard_options(StandardOptionsTests)
+class LabelTest(AbstractLabelTest, unittest.TestCase):
+ OPTIONS = (
+ 'activebackground', 'activeforeground', 'anchor',
+ 'background', 'bitmap', 'borderwidth', 'compound', 'cursor',
+ 'disabledforeground', 'font', 'foreground', 'height',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'image', 'justify', 'padx', 'pady', 'relief', 'state',
+ 'takefocus', 'text', 'textvariable',
+ 'underline', 'width', 'wraplength',
+ )
+
+ def create(self, **kwargs):
+ return tkinter.Label(self.root, **kwargs)
+
+
+@add_standard_options(StandardOptionsTests)
+class ButtonTest(AbstractLabelTest, unittest.TestCase):
+ OPTIONS = (
+ 'activebackground', 'activeforeground', 'anchor',
+ 'background', 'bitmap', 'borderwidth',
+ 'command', 'compound', 'cursor', 'default',
+ 'disabledforeground', 'font', 'foreground', 'height',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'image', 'justify', 'overrelief', 'padx', 'pady', 'relief',
+ 'repeatdelay', 'repeatinterval',
+ 'state', 'takefocus', 'text', 'textvariable',
+ 'underline', 'width', 'wraplength')
+
+ def create(self, **kwargs):
+ return tkinter.Button(self.root, **kwargs)
+
+ def test_configure_default(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'default', 'active', 'disabled', 'normal')
+
+
+@add_standard_options(StandardOptionsTests)
+class CheckbuttonTest(AbstractLabelTest, unittest.TestCase):
+ OPTIONS = (
+ 'activebackground', 'activeforeground', 'anchor',
+ 'background', 'bitmap', 'borderwidth',
+ 'command', 'compound', 'cursor',
+ 'disabledforeground', 'font', 'foreground', 'height',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'image', 'indicatoron', 'justify',
+ 'offrelief', 'offvalue', 'onvalue', 'overrelief',
+ 'padx', 'pady', 'relief', 'selectcolor', 'selectimage', 'state',
+ 'takefocus', 'text', 'textvariable',
+ 'tristateimage', 'tristatevalue',
+ 'underline', 'variable', 'width', 'wraplength',
+ )
+
+ def create(self, **kwargs):
+ return tkinter.Checkbutton(self.root, **kwargs)
+
+
+ def test_configure_offvalue(self):
+ widget = self.create()
+ self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string')
+
+ def test_configure_onvalue(self):
+ widget = self.create()
+ self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string')
+
+
+@add_standard_options(StandardOptionsTests)
+class RadiobuttonTest(AbstractLabelTest, unittest.TestCase):
+ OPTIONS = (
+ 'activebackground', 'activeforeground', 'anchor',
+ 'background', 'bitmap', 'borderwidth',
+ 'command', 'compound', 'cursor',
+ 'disabledforeground', 'font', 'foreground', 'height',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'image', 'indicatoron', 'justify', 'offrelief', 'overrelief',
+ 'padx', 'pady', 'relief', 'selectcolor', 'selectimage', 'state',
+ 'takefocus', 'text', 'textvariable',
+ 'tristateimage', 'tristatevalue',
+ 'underline', 'value', 'variable', 'width', 'wraplength',
+ )
+
+ def create(self, **kwargs):
+ return tkinter.Radiobutton(self.root, **kwargs)
+
+ def test_configure_value(self):
+ widget = self.create()
+ self.checkParams(widget, 'value', 1, 2.3, '', 'any string')
+
+
+@add_standard_options(StandardOptionsTests)
+class MenubuttonTest(AbstractLabelTest, unittest.TestCase):
+ OPTIONS = (
+ 'activebackground', 'activeforeground', 'anchor',
+ 'background', 'bitmap', 'borderwidth',
+ 'compound', 'cursor', 'direction',
+ 'disabledforeground', 'font', 'foreground', 'height',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'image', 'indicatoron', 'justify', 'menu',
+ 'padx', 'pady', 'relief', 'state',
+ 'takefocus', 'text', 'textvariable',
+ 'underline', 'width', 'wraplength',
+ )
+ _conv_pixels = round
+
+ def create(self, **kwargs):
+ return tkinter.Menubutton(self.root, **kwargs)
+
+ def test_configure_direction(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'direction',
+ 'above', 'below', 'flush', 'left', 'right')
+
+ def test_configure_height(self):
+ widget = self.create()
+ self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=str)
+
+ test_configure_highlightthickness = \
+ StandardOptionsTests.test_configure_highlightthickness
+
+ def test_configure_image(self):
+ widget = self.create()
+ image = tkinter.PhotoImage(master=self.root, name='image1')
+ self.checkParam(widget, 'image', image, conv=str)
+ errmsg = 'image "spam" doesn\'t exist'
+ with self.assertRaises(tkinter.TclError) as cm:
+ widget['image'] = 'spam'
+ if errmsg is not None:
+ self.assertEqual(str(cm.exception), errmsg)
+ with self.assertRaises(tkinter.TclError) as cm:
+ widget.configure({'image': 'spam'})
+ if errmsg is not None:
+ self.assertEqual(str(cm.exception), errmsg)
+
+ def test_configure_menu(self):
+ widget = self.create()
+ menu = tkinter.Menu(widget, name='menu')
+ self.checkParam(widget, 'menu', menu, eq=widget_eq)
+ menu.destroy()
+
+ def test_configure_padx(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m')
+ self.checkParam(widget, 'padx', -2, expected=0)
+
+ def test_configure_pady(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m')
+ self.checkParam(widget, 'pady', -2, expected=0)
+
+ def test_configure_width(self):
+ widget = self.create()
+ self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=str)
+
+
+class OptionMenuTest(MenubuttonTest, unittest.TestCase):
+
+ def create(self, default='b', values=('a', 'b', 'c'), **kwargs):
+ return tkinter.OptionMenu(self.root, None, default, *values, **kwargs)
+
+ def test_bad_kwarg(self):
+ with self.assertRaisesRegex(TclError, r"^unknown option -image$"):
+ tkinter.OptionMenu(self.root, None, 'b', image='')
+
+
+@add_standard_options(IntegerSizeTests, StandardOptionsTests)
+class EntryTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'background', 'borderwidth', 'cursor',
+ 'disabledbackground', 'disabledforeground',
+ 'exportselection', 'font', 'foreground',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'insertbackground', 'insertborderwidth',
+ 'insertofftime', 'insertontime', 'insertwidth',
+ 'invalidcommand', 'justify', 'readonlybackground', 'relief',
+ 'selectbackground', 'selectborderwidth', 'selectforeground',
+ 'show', 'state', 'takefocus', 'textvariable',
+ 'validate', 'validatecommand', 'width', 'xscrollcommand',
+ )
+
+ def create(self, **kwargs):
+ return tkinter.Entry(self.root, **kwargs)
+
+ def test_configure_disabledbackground(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'disabledbackground')
+
+ def test_configure_insertborderwidth(self):
+ widget = self.create(insertwidth=100)
+ self.checkPixelsParam(widget, 'insertborderwidth',
+ 0, 1.3, 2.6, 6, -2, '10p')
+ # insertborderwidth is bounded above by a half of insertwidth.
+ self.checkParam(widget, 'insertborderwidth', 60, expected=100//2)
+
+ def test_configure_insertwidth(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, '10p')
+ self.checkParam(widget, 'insertwidth', 0.1, expected=2)
+ self.checkParam(widget, 'insertwidth', -2, expected=2)
+ self.checkParam(widget, 'insertwidth', 0.9, expected=1)
+
+ def test_configure_invalidcommand(self):
+ widget = self.create()
+ self.checkCommandParam(widget, 'invalidcommand')
+ self.checkCommandParam(widget, 'invcmd')
+
+ def test_configure_readonlybackground(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'readonlybackground')
+
+ def test_configure_show(self):
+ widget = self.create()
+ self.checkParam(widget, 'show', '*')
+ self.checkParam(widget, 'show', '')
+ self.checkParam(widget, 'show', ' ')
+
+ def test_configure_state(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'state',
+ 'disabled', 'normal', 'readonly')
+
+ def test_configure_validate(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'validate',
+ 'all', 'key', 'focus', 'focusin', 'focusout', 'none')
+
+ def test_configure_validatecommand(self):
+ widget = self.create()
+ self.checkCommandParam(widget, 'validatecommand')
+ self.checkCommandParam(widget, 'vcmd')
+
+ def test_selection_methods(self):
+ widget = self.create()
+ widget.insert(0, '12345')
+ self.assertFalse(widget.selection_present())
+ widget.selection_range(0, 'end')
+ self.assertEqual(widget.selection_get(), '12345')
+ self.assertTrue(widget.selection_present())
+ widget.selection_from(1)
+ widget.selection_to(2)
+ self.assertEqual(widget.selection_get(), '2')
+ widget.selection_range(3, 4)
+ self.assertEqual(widget.selection_get(), '4')
+ widget.selection_clear()
+ self.assertFalse(widget.selection_present())
+ widget.selection_range(0, 'end')
+ widget.selection_adjust(4)
+ self.assertEqual(widget.selection_get(), '1234')
+ widget.selection_adjust(1)
+ self.assertEqual(widget.selection_get(), '234')
+ widget.selection_adjust(5)
+ self.assertEqual(widget.selection_get(), '2345')
+ widget.selection_adjust(0)
+ self.assertEqual(widget.selection_get(), '12345')
+ widget.selection_adjust(0)
+
+
+@add_standard_options(StandardOptionsTests)
+class SpinboxTest(EntryTest, unittest.TestCase):
+ OPTIONS = (
+ 'activebackground', 'background', 'borderwidth',
+ 'buttonbackground', 'buttoncursor', 'buttondownrelief', 'buttonuprelief',
+ 'command', 'cursor', 'disabledbackground', 'disabledforeground',
+ 'exportselection', 'font', 'foreground', 'format', 'from',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'increment',
+ 'insertbackground', 'insertborderwidth',
+ 'insertofftime', 'insertontime', 'insertwidth',
+ 'invalidcommand', 'justify', 'relief', 'readonlybackground',
+ 'repeatdelay', 'repeatinterval',
+ 'selectbackground', 'selectborderwidth', 'selectforeground',
+ 'state', 'takefocus', 'textvariable', 'to',
+ 'validate', 'validatecommand', 'values',
+ 'width', 'wrap', 'xscrollcommand',
+ )
+
+ def create(self, **kwargs):
+ return tkinter.Spinbox(self.root, **kwargs)
+
+ test_configure_show = None
+
+ def test_configure_buttonbackground(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'buttonbackground')
+
+ def test_configure_buttoncursor(self):
+ widget = self.create()
+ self.checkCursorParam(widget, 'buttoncursor')
+
+ def test_configure_buttondownrelief(self):
+ widget = self.create()
+ self.checkReliefParam(widget, 'buttondownrelief')
+
+ def test_configure_buttonuprelief(self):
+ widget = self.create()
+ self.checkReliefParam(widget, 'buttonuprelief')
+
+ def test_configure_format(self):
+ widget = self.create()
+ self.checkParam(widget, 'format', '%2f')
+ self.checkParam(widget, 'format', '%2.2f')
+ self.checkParam(widget, 'format', '%.2f')
+ self.checkParam(widget, 'format', '%2.f')
+ self.checkInvalidParam(widget, 'format', '%2e-1f')
+ self.checkInvalidParam(widget, 'format', '2.2')
+ self.checkInvalidParam(widget, 'format', '%2.-2f')
+ self.checkParam(widget, 'format', '%-2.02f')
+ self.checkParam(widget, 'format', '% 2.02f')
+ self.checkParam(widget, 'format', '% -2.200f')
+ self.checkParam(widget, 'format', '%09.200f')
+ self.checkInvalidParam(widget, 'format', '%d')
+
+ def test_configure_from(self):
+ widget = self.create()
+ self.checkParam(widget, 'to', 100.0)
+ self.checkFloatParam(widget, 'from', -10, 10.2, 11.7)
+ self.checkInvalidParam(widget, 'from', 200,
+ errmsg='-to value must be greater than -from value')
+
+ def test_configure_increment(self):
+ widget = self.create()
+ self.checkFloatParam(widget, 'increment', -1, 1, 10.2, 12.8, 0)
+
+ def test_configure_to(self):
+ widget = self.create()
+ self.checkParam(widget, 'from', -100.0)
+ self.checkFloatParam(widget, 'to', -10, 10.2, 11.7)
+ self.checkInvalidParam(widget, 'to', -200,
+ errmsg='-to value must be greater than -from value')
+
+ def test_configure_values(self):
+ # XXX
+ widget = self.create()
+ self.assertEqual(widget['values'], '')
+ self.checkParam(widget, 'values', 'mon tue wed thur')
+ self.checkParam(widget, 'values', ('mon', 'tue', 'wed', 'thur'),
+ expected='mon tue wed thur')
+ self.checkParam(widget, 'values', (42, 3.14, '', 'any string'),
+ expected='42 3.14 {} {any string}')
+ self.checkParam(widget, 'values', '')
+
+ def test_configure_wrap(self):
+ widget = self.create()
+ self.checkBooleanParam(widget, 'wrap')
+
+ def test_bbox(self):
+ widget = self.create()
+ self.assertIsBoundingBox(widget.bbox(0))
+ self.assertRaises(tkinter.TclError, widget.bbox, 'noindex')
+ self.assertRaises(tkinter.TclError, widget.bbox, None)
+ self.assertRaises(TypeError, widget.bbox)
+ self.assertRaises(TypeError, widget.bbox, 0, 1)
+
+ def test_selection_methods(self):
+ widget = self.create()
+ widget.insert(0, '12345')
+ self.assertFalse(widget.selection_present())
+ widget.selection_range(0, 'end')
+ self.assertEqual(widget.selection_get(), '12345')
+ self.assertTrue(widget.selection_present())
+ widget.selection_from(1)
+ widget.selection_to(2)
+ self.assertEqual(widget.selection_get(), '2')
+ widget.selection_range(3, 4)
+ self.assertEqual(widget.selection_get(), '4')
+ widget.selection_clear()
+ self.assertFalse(widget.selection_present())
+ widget.selection_range(0, 'end')
+ widget.selection_adjust(4)
+ self.assertEqual(widget.selection_get(), '1234')
+ widget.selection_adjust(1)
+ self.assertEqual(widget.selection_get(), '234')
+ widget.selection_adjust(5)
+ self.assertEqual(widget.selection_get(), '2345')
+ widget.selection_adjust(0)
+ self.assertEqual(widget.selection_get(), '12345')
+
+ def test_selection_element(self):
+ widget = self.create()
+ self.assertEqual(widget.selection_element(), "none")
+ widget.selection_element("buttonup")
+ self.assertEqual(widget.selection_element(), "buttonup")
+ widget.selection_element("buttondown")
+ self.assertEqual(widget.selection_element(), "buttondown")
+
+
+@add_standard_options(StandardOptionsTests)
+class TextTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'autoseparators', 'background', 'blockcursor', 'borderwidth',
+ 'cursor', 'endline', 'exportselection',
+ 'font', 'foreground', 'height',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'inactiveselectbackground', 'insertbackground', 'insertborderwidth',
+ 'insertofftime', 'insertontime', 'insertunfocussed', 'insertwidth',
+ 'maxundo', 'padx', 'pady', 'relief',
+ 'selectbackground', 'selectborderwidth', 'selectforeground',
+ 'setgrid', 'spacing1', 'spacing2', 'spacing3', 'startline', 'state',
+ 'tabs', 'tabstyle', 'takefocus', 'undo', 'width', 'wrap',
+ 'xscrollcommand', 'yscrollcommand',
+ )
+
+ def create(self, **kwargs):
+ return tkinter.Text(self.root, **kwargs)
+
+ def test_configure_autoseparators(self):
+ widget = self.create()
+ self.checkBooleanParam(widget, 'autoseparators')
+
+ def test_configure_blockcursor(self):
+ widget = self.create()
+ self.checkBooleanParam(widget, 'blockcursor')
+
+ def test_configure_endline(self):
+ widget = self.create()
+ text = '\n'.join('Line %d' for i in range(100))
+ widget.insert('end', text)
+ self.checkParam(widget, 'endline', 200, expected='')
+ self.checkParam(widget, 'endline', -10, expected='')
+ self.checkInvalidParam(widget, 'endline', 'spam',
+ errmsg='expected integer but got "spam"')
+ self.checkParam(widget, 'endline', 50)
+ self.checkParam(widget, 'startline', 15)
+ self.checkInvalidParam(widget, 'endline', 10,
+ errmsg='-startline must be less than or equal to -endline')
+
+ def test_configure_height(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, '3c')
+ self.checkParam(widget, 'height', -100, expected=1)
+ self.checkParam(widget, 'height', 0, expected=1)
+
+ def test_configure_maxundo(self):
+ widget = self.create()
+ self.checkIntegerParam(widget, 'maxundo', 0, 5, -1)
+
+ def test_configure_inactiveselectbackground(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'inactiveselectbackground')
+
+ @requires_tcl(8, 6)
+ def test_configure_insertunfocussed(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'insertunfocussed',
+ 'hollow', 'none', 'solid')
+
+ def test_configure_selectborderwidth(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'selectborderwidth',
+ 1.3, 2.6, -2, '10p', conv=False)
+
+ def test_configure_spacing1(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'spacing1', 20, 21.4, 22.6, '0.5c')
+ self.checkParam(widget, 'spacing1', -5, expected=0)
+
+ def test_configure_spacing2(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'spacing2', 5, 6.4, 7.6, '0.1c')
+ self.checkParam(widget, 'spacing2', -1, expected=0)
+
+ def test_configure_spacing3(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, '0.5c')
+ self.checkParam(widget, 'spacing3', -10, expected=0)
+
+ def test_configure_startline(self):
+ widget = self.create()
+ text = '\n'.join('Line %d' for i in range(100))
+ widget.insert('end', text)
+ self.checkParam(widget, 'startline', 200, expected='')
+ self.checkParam(widget, 'startline', -10, expected='')
+ self.checkInvalidParam(widget, 'startline', 'spam',
+ errmsg='expected integer but got "spam"')
+ self.checkParam(widget, 'startline', 10)
+ self.checkParam(widget, 'endline', 50)
+ self.checkInvalidParam(widget, 'startline', 70,
+ errmsg='-startline must be less than or equal to -endline')
+
+ def test_configure_state(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'state', 'disabled', 'normal')
+
+ def test_configure_tabs(self):
+ widget = self.create()
+ self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i'))
+ self.checkParam(widget, 'tabs', '10.2 20.7 1i 2i',
+ expected=('10.2', '20.7', '1i', '2i'))
+ self.checkParam(widget, 'tabs', '2c left 4c 6c center',
+ expected=('2c', 'left', '4c', '6c', 'center'))
+ self.checkInvalidParam(widget, 'tabs', 'spam',
+ errmsg='bad screen distance "spam"')
+
+ def test_configure_tabstyle(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'tabstyle', 'tabular', 'wordprocessor')
+
+ def test_configure_undo(self):
+ widget = self.create()
+ self.checkBooleanParam(widget, 'undo')
+
+ def test_configure_width(self):
+ widget = self.create()
+ self.checkIntegerParam(widget, 'width', 402)
+ self.checkParam(widget, 'width', -402, expected=1)
+ self.checkParam(widget, 'width', 0, expected=1)
+
+ def test_configure_wrap(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'wrap', 'char', 'none', 'word')
+
+ def test_bbox(self):
+ widget = self.create()
+ self.assertIsBoundingBox(widget.bbox('1.1'))
+ self.assertIsNone(widget.bbox('end'))
+ self.assertRaises(tkinter.TclError, widget.bbox, 'noindex')
+ self.assertRaises(tkinter.TclError, widget.bbox, None)
+ self.assertRaises(TypeError, widget.bbox)
+ self.assertRaises(TypeError, widget.bbox, '1.1', 'end')
+
+
+@add_standard_options(PixelSizeTests, StandardOptionsTests)
+class CanvasTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'background', 'borderwidth',
+ 'closeenough', 'confine', 'cursor', 'height',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'insertbackground', 'insertborderwidth',
+ 'insertofftime', 'insertontime', 'insertwidth',
+ 'offset', 'relief', 'scrollregion',
+ 'selectbackground', 'selectborderwidth', 'selectforeground',
+ 'state', 'takefocus',
+ 'xscrollcommand', 'xscrollincrement',
+ 'yscrollcommand', 'yscrollincrement', 'width',
+ )
+
+ _conv_pixels = round
+ _stringify = True
+
+ def create(self, **kwargs):
+ return tkinter.Canvas(self.root, **kwargs)
+
+ def test_configure_closeenough(self):
+ widget = self.create()
+ self.checkFloatParam(widget, 'closeenough', 24, 2.4, 3.6, -3,
+ conv=float)
+
+ def test_configure_confine(self):
+ widget = self.create()
+ self.checkBooleanParam(widget, 'confine')
+
+ def test_configure_offset(self):
+ widget = self.create()
+ self.assertEqual(widget['offset'], '0,0')
+ self.checkParams(widget, 'offset',
+ 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center')
+ self.checkParam(widget, 'offset', '10,20')
+ self.checkParam(widget, 'offset', '#5,6')
+ self.checkInvalidParam(widget, 'offset', 'spam')
+
+ def test_configure_scrollregion(self):
+ widget = self.create()
+ self.checkParam(widget, 'scrollregion', '0 0 200 150')
+ self.checkParam(widget, 'scrollregion', (0, 0, 200, 150),
+ expected='0 0 200 150')
+ self.checkParam(widget, 'scrollregion', '')
+ self.checkInvalidParam(widget, 'scrollregion', 'spam',
+ errmsg='bad scrollRegion "spam"')
+ self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 'spam'))
+ self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200))
+ self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 150, 0))
+
+ def test_configure_state(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'state', 'disabled', 'normal',
+ errmsg='bad state value "{}": must be normal or disabled')
+
+ def test_configure_xscrollincrement(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'xscrollincrement',
+ 40, 0, 41.2, 43.6, -40, '0.5i')
+
+ def test_configure_yscrollincrement(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'yscrollincrement',
+ 10, 0, 11.2, 13.6, -10, '0.1i')
+
+ @requires_tcl(8, 6)
+ def test_moveto(self):
+ widget = self.create()
+ i1 = widget.create_rectangle(1, 1, 20, 20, tags='group')
+ i2 = widget.create_rectangle(30, 30, 50, 70, tags='group')
+ x1, y1, _, _ = widget.bbox(i1)
+ x2, y2, _, _ = widget.bbox(i2)
+ widget.moveto('group', 200, 100)
+ x1_2, y1_2, _, _ = widget.bbox(i1)
+ x2_2, y2_2, _, _ = widget.bbox(i2)
+ self.assertEqual(x1_2, 200)
+ self.assertEqual(y1_2, 100)
+ self.assertEqual(x2 - x1, x2_2 - x1_2)
+ self.assertEqual(y2 - y1, y2_2 - y1_2)
+ widget.tag_lower(i2, i1)
+ widget.moveto('group', y=50)
+ x1_3, y1_3, _, _ = widget.bbox(i1)
+ x2_3, y2_3, _, _ = widget.bbox(i2)
+ self.assertEqual(y2_3, 50)
+ self.assertEqual(x2_3, x2_2)
+ self.assertEqual(x2_2 - x1_2, x2_3 - x1_3)
+ self.assertEqual(y2_2 - y1_2, y2_3 - y1_3)
+
+
+@add_standard_options(IntegerSizeTests, StandardOptionsTests)
+class ListboxTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'activestyle', 'background', 'borderwidth', 'cursor',
+ 'disabledforeground', 'exportselection',
+ 'font', 'foreground', 'height',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'justify', 'listvariable', 'relief',
+ 'selectbackground', 'selectborderwidth', 'selectforeground',
+ 'selectmode', 'setgrid', 'state',
+ 'takefocus', 'width', 'xscrollcommand', 'yscrollcommand',
+ )
+
+ def create(self, **kwargs):
+ return tkinter.Listbox(self.root, **kwargs)
+
+ def test_configure_activestyle(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'activestyle',
+ 'dotbox', 'none', 'underline')
+
+ test_configure_justify = requires_tcl(8, 6, 5)(StandardOptionsTests.test_configure_justify)
+
+ def test_configure_listvariable(self):
+ widget = self.create()
+ var = tkinter.DoubleVar(self.root)
+ self.checkVariableParam(widget, 'listvariable', var)
+
+ def test_configure_selectmode(self):
+ widget = self.create()
+ self.checkParam(widget, 'selectmode', 'single')
+ self.checkParam(widget, 'selectmode', 'browse')
+ self.checkParam(widget, 'selectmode', 'multiple')
+ self.checkParam(widget, 'selectmode', 'extended')
+
+ def test_configure_state(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'state', 'disabled', 'normal')
+
+ def test_itemconfigure(self):
+ widget = self.create()
+ with self.assertRaisesRegex(TclError, 'item number "0" out of range'):
+ widget.itemconfigure(0)
+ colors = 'red orange yellow green blue white violet'.split()
+ widget.insert('end', *colors)
+ for i, color in enumerate(colors):
+ widget.itemconfigure(i, background=color)
+ with self.assertRaises(TypeError):
+ widget.itemconfigure()
+ with self.assertRaisesRegex(TclError, 'bad listbox index "red"'):
+ widget.itemconfigure('red')
+ self.assertEqual(widget.itemconfigure(0, 'background'),
+ ('background', 'background', 'Background', '', 'red'))
+ self.assertEqual(widget.itemconfigure('end', 'background'),
+ ('background', 'background', 'Background', '', 'violet'))
+ self.assertEqual(widget.itemconfigure('@0,0', 'background'),
+ ('background', 'background', 'Background', '', 'red'))
+
+ d = widget.itemconfigure(0)
+ self.assertIsInstance(d, dict)
+ for k, v in d.items():
+ self.assertIn(len(v), (2, 5))
+ if len(v) == 5:
+ self.assertEqual(v, widget.itemconfigure(0, k))
+ self.assertEqual(v[4], widget.itemcget(0, k))
+
+ def check_itemconfigure(self, name, value):
+ widget = self.create()
+ widget.insert('end', 'a', 'b', 'c', 'd')
+ widget.itemconfigure(0, **{name: value})
+ self.assertEqual(widget.itemconfigure(0, name)[4], value)
+ self.assertEqual(widget.itemcget(0, name), value)
+ with self.assertRaisesRegex(TclError, 'unknown color name "spam"'):
+ widget.itemconfigure(0, **{name: 'spam'})
+
+ def test_itemconfigure_background(self):
+ self.check_itemconfigure('background', '#ff0000')
+
+ def test_itemconfigure_bg(self):
+ self.check_itemconfigure('bg', '#ff0000')
+
+ def test_itemconfigure_fg(self):
+ self.check_itemconfigure('fg', '#110022')
+
+ def test_itemconfigure_foreground(self):
+ self.check_itemconfigure('foreground', '#110022')
+
+ def test_itemconfigure_selectbackground(self):
+ self.check_itemconfigure('selectbackground', '#110022')
+
+ def test_itemconfigure_selectforeground(self):
+ self.check_itemconfigure('selectforeground', '#654321')
+
+ def test_box(self):
+ lb = self.create()
+ lb.insert(0, *('el%d' % i for i in range(8)))
+ lb.pack()
+ self.assertIsBoundingBox(lb.bbox(0))
+ self.assertIsNone(lb.bbox(-1))
+ self.assertIsNone(lb.bbox(10))
+ self.assertRaises(TclError, lb.bbox, 'noindex')
+ self.assertRaises(TclError, lb.bbox, None)
+ self.assertRaises(TypeError, lb.bbox)
+ self.assertRaises(TypeError, lb.bbox, 0, 1)
+
+ def test_curselection(self):
+ lb = self.create()
+ lb.insert(0, *('el%d' % i for i in range(8)))
+ lb.selection_clear(0, tkinter.END)
+ lb.selection_set(2, 4)
+ lb.selection_set(6)
+ self.assertEqual(lb.curselection(), (2, 3, 4, 6))
+ self.assertRaises(TypeError, lb.curselection, 0)
+
+ def test_get(self):
+ lb = self.create()
+ lb.insert(0, *('el%d' % i for i in range(8)))
+ self.assertEqual(lb.get(0), 'el0')
+ self.assertEqual(lb.get(3), 'el3')
+ self.assertEqual(lb.get('end'), 'el7')
+ self.assertEqual(lb.get(8), '')
+ self.assertEqual(lb.get(-1), '')
+ self.assertEqual(lb.get(3, 5), ('el3', 'el4', 'el5'))
+ self.assertEqual(lb.get(5, 'end'), ('el5', 'el6', 'el7'))
+ self.assertEqual(lb.get(5, 0), ())
+ self.assertEqual(lb.get(0, 0), ('el0',))
+ self.assertRaises(TclError, lb.get, 'noindex')
+ self.assertRaises(TclError, lb.get, None)
+ self.assertRaises(TypeError, lb.get)
+ self.assertRaises(TclError, lb.get, 'end', 'noindex')
+ self.assertRaises(TypeError, lb.get, 1, 2, 3)
+ self.assertRaises(TclError, lb.get, 2.4)
+
+
+@add_standard_options(PixelSizeTests, StandardOptionsTests)
+class ScaleTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'activebackground', 'background', 'bigincrement', 'borderwidth',
+ 'command', 'cursor', 'digits', 'font', 'foreground', 'from',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'label', 'length', 'orient', 'relief',
+ 'repeatdelay', 'repeatinterval',
+ 'resolution', 'showvalue', 'sliderlength', 'sliderrelief', 'state',
+ 'takefocus', 'tickinterval', 'to', 'troughcolor', 'variable', 'width',
+ )
+ default_orient = 'vertical'
+
+ def create(self, **kwargs):
+ return tkinter.Scale(self.root, **kwargs)
+
+ def test_configure_bigincrement(self):
+ widget = self.create()
+ self.checkFloatParam(widget, 'bigincrement', 12.4, 23.6, -5)
+
+ def test_configure_digits(self):
+ widget = self.create()
+ self.checkIntegerParam(widget, 'digits', 5, 0)
+
+ def test_configure_from(self):
+ widget = self.create()
+ conv = float if get_tk_patchlevel() >= (8, 6, 10) else float_round
+ self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=conv)
+
+ def test_configure_label(self):
+ widget = self.create()
+ self.checkParam(widget, 'label', 'any string')
+ self.checkParam(widget, 'label', '')
+
+ def test_configure_length(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i')
+
+ def test_configure_resolution(self):
+ widget = self.create()
+ self.checkFloatParam(widget, 'resolution', 4.2, 0, 6.7, -2)
+
+ def test_configure_showvalue(self):
+ widget = self.create()
+ self.checkBooleanParam(widget, 'showvalue')
+
+ def test_configure_sliderlength(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'sliderlength',
+ 10, 11.2, 15.6, -3, '3m')
+
+ def test_configure_sliderrelief(self):
+ widget = self.create()
+ self.checkReliefParam(widget, 'sliderrelief')
+
+ def test_configure_tickinterval(self):
+ widget = self.create()
+ self.checkFloatParam(widget, 'tickinterval', 1, 4.3, 7.6, 0,
+ conv=float_round)
+ self.checkParam(widget, 'tickinterval', -2, expected=2,
+ conv=float_round)
+
+ def test_configure_to(self):
+ widget = self.create()
+ self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10,
+ conv=float_round)
+
+
+@add_standard_options(PixelSizeTests, StandardOptionsTests)
+class ScrollbarTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'activebackground', 'activerelief',
+ 'background', 'borderwidth',
+ 'command', 'cursor', 'elementborderwidth',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'jump', 'orient', 'relief',
+ 'repeatdelay', 'repeatinterval',
+ 'takefocus', 'troughcolor', 'width',
+ )
+ _conv_pixels = round
+ _stringify = True
+ default_orient = 'vertical'
+
+ def create(self, **kwargs):
+ return tkinter.Scrollbar(self.root, **kwargs)
+
+ def test_configure_activerelief(self):
+ widget = self.create()
+ self.checkReliefParam(widget, 'activerelief')
+
+ def test_configure_elementborderwidth(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'elementborderwidth', 4.3, 5.6, -2, '1m')
+
+ def test_configure_orient(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'orient', 'vertical', 'horizontal',
+ errmsg='bad orientation "{}": must be vertical or horizontal')
+
+ def test_activate(self):
+ sb = self.create()
+ for e in ('arrow1', 'slider', 'arrow2'):
+ sb.activate(e)
+ self.assertEqual(sb.activate(), e)
+ sb.activate('')
+ self.assertIsNone(sb.activate())
+ self.assertRaises(TypeError, sb.activate, 'arrow1', 'arrow2')
+
+ def test_set(self):
+ sb = self.create()
+ sb.set(0.2, 0.4)
+ self.assertEqual(sb.get(), (0.2, 0.4))
+ self.assertRaises(TclError, sb.set, 'abc', 'def')
+ self.assertRaises(TclError, sb.set, 0.6, 'def')
+ self.assertRaises(TclError, sb.set, 0.6, None)
+ self.assertRaises(TypeError, sb.set, 0.6)
+ self.assertRaises(TypeError, sb.set, 0.6, 0.7, 0.8)
+
+
+@add_standard_options(StandardOptionsTests)
+class PanedWindowTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'background', 'borderwidth', 'cursor',
+ 'handlepad', 'handlesize', 'height',
+ 'opaqueresize', 'orient',
+ 'proxybackground', 'proxyborderwidth', 'proxyrelief',
+ 'relief',
+ 'sashcursor', 'sashpad', 'sashrelief', 'sashwidth',
+ 'showhandle', 'width',
+ )
+ default_orient = 'horizontal'
+
+ def create(self, **kwargs):
+ return tkinter.PanedWindow(self.root, **kwargs)
+
+ def test_configure_handlepad(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'handlepad', 5, 6.4, 7.6, -3, '1m')
+
+ def test_configure_handlesize(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m',
+ conv=False)
+
+ def test_configure_height(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i',
+ conv=False)
+
+ def test_configure_opaqueresize(self):
+ widget = self.create()
+ self.checkBooleanParam(widget, 'opaqueresize')
+
+ @requires_tcl(8, 6, 5)
+ def test_configure_proxybackground(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'proxybackground')
+
+ @requires_tcl(8, 6, 5)
+ def test_configure_proxyborderwidth(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'proxyborderwidth',
+ 0, 1.3, 2.9, 6, -2, '10p',
+ conv=False)
+
+ @requires_tcl(8, 6, 5)
+ def test_configure_proxyrelief(self):
+ widget = self.create()
+ self.checkReliefParam(widget, 'proxyrelief')
+
+ def test_configure_sashcursor(self):
+ widget = self.create()
+ self.checkCursorParam(widget, 'sashcursor')
+
+ def test_configure_sashpad(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'sashpad', 8, 1.3, 2.6, -2, '2m')
+
+ def test_configure_sashrelief(self):
+ widget = self.create()
+ self.checkReliefParam(widget, 'sashrelief')
+
+ def test_configure_sashwidth(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m',
+ conv=False)
+
+ def test_configure_showhandle(self):
+ widget = self.create()
+ self.checkBooleanParam(widget, 'showhandle')
+
+ def test_configure_width(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i',
+ conv=False)
+
+ def create2(self):
+ p = self.create()
+ b = tkinter.Button(p)
+ c = tkinter.Button(p)
+ p.add(b)
+ p.add(c)
+ return p, b, c
+
+ def test_paneconfigure(self):
+ p, b, c = self.create2()
+ self.assertRaises(TypeError, p.paneconfigure)
+ d = p.paneconfigure(b)
+ self.assertIsInstance(d, dict)
+ for k, v in d.items():
+ self.assertEqual(len(v), 5)
+ self.assertEqual(v, p.paneconfigure(b, k))
+ self.assertEqual(v[4], p.panecget(b, k))
+
+ def check_paneconfigure(self, p, b, name, value, expected):
+ if not self.wantobjects:
+ expected = str(expected)
+ p.paneconfigure(b, **{name: value})
+ self.assertEqual(p.paneconfigure(b, name)[4], expected)
+ self.assertEqual(p.panecget(b, name), expected)
+
+ def check_paneconfigure_bad(self, p, b, name, msg):
+ with self.assertRaisesRegex(TclError, msg):
+ p.paneconfigure(b, **{name: 'badValue'})
+
+ def test_paneconfigure_after(self):
+ p, b, c = self.create2()
+ self.check_paneconfigure(p, b, 'after', c, str(c))
+ self.check_paneconfigure_bad(p, b, 'after',
+ 'bad window path name "badValue"')
+
+ def test_paneconfigure_before(self):
+ p, b, c = self.create2()
+ self.check_paneconfigure(p, b, 'before', c, str(c))
+ self.check_paneconfigure_bad(p, b, 'before',
+ 'bad window path name "badValue"')
+
+ def test_paneconfigure_height(self):
+ p, b, c = self.create2()
+ self.check_paneconfigure(p, b, 'height', 10, 10)
+ self.check_paneconfigure_bad(p, b, 'height',
+ 'bad screen distance "badValue"')
+
+ def test_paneconfigure_hide(self):
+ p, b, c = self.create2()
+ self.check_paneconfigure(p, b, 'hide', False, 0)
+ self.check_paneconfigure_bad(p, b, 'hide',
+ 'expected boolean value but got "badValue"')
+
+ def test_paneconfigure_minsize(self):
+ p, b, c = self.create2()
+ self.check_paneconfigure(p, b, 'minsize', 10, 10)
+ self.check_paneconfigure_bad(p, b, 'minsize',
+ 'bad screen distance "badValue"')
+
+ def test_paneconfigure_padx(self):
+ p, b, c = self.create2()
+ self.check_paneconfigure(p, b, 'padx', 1.3, 1)
+ self.check_paneconfigure_bad(p, b, 'padx',
+ 'bad screen distance "badValue"')
+
+ def test_paneconfigure_pady(self):
+ p, b, c = self.create2()
+ self.check_paneconfigure(p, b, 'pady', 1.3, 1)
+ self.check_paneconfigure_bad(p, b, 'pady',
+ 'bad screen distance "badValue"')
+
+ def test_paneconfigure_sticky(self):
+ p, b, c = self.create2()
+ self.check_paneconfigure(p, b, 'sticky', 'nsew', 'nesw')
+ self.check_paneconfigure_bad(p, b, 'sticky',
+ 'bad stickyness value "badValue": must '
+ 'be a string containing zero or more of '
+ 'n, e, s, and w')
+
+ def test_paneconfigure_stretch(self):
+ p, b, c = self.create2()
+ self.check_paneconfigure(p, b, 'stretch', 'alw', 'always')
+ self.check_paneconfigure_bad(p, b, 'stretch',
+ 'bad stretch "badValue": must be '
+ 'always, first, last, middle, or never')
+
+ def test_paneconfigure_width(self):
+ p, b, c = self.create2()
+ self.check_paneconfigure(p, b, 'width', 10, 10)
+ self.check_paneconfigure_bad(p, b, 'width',
+ 'bad screen distance "badValue"')
+
+
+@add_standard_options(StandardOptionsTests)
+class MenuTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'activebackground', 'activeborderwidth', 'activeforeground',
+ 'background', 'borderwidth', 'cursor',
+ 'disabledforeground', 'font', 'foreground',
+ 'postcommand', 'relief', 'selectcolor', 'takefocus',
+ 'tearoff', 'tearoffcommand', 'title', 'type',
+ )
+ _conv_pixels = False
+
+ def create(self, **kwargs):
+ return tkinter.Menu(self.root, **kwargs)
+
+ def test_configure_postcommand(self):
+ widget = self.create()
+ self.checkCommandParam(widget, 'postcommand')
+
+ def test_configure_tearoff(self):
+ widget = self.create()
+ self.checkBooleanParam(widget, 'tearoff')
+
+ def test_configure_tearoffcommand(self):
+ widget = self.create()
+ self.checkCommandParam(widget, 'tearoffcommand')
+
+ def test_configure_title(self):
+ widget = self.create()
+ self.checkParam(widget, 'title', 'any string')
+
+ def test_configure_type(self):
+ widget = self.create()
+ self.checkEnumParam(
+ widget, 'type',
+ 'normal', 'tearoff', 'menubar',
+ errmsg='bad type "{}": must be normal, tearoff, or menubar',
+ )
+
+ def test_entryconfigure(self):
+ m1 = self.create()
+ m1.add_command(label='test')
+ self.assertRaises(TypeError, m1.entryconfigure)
+ with self.assertRaisesRegex(TclError, 'bad menu entry index "foo"'):
+ m1.entryconfigure('foo')
+ d = m1.entryconfigure(1)
+ self.assertIsInstance(d, dict)
+ for k, v in d.items():
+ self.assertIsInstance(k, str)
+ self.assertIsInstance(v, tuple)
+ self.assertEqual(len(v), 5)
+ self.assertEqual(v[0], k)
+ self.assertEqual(m1.entrycget(1, k), v[4])
+ m1.destroy()
+
+ def test_entryconfigure_label(self):
+ m1 = self.create()
+ m1.add_command(label='test')
+ self.assertEqual(m1.entrycget(1, 'label'), 'test')
+ m1.entryconfigure(1, label='changed')
+ self.assertEqual(m1.entrycget(1, 'label'), 'changed')
+
+ def test_entryconfigure_variable(self):
+ m1 = self.create()
+ v1 = tkinter.BooleanVar(self.root)
+ v2 = tkinter.BooleanVar(self.root)
+ m1.add_checkbutton(variable=v1, onvalue=True, offvalue=False,
+ label='Nonsense')
+ self.assertEqual(str(m1.entrycget(1, 'variable')), str(v1))
+ m1.entryconfigure(1, variable=v2)
+ self.assertEqual(str(m1.entrycget(1, 'variable')), str(v2))
+
+
+@add_standard_options(PixelSizeTests, StandardOptionsTests)
+class MessageTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'anchor', 'aspect', 'background', 'borderwidth',
+ 'cursor', 'font', 'foreground',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'justify', 'padx', 'pady', 'relief',
+ 'takefocus', 'text', 'textvariable', 'width',
+ )
+ _conv_pad_pixels = False
+
+ def create(self, **kwargs):
+ return tkinter.Message(self.root, **kwargs)
+
+ def test_configure_aspect(self):
+ widget = self.create()
+ self.checkIntegerParam(widget, 'aspect', 250, 0, -300)
+
+
+class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
+
+ def test_frame(self):
+ self._test_widget(tkinter.Frame)
+
+ def test_label(self):
+ self._test_widget(tkinter.Label)
+
+
+tests_gui = (
+ ButtonTest, CanvasTest, CheckbuttonTest, EntryTest,
+ FrameTest, LabelFrameTest,LabelTest, ListboxTest,
+ MenubuttonTest, MenuTest, MessageTest, OptionMenuTest,
+ PanedWindowTest, RadiobuttonTest, ScaleTest, ScrollbarTest,
+ SpinboxTest, TextTest, ToplevelTest, DefaultRootTest,
+)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Lib/test/test_tkinter/widget_tests.py b/Lib/test/test_tkinter/widget_tests.py
new file mode 100644
index 0000000..85b0511
--- /dev/null
+++ b/Lib/test/test_tkinter/widget_tests.py
@@ -0,0 +1,519 @@
+# Common tests for test_tkinter/test_widgets.py and test_ttk/test_widgets.py
+
+import tkinter
+from test.test_tkinter.support import (AbstractTkTest, tcl_version,
+ pixels_conv, tcl_obj_eq)
+import test.support
+
+
+_sentinel = object()
+
+class AbstractWidgetTest(AbstractTkTest):
+ _conv_pixels = round
+ _conv_pad_pixels = None
+ _stringify = False
+
+ @property
+ def scaling(self):
+ try:
+ return self._scaling
+ except AttributeError:
+ self._scaling = float(self.root.call('tk', 'scaling'))
+ return self._scaling
+
+ def _str(self, value):
+ if not self._stringify and self.wantobjects and tcl_version >= (8, 6):
+ return value
+ if isinstance(value, tuple):
+ return ' '.join(map(self._str, value))
+ return str(value)
+
+ def assertEqual2(self, actual, expected, msg=None, eq=object.__eq__):
+ if eq(actual, expected):
+ return
+ self.assertEqual(actual, expected, msg)
+
+ def checkParam(self, widget, name, value, *, expected=_sentinel,
+ conv=False, eq=None):
+ widget[name] = value
+ if expected is _sentinel:
+ expected = value
+ if conv:
+ expected = conv(expected)
+ if self._stringify or not self.wantobjects:
+ if isinstance(expected, tuple):
+ expected = tkinter._join(expected)
+ else:
+ expected = str(expected)
+ if eq is None:
+ eq = tcl_obj_eq
+ self.assertEqual2(widget[name], expected, eq=eq)
+ self.assertEqual2(widget.cget(name), expected, eq=eq)
+ t = widget.configure(name)
+ self.assertEqual(len(t), 5)
+ self.assertEqual2(t[4], expected, eq=eq)
+
+ def checkInvalidParam(self, widget, name, value, errmsg=None):
+ orig = widget[name]
+ if errmsg is not None:
+ errmsg = errmsg.format(value)
+ with self.assertRaises(tkinter.TclError) as cm:
+ widget[name] = value
+ if errmsg is not None:
+ self.assertEqual(str(cm.exception), errmsg)
+ self.assertEqual(widget[name], orig)
+ with self.assertRaises(tkinter.TclError) as cm:
+ widget.configure({name: value})
+ if errmsg is not None:
+ self.assertEqual(str(cm.exception), errmsg)
+ self.assertEqual(widget[name], orig)
+
+ def checkParams(self, widget, name, *values, **kwargs):
+ for value in values:
+ self.checkParam(widget, name, value, **kwargs)
+
+ def checkIntegerParam(self, widget, name, *values, **kwargs):
+ self.checkParams(widget, name, *values, **kwargs)
+ self.checkInvalidParam(widget, name, '',
+ errmsg='expected integer but got ""')
+ self.checkInvalidParam(widget, name, '10p',
+ errmsg='expected integer but got "10p"')
+ self.checkInvalidParam(widget, name, 3.2,
+ errmsg='expected integer but got "3.2"')
+
+ def checkFloatParam(self, widget, name, *values, conv=float, **kwargs):
+ for value in values:
+ self.checkParam(widget, name, value, conv=conv, **kwargs)
+ self.checkInvalidParam(widget, name, '',
+ errmsg='expected floating-point number but got ""')
+ self.checkInvalidParam(widget, name, 'spam',
+ errmsg='expected floating-point number but got "spam"')
+
+ def checkBooleanParam(self, widget, name):
+ for value in (False, 0, 'false', 'no', 'off'):
+ self.checkParam(widget, name, value, expected=0)
+ for value in (True, 1, 'true', 'yes', 'on'):
+ self.checkParam(widget, name, value, expected=1)
+ self.checkInvalidParam(widget, name, '',
+ errmsg='expected boolean value but got ""')
+ self.checkInvalidParam(widget, name, 'spam',
+ errmsg='expected boolean value but got "spam"')
+
+ def checkColorParam(self, widget, name, *, allow_empty=None, **kwargs):
+ self.checkParams(widget, name,
+ '#ff0000', '#00ff00', '#0000ff', '#123456',
+ 'red', 'green', 'blue', 'white', 'black', 'grey',
+ **kwargs)
+ self.checkInvalidParam(widget, name, 'spam',
+ errmsg='unknown color name "spam"')
+
+ def checkCursorParam(self, widget, name, **kwargs):
+ self.checkParams(widget, name, 'arrow', 'watch', 'cross', '',**kwargs)
+ self.checkParam(widget, name, 'none')
+ self.checkInvalidParam(widget, name, 'spam',
+ errmsg='bad cursor spec "spam"')
+
+ def checkCommandParam(self, widget, name):
+ def command(*args):
+ pass
+ widget[name] = command
+ self.assertTrue(widget[name])
+ self.checkParams(widget, name, '')
+
+ def checkEnumParam(self, widget, name, *values, errmsg=None, **kwargs):
+ self.checkParams(widget, name, *values, **kwargs)
+ if errmsg is None:
+ errmsg2 = ' %s "{}": must be %s%s or %s' % (
+ name,
+ ', '.join(values[:-1]),
+ ',' if len(values) > 2 else '',
+ values[-1])
+ self.checkInvalidParam(widget, name, '',
+ errmsg='ambiguous' + errmsg2)
+ errmsg = 'bad' + errmsg2
+ self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg)
+
+ def checkPixelsParam(self, widget, name, *values,
+ conv=None, **kwargs):
+ if conv is None:
+ conv = self._conv_pixels
+ for value in values:
+ expected = _sentinel
+ conv1 = conv
+ if isinstance(value, str):
+ if conv1 and conv1 is not str:
+ expected = pixels_conv(value) * self.scaling
+ conv1 = round
+ self.checkParam(widget, name, value, expected=expected,
+ conv=conv1, **kwargs)
+ self.checkInvalidParam(widget, name, '6x',
+ errmsg='bad screen distance "6x"')
+ self.checkInvalidParam(widget, name, 'spam',
+ errmsg='bad screen distance "spam"')
+
+ def checkReliefParam(self, widget, name):
+ self.checkParams(widget, name,
+ 'flat', 'groove', 'raised', 'ridge', 'solid', 'sunken')
+ errmsg='bad relief "spam": must be '\
+ 'flat, groove, raised, ridge, solid, or sunken'
+ if tcl_version < (8, 6):
+ errmsg = None
+ self.checkInvalidParam(widget, name, 'spam',
+ errmsg=errmsg)
+
+ def checkImageParam(self, widget, name):
+ image = tkinter.PhotoImage(master=self.root, name='image1')
+ self.checkParam(widget, name, image, conv=str)
+ self.checkInvalidParam(widget, name, 'spam',
+ errmsg='image "spam" doesn\'t exist')
+ widget[name] = ''
+
+ def checkVariableParam(self, widget, name, var):
+ self.checkParam(widget, name, var, conv=str)
+
+ def assertIsBoundingBox(self, bbox):
+ self.assertIsNotNone(bbox)
+ self.assertIsInstance(bbox, tuple)
+ if len(bbox) != 4:
+ self.fail('Invalid bounding box: %r' % (bbox,))
+ for item in bbox:
+ if not isinstance(item, int):
+ self.fail('Invalid bounding box: %r' % (bbox,))
+ break
+
+
+ def test_keys(self):
+ widget = self.create()
+ keys = widget.keys()
+ self.assertEqual(sorted(keys), sorted(widget.configure()))
+ for k in keys:
+ widget[k]
+ # Test if OPTIONS contains all keys
+ if test.support.verbose:
+ aliases = {
+ 'bd': 'borderwidth',
+ 'bg': 'background',
+ 'fg': 'foreground',
+ 'invcmd': 'invalidcommand',
+ 'vcmd': 'validatecommand',
+ }
+ keys = set(keys)
+ expected = set(self.OPTIONS)
+ for k in sorted(keys - expected):
+ if not (k in aliases and
+ aliases[k] in keys and
+ aliases[k] in expected):
+ print('%s.OPTIONS doesn\'t contain "%s"' %
+ (self.__class__.__name__, k))
+
+
+class StandardOptionsTests:
+ STANDARD_OPTIONS = (
+ 'activebackground', 'activeborderwidth', 'activeforeground', 'anchor',
+ 'background', 'bitmap', 'borderwidth', 'compound', 'cursor',
+ 'disabledforeground', 'exportselection', 'font', 'foreground',
+ 'highlightbackground', 'highlightcolor', 'highlightthickness',
+ 'image', 'insertbackground', 'insertborderwidth',
+ 'insertofftime', 'insertontime', 'insertwidth',
+ 'jump', 'justify', 'orient', 'padx', 'pady', 'relief',
+ 'repeatdelay', 'repeatinterval',
+ 'selectbackground', 'selectborderwidth', 'selectforeground',
+ 'setgrid', 'takefocus', 'text', 'textvariable', 'troughcolor',
+ 'underline', 'wraplength', 'xscrollcommand', 'yscrollcommand',
+ )
+
+ def test_configure_activebackground(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'activebackground')
+
+ def test_configure_activeborderwidth(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'activeborderwidth',
+ 0, 1.3, 2.9, 6, -2, '10p')
+
+ def test_configure_activeforeground(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'activeforeground')
+
+ def test_configure_anchor(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'anchor',
+ 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center')
+
+ def test_configure_background(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'background')
+ if 'bg' in self.OPTIONS:
+ self.checkColorParam(widget, 'bg')
+
+ def test_configure_bitmap(self):
+ widget = self.create()
+ self.checkParam(widget, 'bitmap', 'questhead')
+ self.checkParam(widget, 'bitmap', 'gray50')
+ filename = test.support.findfile('python.xbm', subdir='imghdrdata')
+ self.checkParam(widget, 'bitmap', '@' + filename)
+ # Cocoa Tk widgets don't detect invalid -bitmap values
+ # See https://core.tcl.tk/tk/info/31cd33dbf0
+ if not ('aqua' in self.root.tk.call('tk', 'windowingsystem') and
+ 'AppKit' in self.root.winfo_server()):
+ self.checkInvalidParam(widget, 'bitmap', 'spam',
+ errmsg='bitmap "spam" not defined')
+
+ def test_configure_borderwidth(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'borderwidth',
+ 0, 1.3, 2.6, 6, -2, '10p')
+ if 'bd' in self.OPTIONS:
+ self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, -2, '10p')
+
+ def test_configure_compound(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'compound',
+ 'bottom', 'center', 'left', 'none', 'right', 'top')
+
+ def test_configure_cursor(self):
+ widget = self.create()
+ self.checkCursorParam(widget, 'cursor')
+
+ def test_configure_disabledforeground(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'disabledforeground')
+
+ def test_configure_exportselection(self):
+ widget = self.create()
+ self.checkBooleanParam(widget, 'exportselection')
+
+ def test_configure_font(self):
+ widget = self.create()
+ self.checkParam(widget, 'font',
+ '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*')
+ self.checkInvalidParam(widget, 'font', '',
+ errmsg='font "" doesn\'t exist')
+
+ def test_configure_foreground(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'foreground')
+ if 'fg' in self.OPTIONS:
+ self.checkColorParam(widget, 'fg')
+
+ def test_configure_highlightbackground(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'highlightbackground')
+
+ def test_configure_highlightcolor(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'highlightcolor')
+
+ def test_configure_highlightthickness(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'highlightthickness',
+ 0, 1.3, 2.6, 6, '10p')
+ self.checkParam(widget, 'highlightthickness', -2, expected=0,
+ conv=self._conv_pixels)
+
+ def test_configure_image(self):
+ widget = self.create()
+ self.checkImageParam(widget, 'image')
+
+ def test_configure_insertbackground(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'insertbackground')
+
+ def test_configure_insertborderwidth(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'insertborderwidth',
+ 0, 1.3, 2.6, 6, -2, '10p')
+
+ def test_configure_insertofftime(self):
+ widget = self.create()
+ self.checkIntegerParam(widget, 'insertofftime', 100)
+
+ def test_configure_insertontime(self):
+ widget = self.create()
+ self.checkIntegerParam(widget, 'insertontime', 100)
+
+ def test_configure_insertwidth(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p')
+
+ def test_configure_jump(self):
+ widget = self.create()
+ self.checkBooleanParam(widget, 'jump')
+
+ def test_configure_justify(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'justify', 'left', 'right', 'center',
+ errmsg='bad justification "{}": must be '
+ 'left, right, or center')
+ self.checkInvalidParam(widget, 'justify', '',
+ errmsg='ambiguous justification "": must be '
+ 'left, right, or center')
+
+ def test_configure_orient(self):
+ widget = self.create()
+ self.assertEqual(str(widget['orient']), self.default_orient)
+ self.checkEnumParam(widget, 'orient', 'horizontal', 'vertical')
+
+ def test_configure_padx(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, -2, '12m',
+ conv=self._conv_pad_pixels)
+
+ def test_configure_pady(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, -2, '12m',
+ conv=self._conv_pad_pixels)
+
+ def test_configure_relief(self):
+ widget = self.create()
+ self.checkReliefParam(widget, 'relief')
+
+ def test_configure_repeatdelay(self):
+ widget = self.create()
+ self.checkIntegerParam(widget, 'repeatdelay', -500, 500)
+
+ def test_configure_repeatinterval(self):
+ widget = self.create()
+ self.checkIntegerParam(widget, 'repeatinterval', -500, 500)
+
+ def test_configure_selectbackground(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'selectbackground')
+
+ def test_configure_selectborderwidth(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p')
+
+ def test_configure_selectforeground(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'selectforeground')
+
+ def test_configure_setgrid(self):
+ widget = self.create()
+ self.checkBooleanParam(widget, 'setgrid')
+
+ def test_configure_state(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'state', 'active', 'disabled', 'normal')
+
+ def test_configure_takefocus(self):
+ widget = self.create()
+ self.checkParams(widget, 'takefocus', '0', '1', '')
+
+ def test_configure_text(self):
+ widget = self.create()
+ self.checkParams(widget, 'text', '', 'any string')
+
+ def test_configure_textvariable(self):
+ widget = self.create()
+ var = tkinter.StringVar(self.root)
+ self.checkVariableParam(widget, 'textvariable', var)
+
+ def test_configure_troughcolor(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'troughcolor')
+
+ def test_configure_underline(self):
+ widget = self.create()
+ self.checkIntegerParam(widget, 'underline', 0, 1, 10)
+
+ def test_configure_wraplength(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'wraplength', 100)
+
+ def test_configure_xscrollcommand(self):
+ widget = self.create()
+ self.checkCommandParam(widget, 'xscrollcommand')
+
+ def test_configure_yscrollcommand(self):
+ widget = self.create()
+ self.checkCommandParam(widget, 'yscrollcommand')
+
+ # non-standard but common options
+
+ def test_configure_command(self):
+ widget = self.create()
+ self.checkCommandParam(widget, 'command')
+
+ def test_configure_indicatoron(self):
+ widget = self.create()
+ self.checkBooleanParam(widget, 'indicatoron')
+
+ def test_configure_offrelief(self):
+ widget = self.create()
+ self.checkReliefParam(widget, 'offrelief')
+
+ def test_configure_overrelief(self):
+ widget = self.create()
+ self.checkReliefParam(widget, 'overrelief')
+
+ def test_configure_selectcolor(self):
+ widget = self.create()
+ self.checkColorParam(widget, 'selectcolor')
+
+ def test_configure_selectimage(self):
+ widget = self.create()
+ self.checkImageParam(widget, 'selectimage')
+
+ def test_configure_tristateimage(self):
+ widget = self.create()
+ self.checkImageParam(widget, 'tristateimage')
+
+ def test_configure_tristatevalue(self):
+ widget = self.create()
+ self.checkParam(widget, 'tristatevalue', 'unknowable')
+
+ def test_configure_variable(self):
+ widget = self.create()
+ var = tkinter.DoubleVar(self.root)
+ self.checkVariableParam(widget, 'variable', var)
+
+
+class IntegerSizeTests:
+ def test_configure_height(self):
+ widget = self.create()
+ self.checkIntegerParam(widget, 'height', 100, -100, 0)
+
+ def test_configure_width(self):
+ widget = self.create()
+ self.checkIntegerParam(widget, 'width', 402, -402, 0)
+
+
+class PixelSizeTests:
+ def test_configure_height(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '3c')
+
+ def test_configure_width(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i')
+
+
+def add_standard_options(*source_classes):
+ # This decorator adds test_configure_xxx methods from source classes for
+ # every xxx option in the OPTIONS class attribute if they are not defined
+ # explicitly.
+ def decorator(cls):
+ for option in cls.OPTIONS:
+ methodname = 'test_configure_' + option
+ if not hasattr(cls, methodname):
+ for source_class in source_classes:
+ if hasattr(source_class, methodname):
+ setattr(cls, methodname,
+ getattr(source_class, methodname))
+ break
+ else:
+ def test(self, option=option):
+ widget = self.create()
+ widget[option]
+ raise AssertionError('Option "%s" is not tested in %s' %
+ (option, cls.__name__))
+ test.__name__ = methodname
+ setattr(cls, methodname, test)
+ return cls
+ return decorator
+
+def setUpModule():
+ if test.support.verbose:
+ tcl = tkinter.Tcl()
+ print('patchlevel =', tcl.call('info', 'patchlevel'), flush=True)
diff --git a/Lib/test/test_ttk_guionly.py b/Lib/test/test_ttk/__init__.py
index c491904..7ee7ffb 100644
--- a/Lib/test/test_ttk_guionly.py
+++ b/Lib/test/test_ttk/__init__.py
@@ -1,10 +1,11 @@
+import os.path
import unittest
from test import support
from test.support import import_helper
-from test.support import check_sanitizer
-if check_sanitizer(address=True, memory=True):
- raise unittest.SkipTest("Tests involvin libX11 can SEGFAULT on ASAN/MSAN builds")
+
+if support.check_sanitizer(address=True, memory=True):
+ raise unittest.SkipTest("Tests involving libX11 can SEGFAULT on ASAN/MSAN builds")
# Skip this test if _tkinter wasn't built.
import_helper.import_module('_tkinter')
@@ -12,6 +13,7 @@ import_helper.import_module('_tkinter')
# Skip test if tk cannot be initialized.
support.requires('gui')
+
import tkinter
from _tkinter import TclError
from tkinter import ttk
@@ -32,9 +34,6 @@ def setUpModule():
root.destroy()
del root
-def load_tests(loader, tests, pattern):
- return loader.discover('tkinter.test.test_ttk')
-
-if __name__ == '__main__':
- unittest.main()
+def load_tests(*args):
+ return support.load_package_tests(os.path.dirname(__file__), *args)
diff --git a/Lib/test/test_ttk/__main__.py b/Lib/test/test_ttk/__main__.py
new file mode 100644
index 0000000..40a23a2
--- /dev/null
+++ b/Lib/test/test_ttk/__main__.py
@@ -0,0 +1,4 @@
+from . import load_tests
+import unittest
+
+unittest.main()
diff --git a/Lib/test/test_ttk/test_extensions.py b/Lib/test/test_ttk/test_extensions.py
new file mode 100644
index 0000000..6135c49
--- /dev/null
+++ b/Lib/test/test_ttk/test_extensions.py
@@ -0,0 +1,328 @@
+import sys
+import unittest
+import tkinter
+from tkinter import ttk
+from test.support import requires, gc_collect
+from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest
+
+requires('gui')
+
+class LabeledScaleTest(AbstractTkTest, unittest.TestCase):
+
+ def tearDown(self):
+ self.root.update_idletasks()
+ super().tearDown()
+
+ def test_widget_destroy(self):
+ # automatically created variable
+ x = ttk.LabeledScale(self.root)
+ var = x._variable._name
+ x.destroy()
+ gc_collect() # For PyPy or other GCs.
+ self.assertRaises(tkinter.TclError, x.tk.globalgetvar, var)
+
+ # manually created variable
+ myvar = tkinter.DoubleVar(self.root)
+ name = myvar._name
+ x = ttk.LabeledScale(self.root, variable=myvar)
+ x.destroy()
+ if self.wantobjects:
+ self.assertEqual(x.tk.globalgetvar(name), myvar.get())
+ else:
+ self.assertEqual(float(x.tk.globalgetvar(name)), myvar.get())
+ del myvar
+ gc_collect() # For PyPy or other GCs.
+ self.assertRaises(tkinter.TclError, x.tk.globalgetvar, name)
+
+ # checking that the tracing callback is properly removed
+ myvar = tkinter.IntVar(self.root)
+ # LabeledScale will start tracing myvar
+ x = ttk.LabeledScale(self.root, variable=myvar)
+ x.destroy()
+ # Unless the tracing callback was removed, creating a new
+ # LabeledScale with the same var will cause an error now. This
+ # happens because the variable will be set to (possibly) a new
+ # value which causes the tracing callback to be called and then
+ # it tries calling instance attributes not yet defined.
+ ttk.LabeledScale(self.root, variable=myvar)
+ if hasattr(sys, 'last_type'):
+ self.assertNotEqual(sys.last_type, tkinter.TclError)
+
+ def test_initialization(self):
+ # master passing
+ master = tkinter.Frame(self.root)
+ x = ttk.LabeledScale(master)
+ self.assertEqual(x.master, master)
+ x.destroy()
+
+ # variable initialization/passing
+ passed_expected = (('0', 0), (0, 0), (10, 10),
+ (-1, -1), (sys.maxsize + 1, sys.maxsize + 1),
+ (2.5, 2), ('2.5', 2))
+ for pair in passed_expected:
+ x = ttk.LabeledScale(self.root, from_=pair[0])
+ self.assertEqual(x.value, pair[1])
+ x.destroy()
+ x = ttk.LabeledScale(self.root, from_=None)
+ self.assertRaises((ValueError, tkinter.TclError), x._variable.get)
+ x.destroy()
+ # variable should have its default value set to the from_ value
+ myvar = tkinter.DoubleVar(self.root, value=20)
+ x = ttk.LabeledScale(self.root, variable=myvar)
+ self.assertEqual(x.value, 0)
+ x.destroy()
+ # check that it is really using a DoubleVar
+ x = ttk.LabeledScale(self.root, variable=myvar, from_=0.5)
+ self.assertEqual(x.value, 0.5)
+ self.assertEqual(x._variable._name, myvar._name)
+ x.destroy()
+
+ # widget positionment
+ def check_positions(scale, scale_pos, label, label_pos):
+ self.assertEqual(scale.pack_info()['side'], scale_pos)
+ self.assertEqual(label.place_info()['anchor'], label_pos)
+ x = ttk.LabeledScale(self.root, compound='top')
+ check_positions(x.scale, 'bottom', x.label, 'n')
+ x.destroy()
+ x = ttk.LabeledScale(self.root, compound='bottom')
+ check_positions(x.scale, 'top', x.label, 's')
+ x.destroy()
+ # invert default positions
+ x = ttk.LabeledScale(self.root, compound='unknown')
+ check_positions(x.scale, 'top', x.label, 's')
+ x.destroy()
+ x = ttk.LabeledScale(self.root) # take default positions
+ check_positions(x.scale, 'bottom', x.label, 'n')
+ x.destroy()
+
+ # extra, and invalid, kwargs
+ self.assertRaises(tkinter.TclError, ttk.LabeledScale, master, a='b')
+
+
+ def test_horizontal_range(self):
+ lscale = ttk.LabeledScale(self.root, from_=0, to=10)
+ lscale.pack()
+ lscale.update()
+
+ linfo_1 = lscale.label.place_info()
+ prev_xcoord = lscale.scale.coords()[0]
+ self.assertEqual(prev_xcoord, int(linfo_1['x']))
+ # change range to: from -5 to 5. This should change the x coord of
+ # the scale widget, since 0 is at the middle of the new
+ # range.
+ lscale.scale.configure(from_=-5, to=5)
+ # The following update is needed since the test doesn't use mainloop,
+ # at the same time this shouldn't affect test outcome
+ lscale.update()
+ curr_xcoord = lscale.scale.coords()[0]
+ self.assertNotEqual(prev_xcoord, curr_xcoord)
+ # the label widget should have been repositioned too
+ linfo_2 = lscale.label.place_info()
+ self.assertEqual(lscale.label['text'], 0 if self.wantobjects else '0')
+ self.assertEqual(curr_xcoord, int(linfo_2['x']))
+ # change the range back
+ lscale.scale.configure(from_=0, to=10)
+ self.assertNotEqual(prev_xcoord, curr_xcoord)
+ self.assertEqual(prev_xcoord, int(linfo_1['x']))
+
+ lscale.destroy()
+
+
+ def test_variable_change(self):
+ x = ttk.LabeledScale(self.root)
+ x.pack()
+ x.update()
+
+ curr_xcoord = x.scale.coords()[0]
+ newval = x.value + 1
+ x.value = newval
+ # The following update is needed since the test doesn't use mainloop,
+ # at the same time this shouldn't affect test outcome
+ x.update()
+ self.assertEqual(x.value, newval)
+ self.assertEqual(x.label['text'],
+ newval if self.wantobjects else str(newval))
+ self.assertEqual(float(x.scale.get()), newval)
+ self.assertGreater(x.scale.coords()[0], curr_xcoord)
+ self.assertEqual(x.scale.coords()[0],
+ int(x.label.place_info()['x']))
+
+ # value outside range
+ if self.wantobjects:
+ conv = lambda x: x
+ else:
+ conv = int
+ x.value = conv(x.scale['to']) + 1 # no changes shouldn't happen
+ x.update()
+ self.assertEqual(x.value, newval)
+ self.assertEqual(conv(x.label['text']), newval)
+ self.assertEqual(float(x.scale.get()), newval)
+ self.assertEqual(x.scale.coords()[0],
+ int(x.label.place_info()['x']))
+
+ # non-integer value
+ x.value = newval = newval + 1.5
+ x.update()
+ self.assertEqual(x.value, int(newval))
+ self.assertEqual(conv(x.label['text']), int(newval))
+ self.assertEqual(float(x.scale.get()), newval)
+
+ x.destroy()
+
+
+ def test_resize(self):
+ x = ttk.LabeledScale(self.root)
+ x.pack(expand=True, fill='both')
+ gc_collect() # For PyPy or other GCs.
+ x.update()
+
+ width, height = x.master.winfo_width(), x.master.winfo_height()
+ width_new, height_new = width * 2, height * 2
+
+ x.value = 3
+ x.update()
+ x.master.wm_geometry("%dx%d" % (width_new, height_new))
+ self.assertEqual(int(x.label.place_info()['x']),
+ x.scale.coords()[0])
+
+ # Reset geometry
+ x.master.wm_geometry("%dx%d" % (width, height))
+ x.destroy()
+
+
+class OptionMenuTest(AbstractTkTest, unittest.TestCase):
+
+ def setUp(self):
+ super().setUp()
+ self.textvar = tkinter.StringVar(self.root)
+
+ def tearDown(self):
+ del self.textvar
+ super().tearDown()
+
+
+ def test_widget_destroy(self):
+ var = tkinter.StringVar(self.root)
+ optmenu = ttk.OptionMenu(self.root, var)
+ name = var._name
+ optmenu.update_idletasks()
+ optmenu.destroy()
+ self.assertEqual(optmenu.tk.globalgetvar(name), var.get())
+ del var
+ gc_collect() # For PyPy or other GCs.
+ self.assertRaises(tkinter.TclError, optmenu.tk.globalgetvar, name)
+
+
+ def test_initialization(self):
+ self.assertRaises(tkinter.TclError,
+ ttk.OptionMenu, self.root, self.textvar, invalid='thing')
+
+ optmenu = ttk.OptionMenu(self.root, self.textvar, 'b', 'a', 'b')
+ self.assertEqual(optmenu._variable.get(), 'b')
+
+ self.assertTrue(optmenu['menu'])
+ self.assertTrue(optmenu['textvariable'])
+
+ optmenu.destroy()
+
+
+ def test_menu(self):
+ items = ('a', 'b', 'c')
+ default = 'a'
+ optmenu = ttk.OptionMenu(self.root, self.textvar, default, *items)
+ found_default = False
+ for i in range(len(items)):
+ value = optmenu['menu'].entrycget(i, 'value')
+ self.assertEqual(value, items[i])
+ if value == default:
+ found_default = True
+ self.assertTrue(found_default)
+ optmenu.destroy()
+
+ # default shouldn't be in menu if it is not part of values
+ default = 'd'
+ optmenu = ttk.OptionMenu(self.root, self.textvar, default, *items)
+ curr = None
+ i = 0
+ while True:
+ last, curr = curr, optmenu['menu'].entryconfigure(i, 'value')
+ if last == curr:
+ # no more menu entries
+ break
+ self.assertNotEqual(curr, default)
+ i += 1
+ self.assertEqual(i, len(items))
+
+ # check that variable is updated correctly
+ optmenu.pack()
+ gc_collect() # For PyPy or other GCs.
+ optmenu['menu'].invoke(0)
+ self.assertEqual(optmenu._variable.get(), items[0])
+
+ # changing to an invalid index shouldn't change the variable
+ self.assertRaises(tkinter.TclError, optmenu['menu'].invoke, -1)
+ self.assertEqual(optmenu._variable.get(), items[0])
+
+ optmenu.destroy()
+
+ # specifying a callback
+ success = []
+ def cb_test(item):
+ self.assertEqual(item, items[1])
+ success.append(True)
+ optmenu = ttk.OptionMenu(self.root, self.textvar, 'a', command=cb_test,
+ *items)
+ optmenu['menu'].invoke(1)
+ if not success:
+ self.fail("Menu callback not invoked")
+
+ optmenu.destroy()
+
+ def test_unique_radiobuttons(self):
+ # check that radiobuttons are unique across instances (bpo25684)
+ items = ('a', 'b', 'c')
+ default = 'a'
+ optmenu = ttk.OptionMenu(self.root, self.textvar, default, *items)
+ textvar2 = tkinter.StringVar(self.root)
+ optmenu2 = ttk.OptionMenu(self.root, textvar2, default, *items)
+ optmenu.pack()
+ optmenu2.pack()
+ optmenu['menu'].invoke(1)
+ optmenu2['menu'].invoke(2)
+ optmenu_stringvar_name = optmenu['menu'].entrycget(0, 'variable')
+ optmenu2_stringvar_name = optmenu2['menu'].entrycget(0, 'variable')
+ self.assertNotEqual(optmenu_stringvar_name,
+ optmenu2_stringvar_name)
+ self.assertEqual(self.root.tk.globalgetvar(optmenu_stringvar_name),
+ items[1])
+ self.assertEqual(self.root.tk.globalgetvar(optmenu2_stringvar_name),
+ items[2])
+
+ optmenu.destroy()
+ optmenu2.destroy()
+
+ def test_trace_variable(self):
+ # prior to bpo45160, tracing a variable would cause the callback to be made twice
+ success = []
+ items = ('a', 'b', 'c')
+ textvar = tkinter.StringVar(self.root)
+ def cb_test(*args):
+ success.append(textvar.get())
+ optmenu = ttk.OptionMenu(self.root, textvar, "a", *items)
+ optmenu.pack()
+ cb_name = textvar.trace_add("write", cb_test)
+ optmenu['menu'].invoke(1)
+ self.assertEqual(success, ['b'])
+ self.assertEqual(textvar.get(), 'b')
+ textvar.trace_remove("write", cb_name)
+ optmenu.destroy()
+
+
+class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
+
+ def test_labeledscale(self):
+ self._test_widget(ttk.LabeledScale)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_ttk/test_style.py b/Lib/test/test_ttk/test_style.py
new file mode 100644
index 0000000..0ec95cf
--- /dev/null
+++ b/Lib/test/test_ttk/test_style.py
@@ -0,0 +1,181 @@
+import unittest
+import sys
+import tkinter
+from tkinter import ttk
+from test import support
+from test.support import requires
+from test.test_tkinter.support import AbstractTkTest, get_tk_patchlevel
+
+requires('gui')
+
+CLASS_NAMES = [
+ '.', 'ComboboxPopdownFrame', 'Heading',
+ 'Horizontal.TProgressbar', 'Horizontal.TScale', 'Item', 'Sash',
+ 'TButton', 'TCheckbutton', 'TCombobox', 'TEntry',
+ 'TLabelframe', 'TLabelframe.Label', 'TMenubutton',
+ 'TNotebook', 'TNotebook.Tab', 'Toolbutton', 'TProgressbar',
+ 'TRadiobutton', 'Treeview', 'TScale', 'TScrollbar', 'TSpinbox',
+ 'Vertical.TProgressbar', 'Vertical.TScale'
+]
+
+class StyleTest(AbstractTkTest, unittest.TestCase):
+
+ def setUp(self):
+ super().setUp()
+ self.style = ttk.Style(self.root)
+
+
+ def test_configure(self):
+ style = self.style
+ style.configure('TButton', background='yellow')
+ self.assertEqual(style.configure('TButton', 'background'),
+ 'yellow')
+ self.assertIsInstance(style.configure('TButton'), dict)
+
+
+ def test_map(self):
+ style = self.style
+
+ # Single state
+ for states in ['active'], [('active',)]:
+ with self.subTest(states=states):
+ style.map('TButton', background=[(*states, 'white')])
+ expected = [('active', 'white')]
+ self.assertEqual(style.map('TButton', 'background'), expected)
+ m = style.map('TButton')
+ self.assertIsInstance(m, dict)
+ self.assertEqual(m['background'], expected)
+
+ # Multiple states
+ for states in ['pressed', '!disabled'], ['pressed !disabled'], [('pressed', '!disabled')]:
+ with self.subTest(states=states):
+ style.map('TButton', background=[(*states, 'black')])
+ expected = [('pressed', '!disabled', 'black')]
+ self.assertEqual(style.map('TButton', 'background'), expected)
+ m = style.map('TButton')
+ self.assertIsInstance(m, dict)
+ self.assertEqual(m['background'], expected)
+
+ # Default state
+ for states in [], [''], [()]:
+ with self.subTest(states=states):
+ style.map('TButton', background=[(*states, 'grey')])
+ expected = [('grey',)]
+ self.assertEqual(style.map('TButton', 'background'), expected)
+ m = style.map('TButton')
+ self.assertIsInstance(m, dict)
+ self.assertEqual(m['background'], expected)
+
+
+ def test_lookup(self):
+ style = self.style
+ style.configure('TButton', background='yellow')
+ style.map('TButton', background=[('active', 'background', 'blue')])
+
+ self.assertEqual(style.lookup('TButton', 'background'), 'yellow')
+ self.assertEqual(style.lookup('TButton', 'background',
+ ['active', 'background']), 'blue')
+ self.assertEqual(style.lookup('TButton', 'optionnotdefined',
+ default='iknewit'), 'iknewit')
+
+
+ def test_layout(self):
+ style = self.style
+ self.assertRaises(tkinter.TclError, style.layout, 'NotALayout')
+ tv_style = style.layout('Treeview')
+
+ # "erase" Treeview layout
+ style.layout('Treeview', '')
+ self.assertEqual(style.layout('Treeview'),
+ [('null', {'sticky': 'nswe'})]
+ )
+
+ # restore layout
+ style.layout('Treeview', tv_style)
+ self.assertEqual(style.layout('Treeview'), tv_style)
+
+ # should return a list
+ self.assertIsInstance(style.layout('TButton'), list)
+
+ # correct layout, but "option" doesn't exist as option
+ self.assertRaises(tkinter.TclError, style.layout, 'Treeview',
+ [('name', {'option': 'inexistent'})])
+
+
+ def test_theme_use(self):
+ self.assertRaises(tkinter.TclError, self.style.theme_use,
+ 'nonexistingname')
+
+ curr_theme = self.style.theme_use()
+ new_theme = None
+ for theme in self.style.theme_names():
+ if theme != curr_theme:
+ new_theme = theme
+ self.style.theme_use(theme)
+ break
+ else:
+ # just one theme available, can't go on with tests
+ return
+
+ self.assertFalse(curr_theme == new_theme)
+ self.assertFalse(new_theme != self.style.theme_use())
+
+ self.style.theme_use(curr_theme)
+
+
+ def test_configure_custom_copy(self):
+ style = self.style
+
+ curr_theme = self.style.theme_use()
+ self.addCleanup(self.style.theme_use, curr_theme)
+ for theme in self.style.theme_names():
+ self.style.theme_use(theme)
+ for name in CLASS_NAMES:
+ default = style.configure(name)
+ if not default:
+ continue
+ with self.subTest(theme=theme, name=name):
+ if support.verbose >= 2:
+ print('configure', theme, name, default)
+ if (theme in ('vista', 'xpnative')
+ and sys.getwindowsversion()[:2] == (6, 1)):
+ # Fails on the Windows 7 buildbot
+ continue
+ newname = f'C.{name}'
+ self.assertEqual(style.configure(newname), None)
+ style.configure(newname, **default)
+ self.assertEqual(style.configure(newname), default)
+ for key, value in default.items():
+ self.assertEqual(style.configure(newname, key), value)
+
+
+ def test_map_custom_copy(self):
+ style = self.style
+
+ curr_theme = self.style.theme_use()
+ self.addCleanup(self.style.theme_use, curr_theme)
+ for theme in self.style.theme_names():
+ self.style.theme_use(theme)
+ for name in CLASS_NAMES:
+ default = style.map(name)
+ if not default:
+ continue
+ with self.subTest(theme=theme, name=name):
+ if support.verbose >= 2:
+ print('map', theme, name, default)
+ if (theme in ('vista', 'xpnative')
+ and sys.getwindowsversion()[:2] == (6, 1)):
+ # Fails on the Windows 7 buildbot
+ continue
+ newname = f'C.{name}'
+ self.assertEqual(style.map(newname), {})
+ style.map(newname, **default)
+ if theme == 'alt' and name == '.' and get_tk_patchlevel() < (8, 6, 1):
+ default['embossed'] = [('disabled', '1')]
+ self.assertEqual(style.map(newname), default)
+ for key, value in default.items():
+ self.assertEqual(style.map(newname, key), value)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_ttk/test_widgets.py b/Lib/test/test_ttk/test_widgets.py
new file mode 100644
index 0000000..eb0cc93
--- /dev/null
+++ b/Lib/test/test_ttk/test_widgets.py
@@ -0,0 +1,1858 @@
+import unittest
+import tkinter
+from tkinter import ttk, TclError
+from test.support import requires, gc_collect
+import sys
+
+from test.test_ttk_textonly import MockTclObj
+from test.test_tkinter.support import (AbstractTkTest, tcl_version, get_tk_patchlevel,
+ simulate_mouse_click, AbstractDefaultRootTest)
+from test.test_tkinter.widget_tests import (add_standard_options,
+ AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests,
+ setUpModule)
+
+requires('gui')
+
+
+class StandardTtkOptionsTests(StandardOptionsTests):
+
+ def test_configure_class(self):
+ widget = self.create()
+ self.assertEqual(widget['class'], '')
+ errmsg='attempt to change read-only option'
+ if get_tk_patchlevel() < (8, 6, 0, 'beta', 3):
+ errmsg='Attempt to change read-only option'
+ self.checkInvalidParam(widget, 'class', 'Foo', errmsg=errmsg)
+ widget2 = self.create(class_='Foo')
+ self.assertEqual(widget2['class'], 'Foo')
+
+ def test_configure_padding(self):
+ widget = self.create()
+ self.checkParam(widget, 'padding', 0, expected=('0',))
+ self.checkParam(widget, 'padding', 5, expected=('5',))
+ self.checkParam(widget, 'padding', (5, 6), expected=('5', '6'))
+ self.checkParam(widget, 'padding', (5, 6, 7),
+ expected=('5', '6', '7'))
+ self.checkParam(widget, 'padding', (5, 6, 7, 8),
+ expected=('5', '6', '7', '8'))
+ self.checkParam(widget, 'padding', ('5p', '6p', '7p', '8p'))
+ self.checkParam(widget, 'padding', (), expected='')
+
+ def test_configure_style(self):
+ widget = self.create()
+ self.assertEqual(widget['style'], '')
+ errmsg = 'Layout Foo not found'
+ if hasattr(self, 'default_orient'):
+ errmsg = ('Layout %s.Foo not found' %
+ getattr(self, 'default_orient').title())
+ self.checkInvalidParam(widget, 'style', 'Foo',
+ errmsg=errmsg)
+ widget2 = self.create(class_='Foo')
+ self.assertEqual(widget2['class'], 'Foo')
+ # XXX
+ pass
+
+
+class WidgetTest(AbstractTkTest, unittest.TestCase):
+ """Tests methods available in every ttk widget."""
+
+ def setUp(self):
+ super().setUp()
+ self.widget = ttk.Button(self.root, width=0, text="Text")
+ self.widget.pack()
+
+ def test_identify(self):
+ self.widget.update()
+ self.assertEqual(self.widget.identify(
+ int(self.widget.winfo_width() / 2),
+ int(self.widget.winfo_height() / 2)
+ ), "label")
+ self.assertEqual(self.widget.identify(-1, -1), "")
+
+ self.assertRaises(tkinter.TclError, self.widget.identify, None, 5)
+ self.assertRaises(tkinter.TclError, self.widget.identify, 5, None)
+ self.assertRaises(tkinter.TclError, self.widget.identify, 5, '')
+
+ def test_widget_state(self):
+ # XXX not sure about the portability of all these tests
+ self.assertEqual(self.widget.state(), ())
+ self.assertEqual(self.widget.instate(['!disabled']), True)
+
+ # changing from !disabled to disabled
+ self.assertEqual(self.widget.state(['disabled']), ('!disabled', ))
+ # no state change
+ self.assertEqual(self.widget.state(['disabled']), ())
+ # change back to !disable but also active
+ self.assertEqual(self.widget.state(['!disabled', 'active']),
+ ('!active', 'disabled'))
+ # no state changes, again
+ self.assertEqual(self.widget.state(['!disabled', 'active']), ())
+ self.assertEqual(self.widget.state(['active', '!disabled']), ())
+
+ def test_cb(arg1, **kw):
+ return arg1, kw
+ self.assertEqual(self.widget.instate(['!disabled'],
+ test_cb, "hi", **{"msg": "there"}),
+ ('hi', {'msg': 'there'}))
+
+ # attempt to set invalid statespec
+ currstate = self.widget.state()
+ self.assertRaises(tkinter.TclError, self.widget.instate,
+ ['badstate'])
+ self.assertRaises(tkinter.TclError, self.widget.instate,
+ ['disabled', 'badstate'])
+ # verify that widget didn't change its state
+ self.assertEqual(currstate, self.widget.state())
+
+ # ensuring that passing None as state doesn't modify current state
+ self.widget.state(['active', '!disabled'])
+ self.assertEqual(self.widget.state(), ('active', ))
+
+
+class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
+ _conv_pixels = False
+
+
+@add_standard_options(StandardTtkOptionsTests)
+class FrameTest(AbstractToplevelTest, unittest.TestCase):
+ OPTIONS = (
+ 'borderwidth', 'class', 'cursor', 'height',
+ 'padding', 'relief', 'style', 'takefocus',
+ 'width',
+ )
+
+ def create(self, **kwargs):
+ return ttk.Frame(self.root, **kwargs)
+
+
+@add_standard_options(StandardTtkOptionsTests)
+class LabelFrameTest(AbstractToplevelTest, unittest.TestCase):
+ OPTIONS = (
+ 'borderwidth', 'class', 'cursor', 'height',
+ 'labelanchor', 'labelwidget',
+ 'padding', 'relief', 'style', 'takefocus',
+ 'text', 'underline', 'width',
+ )
+
+ def create(self, **kwargs):
+ return ttk.LabelFrame(self.root, **kwargs)
+
+ def test_configure_labelanchor(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'labelanchor',
+ 'e', 'en', 'es', 'n', 'ne', 'nw', 's', 'se', 'sw', 'w', 'wn', 'ws',
+ errmsg='Bad label anchor specification {}')
+ self.checkInvalidParam(widget, 'labelanchor', 'center')
+
+ def test_configure_labelwidget(self):
+ widget = self.create()
+ label = ttk.Label(self.root, text='Mupp', name='foo')
+ self.checkParam(widget, 'labelwidget', label, expected='.foo')
+ label.destroy()
+
+
+class AbstractLabelTest(AbstractWidgetTest):
+
+ def checkImageParam(self, widget, name):
+ image = tkinter.PhotoImage(master=self.root, name='image1')
+ image2 = tkinter.PhotoImage(master=self.root, name='image2')
+ self.checkParam(widget, name, image, expected=('image1',))
+ self.checkParam(widget, name, 'image1', expected=('image1',))
+ self.checkParam(widget, name, (image,), expected=('image1',))
+ self.checkParam(widget, name, (image, 'active', image2),
+ expected=('image1', 'active', 'image2'))
+ self.checkParam(widget, name, 'image1 active image2',
+ expected=('image1', 'active', 'image2'))
+ self.checkInvalidParam(widget, name, 'spam',
+ errmsg='image "spam" doesn\'t exist')
+
+ def test_configure_compound(self):
+ options = 'none text image center top bottom left right'.split()
+ errmsg = (
+ 'bad compound "{}": must be'
+ f' {", ".join(options[:-1])}, or {options[-1]}'
+ )
+ widget = self.create()
+ self.checkEnumParam(widget, 'compound', *options, errmsg=errmsg)
+
+ def test_configure_state(self):
+ widget = self.create()
+ self.checkParams(widget, 'state', 'active', 'disabled', 'normal')
+
+ def test_configure_width(self):
+ widget = self.create()
+ self.checkParams(widget, 'width', 402, -402, 0)
+
+
+@add_standard_options(StandardTtkOptionsTests)
+class LabelTest(AbstractLabelTest, unittest.TestCase):
+ OPTIONS = (
+ 'anchor', 'background', 'borderwidth',
+ 'class', 'compound', 'cursor', 'font', 'foreground',
+ 'image', 'justify', 'padding', 'relief', 'state', 'style',
+ 'takefocus', 'text', 'textvariable',
+ 'underline', 'width', 'wraplength',
+ )
+ _conv_pixels = False
+
+ def create(self, **kwargs):
+ return ttk.Label(self.root, **kwargs)
+
+ def test_configure_font(self):
+ widget = self.create()
+ self.checkParam(widget, 'font',
+ '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*')
+
+
+@add_standard_options(StandardTtkOptionsTests)
+class ButtonTest(AbstractLabelTest, unittest.TestCase):
+ OPTIONS = (
+ 'class', 'command', 'compound', 'cursor', 'default',
+ 'image', 'padding', 'state', 'style',
+ 'takefocus', 'text', 'textvariable',
+ 'underline', 'width',
+ )
+
+ def create(self, **kwargs):
+ return ttk.Button(self.root, **kwargs)
+
+ def test_configure_default(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'default', 'normal', 'active', 'disabled')
+
+ def test_invoke(self):
+ success = []
+ btn = ttk.Button(self.root, command=lambda: success.append(1))
+ btn.invoke()
+ self.assertTrue(success)
+
+
+@add_standard_options(StandardTtkOptionsTests)
+class CheckbuttonTest(AbstractLabelTest, unittest.TestCase):
+ OPTIONS = (
+ 'class', 'command', 'compound', 'cursor',
+ 'image',
+ 'offvalue', 'onvalue',
+ 'padding', 'state', 'style',
+ 'takefocus', 'text', 'textvariable',
+ 'underline', 'variable', 'width',
+ )
+
+ def create(self, **kwargs):
+ return ttk.Checkbutton(self.root, **kwargs)
+
+ def test_configure_offvalue(self):
+ widget = self.create()
+ self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string')
+
+ def test_configure_onvalue(self):
+ widget = self.create()
+ self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string')
+
+ def test_invoke(self):
+ success = []
+ def cb_test():
+ success.append(1)
+ return "cb test called"
+
+ cbtn = ttk.Checkbutton(self.root, command=cb_test)
+ # the variable automatically created by ttk.Checkbutton is actually
+ # undefined till we invoke the Checkbutton
+ self.assertEqual(cbtn.state(), ('alternate', ))
+ self.assertRaises(tkinter.TclError, cbtn.tk.globalgetvar,
+ cbtn['variable'])
+
+ res = cbtn.invoke()
+ self.assertEqual(res, "cb test called")
+ self.assertEqual(cbtn['onvalue'],
+ cbtn.tk.globalgetvar(cbtn['variable']))
+ self.assertTrue(success)
+
+ cbtn['command'] = ''
+ res = cbtn.invoke()
+ self.assertFalse(str(res))
+ self.assertLessEqual(len(success), 1)
+ self.assertEqual(cbtn['offvalue'],
+ cbtn.tk.globalgetvar(cbtn['variable']))
+
+
+@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
+class EntryTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'background', 'class', 'cursor',
+ 'exportselection', 'font', 'foreground',
+ 'invalidcommand', 'justify',
+ 'show', 'state', 'style', 'takefocus', 'textvariable',
+ 'validate', 'validatecommand', 'width', 'xscrollcommand',
+ )
+ IDENTIFY_AS = 'Entry.field' if sys.platform == 'darwin' else 'textarea'
+
+ def setUp(self):
+ super().setUp()
+ self.entry = self.create()
+
+ def create(self, **kwargs):
+ return ttk.Entry(self.root, **kwargs)
+
+ def test_configure_invalidcommand(self):
+ widget = self.create()
+ self.checkCommandParam(widget, 'invalidcommand')
+
+ def test_configure_show(self):
+ widget = self.create()
+ self.checkParam(widget, 'show', '*')
+ self.checkParam(widget, 'show', '')
+ self.checkParam(widget, 'show', ' ')
+
+ def test_configure_state(self):
+ widget = self.create()
+ self.checkParams(widget, 'state',
+ 'disabled', 'normal', 'readonly')
+
+ def test_configure_validate(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'validate',
+ 'all', 'key', 'focus', 'focusin', 'focusout', 'none')
+
+ def test_configure_validatecommand(self):
+ widget = self.create()
+ self.checkCommandParam(widget, 'validatecommand')
+
+ def test_bbox(self):
+ self.assertIsBoundingBox(self.entry.bbox(0))
+ self.assertRaises(tkinter.TclError, self.entry.bbox, 'noindex')
+ self.assertRaises(tkinter.TclError, self.entry.bbox, None)
+
+ def test_identify(self):
+ self.entry.pack()
+ self.entry.update()
+
+ # bpo-27313: macOS Cocoa widget differs from X, allow either
+ self.assertEqual(self.entry.identify(5, 5), self.IDENTIFY_AS)
+ self.assertEqual(self.entry.identify(-1, -1), "")
+
+ self.assertRaises(tkinter.TclError, self.entry.identify, None, 5)
+ self.assertRaises(tkinter.TclError, self.entry.identify, 5, None)
+ self.assertRaises(tkinter.TclError, self.entry.identify, 5, '')
+
+ def test_validation_options(self):
+ success = []
+ test_invalid = lambda: success.append(True)
+
+ self.entry['validate'] = 'none'
+ self.entry['validatecommand'] = lambda: False
+
+ self.entry['invalidcommand'] = test_invalid
+ self.entry.validate()
+ self.assertTrue(success)
+
+ self.entry['invalidcommand'] = ''
+ self.entry.validate()
+ self.assertEqual(len(success), 1)
+
+ self.entry['invalidcommand'] = test_invalid
+ self.entry['validatecommand'] = lambda: True
+ self.entry.validate()
+ self.assertEqual(len(success), 1)
+
+ self.entry['validatecommand'] = ''
+ self.entry.validate()
+ self.assertEqual(len(success), 1)
+
+ self.entry['validatecommand'] = True
+ self.assertRaises(tkinter.TclError, self.entry.validate)
+
+ def test_validation(self):
+ validation = []
+ def validate(to_insert):
+ if not 'a' <= to_insert.lower() <= 'z':
+ validation.append(False)
+ return False
+ validation.append(True)
+ return True
+
+ self.entry['validate'] = 'key'
+ self.entry['validatecommand'] = self.entry.register(validate), '%S'
+
+ self.entry.insert('end', 1)
+ self.entry.insert('end', 'a')
+ self.assertEqual(validation, [False, True])
+ self.assertEqual(self.entry.get(), 'a')
+
+ def test_revalidation(self):
+ def validate(content):
+ for letter in content:
+ if not 'a' <= letter.lower() <= 'z':
+ return False
+ return True
+
+ self.entry['validatecommand'] = self.entry.register(validate), '%P'
+
+ self.entry.insert('end', 'avocado')
+ self.assertEqual(self.entry.validate(), True)
+ self.assertEqual(self.entry.state(), ())
+
+ self.entry.delete(0, 'end')
+ self.assertEqual(self.entry.get(), '')
+
+ self.entry.insert('end', 'a1b')
+ self.assertEqual(self.entry.validate(), False)
+ self.assertEqual(self.entry.state(), ('invalid', ))
+
+ self.entry.delete(1)
+ self.assertEqual(self.entry.validate(), True)
+ self.assertEqual(self.entry.state(), ())
+
+
+@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
+class ComboboxTest(EntryTest, unittest.TestCase):
+ OPTIONS = (
+ 'background', 'class', 'cursor', 'exportselection',
+ 'font', 'foreground', 'height', 'invalidcommand',
+ 'justify', 'postcommand', 'show', 'state', 'style',
+ 'takefocus', 'textvariable',
+ 'validate', 'validatecommand', 'values',
+ 'width', 'xscrollcommand',
+ )
+ IDENTIFY_AS = 'Combobox.button' if sys.platform == 'darwin' else 'textarea'
+
+ def setUp(self):
+ super().setUp()
+ self.combo = self.create()
+
+ def create(self, **kwargs):
+ return ttk.Combobox(self.root, **kwargs)
+
+ def test_configure_height(self):
+ widget = self.create()
+ self.checkParams(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i')
+
+ def _show_drop_down_listbox(self):
+ width = self.combo.winfo_width()
+ x, y = width - 5, 5
+ if sys.platform != 'darwin': # there's no down arrow on macOS
+ self.assertRegex(self.combo.identify(x, y), r'.*downarrow\Z')
+ self.combo.event_generate('<ButtonPress-1>', x=x, y=y)
+ self.combo.event_generate('<ButtonRelease-1>', x=x, y=y)
+ self.combo.update_idletasks()
+
+ def test_virtual_event(self):
+ success = []
+
+ self.combo['values'] = [1]
+ self.combo.bind('<<ComboboxSelected>>',
+ lambda evt: success.append(True))
+ self.combo.pack()
+ self.combo.update()
+
+ height = self.combo.winfo_height()
+ self._show_drop_down_listbox()
+ self.combo.update()
+ self.combo.event_generate('<Return>')
+ self.combo.update()
+
+ self.assertTrue(success)
+
+ def test_configure_postcommand(self):
+ success = []
+
+ self.combo['postcommand'] = lambda: success.append(True)
+ self.combo.pack()
+ self.combo.update()
+
+ self._show_drop_down_listbox()
+ self.assertTrue(success)
+
+ # testing postcommand removal
+ self.combo['postcommand'] = ''
+ self._show_drop_down_listbox()
+ self.assertEqual(len(success), 1)
+
+ def test_configure_values(self):
+ def check_get_current(getval, currval):
+ self.assertEqual(self.combo.get(), getval)
+ self.assertEqual(self.combo.current(), currval)
+
+ self.assertEqual(self.combo['values'], '')
+ check_get_current('', -1)
+
+ self.checkParam(self.combo, 'values', 'mon tue wed thur',
+ expected=('mon', 'tue', 'wed', 'thur'))
+ self.checkParam(self.combo, 'values', ('mon', 'tue', 'wed', 'thur'))
+ self.checkParam(self.combo, 'values', (42, 3.14, '', 'any string'))
+ self.checkParam(self.combo, 'values', '')
+
+ self.combo['values'] = ['a', 1, 'c']
+
+ self.combo.set('c')
+ check_get_current('c', 2)
+
+ self.combo.current(0)
+ check_get_current('a', 0)
+
+ self.combo.set('d')
+ check_get_current('d', -1)
+
+ # testing values with empty string
+ self.combo.set('')
+ self.combo['values'] = (1, 2, '', 3)
+ check_get_current('', 2)
+
+ # testing values with empty string set through configure
+ self.combo.configure(values=[1, '', 2])
+ self.assertEqual(self.combo['values'],
+ ('1', '', '2') if self.wantobjects else
+ '1 {} 2')
+
+ # testing values with spaces
+ self.combo['values'] = ['a b', 'a\tb', 'a\nb']
+ self.assertEqual(self.combo['values'],
+ ('a b', 'a\tb', 'a\nb') if self.wantobjects else
+ '{a b} {a\tb} {a\nb}')
+
+ # testing values with special characters
+ self.combo['values'] = [r'a\tb', '"a"', '} {']
+ self.assertEqual(self.combo['values'],
+ (r'a\tb', '"a"', '} {') if self.wantobjects else
+ r'a\\tb {"a"} \}\ \{')
+
+ # out of range
+ self.assertRaises(tkinter.TclError, self.combo.current,
+ len(self.combo['values']))
+ # it expects an integer (or something that can be converted to int)
+ self.assertRaises(tkinter.TclError, self.combo.current, '')
+
+ # testing creating combobox with empty string in values
+ combo2 = ttk.Combobox(self.root, values=[1, 2, ''])
+ self.assertEqual(combo2['values'],
+ ('1', '2', '') if self.wantobjects else '1 2 {}')
+ combo2.destroy()
+
+
+@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
+class PanedWindowTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'class', 'cursor', 'height',
+ 'orient', 'style', 'takefocus', 'width',
+ )
+
+ def setUp(self):
+ super().setUp()
+ self.paned = self.create()
+
+ def create(self, **kwargs):
+ return ttk.PanedWindow(self.root, **kwargs)
+
+ def test_configure_orient(self):
+ widget = self.create()
+ self.assertEqual(str(widget['orient']), 'vertical')
+ errmsg='attempt to change read-only option'
+ if get_tk_patchlevel() < (8, 6, 0, 'beta', 3):
+ errmsg='Attempt to change read-only option'
+ self.checkInvalidParam(widget, 'orient', 'horizontal',
+ errmsg=errmsg)
+ widget2 = self.create(orient='horizontal')
+ self.assertEqual(str(widget2['orient']), 'horizontal')
+
+ def test_add(self):
+ # attempt to add a child that is not a direct child of the paned window
+ label = ttk.Label(self.paned)
+ child = ttk.Label(label)
+ self.assertRaises(tkinter.TclError, self.paned.add, child)
+ label.destroy()
+ child.destroy()
+ # another attempt
+ label = ttk.Label(self.root)
+ child = ttk.Label(label)
+ self.assertRaises(tkinter.TclError, self.paned.add, child)
+ child.destroy()
+ label.destroy()
+
+ good_child = ttk.Label(self.root)
+ self.paned.add(good_child)
+ # re-adding a child is not accepted
+ self.assertRaises(tkinter.TclError, self.paned.add, good_child)
+
+ other_child = ttk.Label(self.paned)
+ self.paned.add(other_child)
+ self.assertEqual(self.paned.pane(0), self.paned.pane(1))
+ self.assertRaises(tkinter.TclError, self.paned.pane, 2)
+ good_child.destroy()
+ other_child.destroy()
+ self.assertRaises(tkinter.TclError, self.paned.pane, 0)
+
+ def test_forget(self):
+ self.assertRaises(tkinter.TclError, self.paned.forget, None)
+ self.assertRaises(tkinter.TclError, self.paned.forget, 0)
+
+ self.paned.add(ttk.Label(self.root))
+ self.paned.forget(0)
+ self.assertRaises(tkinter.TclError, self.paned.forget, 0)
+
+ def test_insert(self):
+ self.assertRaises(tkinter.TclError, self.paned.insert, None, 0)
+ self.assertRaises(tkinter.TclError, self.paned.insert, 0, None)
+ self.assertRaises(tkinter.TclError, self.paned.insert, 0, 0)
+
+ child = ttk.Label(self.root)
+ child2 = ttk.Label(self.root)
+ child3 = ttk.Label(self.root)
+
+ self.assertRaises(tkinter.TclError, self.paned.insert, 0, child)
+
+ self.paned.insert('end', child2)
+ self.paned.insert(0, child)
+ self.assertEqual(self.paned.panes(), (str(child), str(child2)))
+
+ self.paned.insert(0, child2)
+ self.assertEqual(self.paned.panes(), (str(child2), str(child)))
+
+ self.paned.insert('end', child3)
+ self.assertEqual(self.paned.panes(),
+ (str(child2), str(child), str(child3)))
+
+ # reinserting a child should move it to its current position
+ panes = self.paned.panes()
+ self.paned.insert('end', child3)
+ self.assertEqual(panes, self.paned.panes())
+
+ # moving child3 to child2 position should result in child2 ending up
+ # in previous child position and child ending up in previous child3
+ # position
+ self.paned.insert(child2, child3)
+ self.assertEqual(self.paned.panes(),
+ (str(child3), str(child2), str(child)))
+
+ def test_pane(self):
+ self.assertRaises(tkinter.TclError, self.paned.pane, 0)
+
+ child = ttk.Label(self.root)
+ self.paned.add(child)
+ self.assertIsInstance(self.paned.pane(0), dict)
+ self.assertEqual(self.paned.pane(0, weight=None),
+ 0 if self.wantobjects else '0')
+ # newer form for querying a single option
+ self.assertEqual(self.paned.pane(0, 'weight'),
+ 0 if self.wantobjects else '0')
+ self.assertEqual(self.paned.pane(0), self.paned.pane(str(child)))
+
+ self.assertRaises(tkinter.TclError, self.paned.pane, 0,
+ badoption='somevalue')
+
+ def test_sashpos(self):
+ self.assertRaises(tkinter.TclError, self.paned.sashpos, None)
+ self.assertRaises(tkinter.TclError, self.paned.sashpos, '')
+ self.assertRaises(tkinter.TclError, self.paned.sashpos, 0)
+
+ child = ttk.Label(self.paned, text='a')
+ self.paned.add(child, weight=1)
+ self.assertRaises(tkinter.TclError, self.paned.sashpos, 0)
+ child2 = ttk.Label(self.paned, text='b')
+ self.paned.add(child2)
+ self.assertRaises(tkinter.TclError, self.paned.sashpos, 1)
+
+ self.paned.pack(expand=True, fill='both')
+
+ curr_pos = self.paned.sashpos(0)
+ self.paned.sashpos(0, 1000)
+ self.assertNotEqual(curr_pos, self.paned.sashpos(0))
+ self.assertIsInstance(self.paned.sashpos(0), int)
+
+
+@add_standard_options(StandardTtkOptionsTests)
+class RadiobuttonTest(AbstractLabelTest, unittest.TestCase):
+ OPTIONS = (
+ 'class', 'command', 'compound', 'cursor',
+ 'image',
+ 'padding', 'state', 'style',
+ 'takefocus', 'text', 'textvariable',
+ 'underline', 'value', 'variable', 'width',
+ )
+
+ def create(self, **kwargs):
+ return ttk.Radiobutton(self.root, **kwargs)
+
+ def test_configure_value(self):
+ widget = self.create()
+ self.checkParams(widget, 'value', 1, 2.3, '', 'any string')
+
+ def test_configure_invoke(self):
+ success = []
+ def cb_test():
+ success.append(1)
+ return "cb test called"
+
+ myvar = tkinter.IntVar(self.root)
+ cbtn = ttk.Radiobutton(self.root, command=cb_test,
+ variable=myvar, value=0)
+ cbtn2 = ttk.Radiobutton(self.root, command=cb_test,
+ variable=myvar, value=1)
+
+ if self.wantobjects:
+ conv = lambda x: x
+ else:
+ conv = int
+
+ res = cbtn.invoke()
+ self.assertEqual(res, "cb test called")
+ self.assertEqual(conv(cbtn['value']), myvar.get())
+ self.assertEqual(myvar.get(),
+ conv(cbtn.tk.globalgetvar(cbtn['variable'])))
+ self.assertTrue(success)
+
+ cbtn2['command'] = ''
+ res = cbtn2.invoke()
+ self.assertEqual(str(res), '')
+ self.assertLessEqual(len(success), 1)
+ self.assertEqual(conv(cbtn2['value']), myvar.get())
+ self.assertEqual(myvar.get(),
+ conv(cbtn.tk.globalgetvar(cbtn['variable'])))
+
+ self.assertEqual(str(cbtn['variable']), str(cbtn2['variable']))
+
+
+class MenubuttonTest(AbstractLabelTest, unittest.TestCase):
+ OPTIONS = (
+ 'class', 'compound', 'cursor', 'direction',
+ 'image', 'menu', 'padding', 'state', 'style',
+ 'takefocus', 'text', 'textvariable',
+ 'underline', 'width',
+ )
+
+ def create(self, **kwargs):
+ return ttk.Menubutton(self.root, **kwargs)
+
+ def test_direction(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'direction',
+ 'above', 'below', 'left', 'right', 'flush')
+
+ def test_configure_menu(self):
+ widget = self.create()
+ menu = tkinter.Menu(widget, name='menu')
+ self.checkParam(widget, 'menu', menu, conv=str)
+ menu.destroy()
+
+
+@add_standard_options(StandardTtkOptionsTests)
+class ScaleTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'class', 'command', 'cursor', 'from', 'length',
+ 'orient', 'style', 'takefocus', 'to', 'value', 'variable',
+ )
+ _conv_pixels = False
+ default_orient = 'horizontal'
+
+ def setUp(self):
+ super().setUp()
+ self.scale = self.create()
+ self.scale.pack()
+ self.scale.update()
+
+ def create(self, **kwargs):
+ return ttk.Scale(self.root, **kwargs)
+
+ def test_configure_from(self):
+ widget = self.create()
+ self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=False)
+
+ def test_configure_length(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i')
+
+ def test_configure_to(self):
+ widget = self.create()
+ self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, conv=False)
+
+ def test_configure_value(self):
+ widget = self.create()
+ self.checkFloatParam(widget, 'value', 300, 14.9, 15.1, -10, conv=False)
+
+ def test_custom_event(self):
+ failure = [1, 1, 1] # will need to be empty
+
+ funcid = self.scale.bind('<<RangeChanged>>', lambda evt: failure.pop())
+
+ self.scale['from'] = 10
+ self.scale['from_'] = 10
+ self.scale['to'] = 3
+
+ self.assertFalse(failure)
+
+ failure = [1, 1, 1]
+ self.scale.configure(from_=2, to=5)
+ self.scale.configure(from_=0, to=-2)
+ self.scale.configure(to=10)
+
+ self.assertFalse(failure)
+
+ def test_get(self):
+ if self.wantobjects:
+ conv = lambda x: x
+ else:
+ conv = float
+
+ scale_width = self.scale.winfo_width()
+ self.assertEqual(self.scale.get(scale_width, 0), self.scale['to'])
+
+ self.assertEqual(conv(self.scale.get(0, 0)), conv(self.scale['from']))
+ self.assertEqual(self.scale.get(), self.scale['value'])
+ self.scale['value'] = 30
+ self.assertEqual(self.scale.get(), self.scale['value'])
+
+ self.assertRaises(tkinter.TclError, self.scale.get, '', 0)
+ self.assertRaises(tkinter.TclError, self.scale.get, 0, '')
+
+ def test_set(self):
+ if self.wantobjects:
+ conv = lambda x: x
+ else:
+ conv = float
+
+ # set restricts the max/min values according to the current range
+ max = conv(self.scale['to'])
+ new_max = max + 10
+ self.scale.set(new_max)
+ self.assertEqual(conv(self.scale.get()), max)
+ min = conv(self.scale['from'])
+ self.scale.set(min - 1)
+ self.assertEqual(conv(self.scale.get()), min)
+
+ # changing directly the variable doesn't impose this limitation tho
+ var = tkinter.DoubleVar(self.root)
+ self.scale['variable'] = var
+ var.set(max + 5)
+ self.assertEqual(conv(self.scale.get()), var.get())
+ self.assertEqual(conv(self.scale.get()), max + 5)
+ del var
+ gc_collect() # For PyPy or other GCs.
+
+ # the same happens with the value option
+ self.scale['value'] = max + 10
+ self.assertEqual(conv(self.scale.get()), max + 10)
+ self.assertEqual(conv(self.scale.get()), conv(self.scale['value']))
+
+ # nevertheless, note that the max/min values we can get specifying
+ # x, y coords are the ones according to the current range
+ self.assertEqual(conv(self.scale.get(0, 0)), min)
+ self.assertEqual(conv(self.scale.get(self.scale.winfo_width(), 0)), max)
+
+ self.assertRaises(tkinter.TclError, self.scale.set, None)
+
+
+@add_standard_options(StandardTtkOptionsTests)
+class ProgressbarTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'class', 'cursor', 'orient', 'length',
+ 'mode', 'maximum', 'phase',
+ 'style', 'takefocus', 'value', 'variable',
+ )
+ _conv_pixels = False
+ default_orient = 'horizontal'
+
+ def create(self, **kwargs):
+ return ttk.Progressbar(self.root, **kwargs)
+
+ def test_configure_length(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'length', 100.1, 56.7, '2i')
+
+ def test_configure_maximum(self):
+ widget = self.create()
+ self.checkFloatParam(widget, 'maximum', 150.2, 77.7, 0, -10, conv=False)
+
+ def test_configure_mode(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'mode', 'determinate', 'indeterminate')
+
+ def test_configure_phase(self):
+ # XXX
+ pass
+
+ def test_configure_value(self):
+ widget = self.create()
+ self.checkFloatParam(widget, 'value', 150.2, 77.7, 0, -10,
+ conv=False)
+
+
+@unittest.skipIf(sys.platform == 'darwin',
+ 'ttk.Scrollbar is special on MacOSX')
+@add_standard_options(StandardTtkOptionsTests)
+class ScrollbarTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'class', 'command', 'cursor', 'orient', 'style', 'takefocus',
+ )
+ default_orient = 'vertical'
+
+ def create(self, **kwargs):
+ return ttk.Scrollbar(self.root, **kwargs)
+
+
+@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
+class NotebookTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'class', 'cursor', 'height', 'padding', 'style', 'takefocus', 'width',
+ )
+
+ def setUp(self):
+ super().setUp()
+ self.nb = self.create(padding=0)
+ self.child1 = ttk.Label(self.root)
+ self.child2 = ttk.Label(self.root)
+ self.nb.add(self.child1, text='a')
+ self.nb.add(self.child2, text='b')
+
+ def create(self, **kwargs):
+ return ttk.Notebook(self.root, **kwargs)
+
+ def test_tab_identifiers(self):
+ self.nb.forget(0)
+ self.nb.hide(self.child2)
+ self.assertRaises(tkinter.TclError, self.nb.tab, self.child1)
+ self.assertEqual(self.nb.index('end'), 1)
+ self.nb.add(self.child2)
+ self.assertEqual(self.nb.index('end'), 1)
+ self.nb.select(self.child2)
+
+ self.assertTrue(self.nb.tab('current'))
+ self.nb.add(self.child1, text='a')
+
+ self.nb.pack()
+ self.nb.update()
+ if sys.platform == 'darwin':
+ tb_idx = "@20,5"
+ else:
+ tb_idx = "@5,5"
+ self.assertEqual(self.nb.tab(tb_idx), self.nb.tab('current'))
+
+ for i in range(5, 100, 5):
+ try:
+ if self.nb.tab('@%d, 5' % i, text=None) == 'a':
+ break
+ except tkinter.TclError:
+ pass
+
+ else:
+ self.fail("Tab with text 'a' not found")
+
+ def test_add_and_hidden(self):
+ self.assertRaises(tkinter.TclError, self.nb.hide, -1)
+ self.assertRaises(tkinter.TclError, self.nb.hide, 'hi')
+ self.assertRaises(tkinter.TclError, self.nb.hide, None)
+ self.assertRaises(tkinter.TclError, self.nb.add, None)
+ self.assertRaises(tkinter.TclError, self.nb.add, ttk.Label(self.root),
+ unknown='option')
+
+ tabs = self.nb.tabs()
+ self.nb.hide(self.child1)
+ self.nb.add(self.child1)
+ self.assertEqual(self.nb.tabs(), tabs)
+
+ child = ttk.Label(self.root)
+ self.nb.add(child, text='c')
+ tabs = self.nb.tabs()
+
+ curr = self.nb.index('current')
+ # verify that the tab gets re-added at its previous position
+ child2_index = self.nb.index(self.child2)
+ self.nb.hide(self.child2)
+ self.nb.add(self.child2)
+ self.assertEqual(self.nb.tabs(), tabs)
+ self.assertEqual(self.nb.index(self.child2), child2_index)
+ self.assertEqual(str(self.child2), self.nb.tabs()[child2_index])
+ # but the tab next to it (not hidden) is the one selected now
+ self.assertEqual(self.nb.index('current'), curr + 1)
+
+ def test_forget(self):
+ self.assertRaises(tkinter.TclError, self.nb.forget, -1)
+ self.assertRaises(tkinter.TclError, self.nb.forget, 'hi')
+ self.assertRaises(tkinter.TclError, self.nb.forget, None)
+
+ tabs = self.nb.tabs()
+ child1_index = self.nb.index(self.child1)
+ self.nb.forget(self.child1)
+ self.assertNotIn(str(self.child1), self.nb.tabs())
+ self.assertEqual(len(tabs) - 1, len(self.nb.tabs()))
+
+ self.nb.add(self.child1)
+ self.assertEqual(self.nb.index(self.child1), 1)
+ self.assertNotEqual(child1_index, self.nb.index(self.child1))
+
+ def test_index(self):
+ self.assertRaises(tkinter.TclError, self.nb.index, -1)
+ self.assertRaises(tkinter.TclError, self.nb.index, None)
+
+ self.assertIsInstance(self.nb.index('end'), int)
+ self.assertEqual(self.nb.index(self.child1), 0)
+ self.assertEqual(self.nb.index(self.child2), 1)
+ self.assertEqual(self.nb.index('end'), 2)
+
+ def test_insert(self):
+ # moving tabs
+ tabs = self.nb.tabs()
+ self.nb.insert(1, tabs[0])
+ self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0]))
+ self.nb.insert(self.child1, self.child2)
+ self.assertEqual(self.nb.tabs(), tabs)
+ self.nb.insert('end', self.child1)
+ self.assertEqual(self.nb.tabs(), (tabs[1], tabs[0]))
+ self.nb.insert('end', 0)
+ self.assertEqual(self.nb.tabs(), tabs)
+ # bad moves
+ self.assertRaises(tkinter.TclError, self.nb.insert, 2, tabs[0])
+ self.assertRaises(tkinter.TclError, self.nb.insert, -1, tabs[0])
+
+ # new tab
+ child3 = ttk.Label(self.root)
+ self.nb.insert(1, child3)
+ self.assertEqual(self.nb.tabs(), (tabs[0], str(child3), tabs[1]))
+ self.nb.forget(child3)
+ self.assertEqual(self.nb.tabs(), tabs)
+ self.nb.insert(self.child1, child3)
+ self.assertEqual(self.nb.tabs(), (str(child3), ) + tabs)
+ self.nb.forget(child3)
+ self.assertRaises(tkinter.TclError, self.nb.insert, 2, child3)
+ self.assertRaises(tkinter.TclError, self.nb.insert, -1, child3)
+
+ # bad inserts
+ self.assertRaises(tkinter.TclError, self.nb.insert, 'end', None)
+ self.assertRaises(tkinter.TclError, self.nb.insert, None, 0)
+ self.assertRaises(tkinter.TclError, self.nb.insert, None, None)
+
+ def test_select(self):
+ self.nb.pack()
+ self.nb.update()
+
+ success = []
+ tab_changed = []
+
+ self.child1.bind('<Unmap>', lambda evt: success.append(True))
+ self.nb.bind('<<NotebookTabChanged>>',
+ lambda evt: tab_changed.append(True))
+
+ self.assertEqual(self.nb.select(), str(self.child1))
+ self.nb.select(self.child2)
+ self.assertTrue(success)
+ self.assertEqual(self.nb.select(), str(self.child2))
+
+ self.nb.update()
+ self.assertTrue(tab_changed)
+
+ def test_tab(self):
+ self.assertRaises(tkinter.TclError, self.nb.tab, -1)
+ self.assertRaises(tkinter.TclError, self.nb.tab, 'notab')
+ self.assertRaises(tkinter.TclError, self.nb.tab, None)
+
+ self.assertIsInstance(self.nb.tab(self.child1), dict)
+ self.assertEqual(self.nb.tab(self.child1, text=None), 'a')
+ # newer form for querying a single option
+ self.assertEqual(self.nb.tab(self.child1, 'text'), 'a')
+ self.nb.tab(self.child1, text='abc')
+ self.assertEqual(self.nb.tab(self.child1, text=None), 'abc')
+ self.assertEqual(self.nb.tab(self.child1, 'text'), 'abc')
+
+ def test_configure_tabs(self):
+ self.assertEqual(len(self.nb.tabs()), 2)
+
+ self.nb.forget(self.child1)
+ self.nb.forget(self.child2)
+
+ self.assertEqual(self.nb.tabs(), ())
+
+ def test_traversal(self):
+ self.nb.pack()
+ self.nb.update()
+
+ self.nb.select(0)
+
+ focus_identify_as = 'focus' if sys.platform != 'darwin' else ''
+ self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
+ simulate_mouse_click(self.nb, 5, 5)
+ self.nb.focus_force()
+ self.nb.event_generate('<Control-Tab>')
+ self.assertEqual(self.nb.select(), str(self.child2))
+ self.nb.focus_force()
+ self.nb.event_generate('<Shift-Control-Tab>')
+ self.assertEqual(self.nb.select(), str(self.child1))
+ self.nb.focus_force()
+ self.nb.event_generate('<Shift-Control-Tab>')
+ self.assertEqual(self.nb.select(), str(self.child2))
+
+ self.nb.tab(self.child1, text='a', underline=0)
+ self.nb.tab(self.child2, text='e', underline=0)
+ self.nb.enable_traversal()
+ self.nb.focus_force()
+ self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
+ simulate_mouse_click(self.nb, 5, 5)
+ # on macOS Emacs-style keyboard shortcuts are region-dependent;
+ # let's use the regular arrow keys instead
+ if sys.platform == 'darwin':
+ begin = '<Left>'
+ end = '<Right>'
+ else:
+ begin = '<Alt-a>'
+ end = '<Alt-e>'
+ self.nb.event_generate(begin)
+ self.assertEqual(self.nb.select(), str(self.child1))
+ self.nb.event_generate(end)
+ self.assertEqual(self.nb.select(), str(self.child2))
+
+
+@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
+class SpinboxTest(EntryTest, unittest.TestCase):
+ OPTIONS = (
+ 'background', 'class', 'command', 'cursor', 'exportselection',
+ 'font', 'foreground', 'format', 'from', 'increment',
+ 'invalidcommand', 'justify', 'show', 'state', 'style',
+ 'takefocus', 'textvariable', 'to', 'validate', 'validatecommand',
+ 'values', 'width', 'wrap', 'xscrollcommand',
+ )
+ IDENTIFY_AS = 'Spinbox.field' if sys.platform == 'darwin' else 'textarea'
+
+ def setUp(self):
+ super().setUp()
+ self.spin = self.create()
+ self.spin.pack()
+
+ def create(self, **kwargs):
+ return ttk.Spinbox(self.root, **kwargs)
+
+ def _click_increment_arrow(self):
+ width = self.spin.winfo_width()
+ height = self.spin.winfo_height()
+ x = width - 5
+ y = height//2 - 5
+ self.assertRegex(self.spin.identify(x, y), r'.*uparrow\Z')
+ self.spin.event_generate('<ButtonPress-1>', x=x, y=y)
+ self.spin.event_generate('<ButtonRelease-1>', x=x, y=y)
+ self.spin.update_idletasks()
+
+ def _click_decrement_arrow(self):
+ width = self.spin.winfo_width()
+ height = self.spin.winfo_height()
+ x = width - 5
+ y = height//2 + 4
+ self.assertRegex(self.spin.identify(x, y), r'.*downarrow\Z')
+ self.spin.event_generate('<ButtonPress-1>', x=x, y=y)
+ self.spin.event_generate('<ButtonRelease-1>', x=x, y=y)
+ self.spin.update_idletasks()
+
+ def test_configure_command(self):
+ success = []
+
+ self.spin['command'] = lambda: success.append(True)
+ self.spin.update()
+ self._click_increment_arrow()
+ self.spin.update()
+ self.assertTrue(success)
+
+ self._click_decrement_arrow()
+ self.assertEqual(len(success), 2)
+
+ # testing postcommand removal
+ self.spin['command'] = ''
+ self.spin.update_idletasks()
+ self._click_increment_arrow()
+ self._click_decrement_arrow()
+ self.spin.update()
+ self.assertEqual(len(success), 2)
+
+ def test_configure_to(self):
+ self.spin['from'] = 0
+ self.spin['to'] = 5
+ self.spin.set(4)
+ self.spin.update()
+ self._click_increment_arrow() # 5
+
+ self.assertEqual(self.spin.get(), '5')
+
+ self._click_increment_arrow() # 5
+ self.assertEqual(self.spin.get(), '5')
+
+ def test_configure_from(self):
+ self.spin['from'] = 1
+ self.spin['to'] = 10
+ self.spin.set(2)
+ self.spin.update()
+ self._click_decrement_arrow() # 1
+ self.assertEqual(self.spin.get(), '1')
+ self._click_decrement_arrow() # 1
+ self.assertEqual(self.spin.get(), '1')
+
+ def test_configure_increment(self):
+ self.spin['from'] = 0
+ self.spin['to'] = 10
+ self.spin['increment'] = 4
+ self.spin.set(1)
+ self.spin.update()
+
+ self._click_increment_arrow() # 5
+ self.assertEqual(self.spin.get(), '5')
+ self.spin['increment'] = 2
+ self.spin.update()
+ self._click_decrement_arrow() # 3
+ self.assertEqual(self.spin.get(), '3')
+
+ def test_configure_format(self):
+ self.spin.set(1)
+ self.spin['format'] = '%10.3f'
+ self.spin.update()
+ self._click_increment_arrow()
+ value = self.spin.get()
+
+ self.assertEqual(len(value), 10)
+ self.assertEqual(value.index('.'), 6)
+
+ self.spin['format'] = ''
+ self.spin.update()
+ self._click_increment_arrow()
+ value = self.spin.get()
+ self.assertTrue('.' not in value)
+ self.assertEqual(len(value), 1)
+
+ def test_configure_wrap(self):
+ self.spin['to'] = 10
+ self.spin['from'] = 1
+ self.spin.set(1)
+ self.spin['wrap'] = True
+ self.spin.update()
+
+ self._click_decrement_arrow()
+ self.assertEqual(self.spin.get(), '10')
+
+ self._click_increment_arrow()
+ self.assertEqual(self.spin.get(), '1')
+
+ self.spin['wrap'] = False
+ self.spin.update()
+
+ self._click_decrement_arrow()
+ self.assertEqual(self.spin.get(), '1')
+
+ def test_configure_values(self):
+ self.assertEqual(self.spin['values'], '')
+ self.checkParam(self.spin, 'values', 'mon tue wed thur',
+ expected=('mon', 'tue', 'wed', 'thur'))
+ self.checkParam(self.spin, 'values', ('mon', 'tue', 'wed', 'thur'))
+ self.checkParam(self.spin, 'values', (42, 3.14, '', 'any string'))
+ self.checkParam(self.spin, 'values', '')
+
+ self.spin['values'] = ['a', 1, 'c']
+
+ # test incrementing / decrementing values
+ self.spin.set('a')
+ self.spin.update()
+ self._click_increment_arrow()
+ self.assertEqual(self.spin.get(), '1')
+
+ self._click_decrement_arrow()
+ self.assertEqual(self.spin.get(), 'a')
+
+ # testing values with empty string set through configure
+ self.spin.configure(values=[1, '', 2])
+ self.assertEqual(self.spin['values'],
+ ('1', '', '2') if self.wantobjects else
+ '1 {} 2')
+
+ # testing values with spaces
+ self.spin['values'] = ['a b', 'a\tb', 'a\nb']
+ self.assertEqual(self.spin['values'],
+ ('a b', 'a\tb', 'a\nb') if self.wantobjects else
+ '{a b} {a\tb} {a\nb}')
+
+ # testing values with special characters
+ self.spin['values'] = [r'a\tb', '"a"', '} {']
+ self.assertEqual(self.spin['values'],
+ (r'a\tb', '"a"', '} {') if self.wantobjects else
+ r'a\\tb {"a"} \}\ \{')
+
+ # testing creating spinbox with empty string in values
+ spin2 = ttk.Spinbox(self.root, values=[1, 2, ''])
+ self.assertEqual(spin2['values'],
+ ('1', '2', '') if self.wantobjects else '1 2 {}')
+ spin2.destroy()
+
+
+@add_standard_options(StandardTtkOptionsTests)
+class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'class', 'columns', 'cursor', 'displaycolumns',
+ 'height', 'padding', 'selectmode', 'show',
+ 'style', 'takefocus', 'xscrollcommand', 'yscrollcommand',
+ )
+
+ def setUp(self):
+ super().setUp()
+ self.tv = self.create(padding=0)
+
+ def create(self, **kwargs):
+ return ttk.Treeview(self.root, **kwargs)
+
+ def test_configure_columns(self):
+ widget = self.create()
+ self.checkParam(widget, 'columns', 'a b c',
+ expected=('a', 'b', 'c'))
+ self.checkParam(widget, 'columns', ('a', 'b', 'c'))
+ self.checkParam(widget, 'columns', '')
+
+ def test_configure_displaycolumns(self):
+ widget = self.create()
+ widget['columns'] = ('a', 'b', 'c')
+ self.checkParam(widget, 'displaycolumns', 'b a c',
+ expected=('b', 'a', 'c'))
+ self.checkParam(widget, 'displaycolumns', ('b', 'a', 'c'))
+ self.checkParam(widget, 'displaycolumns', '#all',
+ expected=('#all',))
+ self.checkParam(widget, 'displaycolumns', (2, 1, 0))
+ self.checkInvalidParam(widget, 'displaycolumns', ('a', 'b', 'd'),
+ errmsg='Invalid column index d')
+ self.checkInvalidParam(widget, 'displaycolumns', (1, 2, 3),
+ errmsg='Column index 3 out of bounds')
+ self.checkInvalidParam(widget, 'displaycolumns', (1, -2),
+ errmsg='Column index -2 out of bounds')
+
+ def test_configure_height(self):
+ widget = self.create()
+ self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', conv=False)
+ self.checkPixelsParam(widget, 'height', 101.2, 102.6, conv=False)
+
+ def test_configure_selectmode(self):
+ widget = self.create()
+ self.checkEnumParam(widget, 'selectmode',
+ 'none', 'browse', 'extended')
+
+ def test_configure_show(self):
+ widget = self.create()
+ self.checkParam(widget, 'show', 'tree headings',
+ expected=('tree', 'headings'))
+ self.checkParam(widget, 'show', ('tree', 'headings'))
+ self.checkParam(widget, 'show', ('headings', 'tree'))
+ self.checkParam(widget, 'show', 'tree', expected=('tree',))
+ self.checkParam(widget, 'show', 'headings', expected=('headings',))
+
+ def test_bbox(self):
+ self.tv.pack()
+ self.assertEqual(self.tv.bbox(''), '')
+ self.tv.update()
+
+ item_id = self.tv.insert('', 'end')
+ children = self.tv.get_children()
+ self.assertTrue(children)
+
+ bbox = self.tv.bbox(children[0])
+ self.assertIsBoundingBox(bbox)
+
+ # compare width in bboxes
+ self.tv['columns'] = ['test']
+ self.tv.column('test', width=50)
+ bbox_column0 = self.tv.bbox(children[0], 0)
+ root_width = self.tv.column('#0', width=None)
+ if not self.wantobjects:
+ root_width = int(root_width)
+ self.assertEqual(bbox_column0[0], bbox[0] + root_width)
+
+ # verify that bbox of a closed item is the empty string
+ child1 = self.tv.insert(item_id, 'end')
+ self.assertEqual(self.tv.bbox(child1), '')
+
+ def test_children(self):
+ # no children yet, should get an empty tuple
+ self.assertEqual(self.tv.get_children(), ())
+
+ item_id = self.tv.insert('', 'end')
+ self.assertIsInstance(self.tv.get_children(), tuple)
+ self.assertEqual(self.tv.get_children()[0], item_id)
+
+ # add item_id and child3 as children of child2
+ child2 = self.tv.insert('', 'end')
+ child3 = self.tv.insert('', 'end')
+ self.tv.set_children(child2, item_id, child3)
+ self.assertEqual(self.tv.get_children(child2), (item_id, child3))
+
+ # child3 has child2 as parent, thus trying to set child2 as a children
+ # of child3 should result in an error
+ self.assertRaises(tkinter.TclError,
+ self.tv.set_children, child3, child2)
+
+ # remove child2 children
+ self.tv.set_children(child2)
+ self.assertEqual(self.tv.get_children(child2), ())
+
+ # remove root's children
+ self.tv.set_children('')
+ self.assertEqual(self.tv.get_children(), ())
+
+ def test_column(self):
+ # return a dict with all options/values
+ self.assertIsInstance(self.tv.column('#0'), dict)
+ # return a single value of the given option
+ if self.wantobjects:
+ self.assertIsInstance(self.tv.column('#0', width=None), int)
+ # set a new value for an option
+ self.tv.column('#0', width=10)
+ # testing new way to get option value
+ self.assertEqual(self.tv.column('#0', 'width'),
+ 10 if self.wantobjects else '10')
+ self.assertEqual(self.tv.column('#0', width=None),
+ 10 if self.wantobjects else '10')
+ # check read-only option
+ self.assertRaises(tkinter.TclError, self.tv.column, '#0', id='X')
+
+ self.assertRaises(tkinter.TclError, self.tv.column, 'invalid')
+ invalid_kws = [
+ {'unknown_option': 'some value'}, {'stretch': 'wrong'},
+ {'anchor': 'wrong'}, {'width': 'wrong'}, {'minwidth': 'wrong'}
+ ]
+ for kw in invalid_kws:
+ self.assertRaises(tkinter.TclError, self.tv.column, '#0',
+ **kw)
+
+ def test_delete(self):
+ self.assertRaises(tkinter.TclError, self.tv.delete, '#0')
+
+ item_id = self.tv.insert('', 'end')
+ item2 = self.tv.insert(item_id, 'end')
+ self.assertEqual(self.tv.get_children(), (item_id, ))
+ self.assertEqual(self.tv.get_children(item_id), (item2, ))
+
+ self.tv.delete(item_id)
+ self.assertFalse(self.tv.get_children())
+
+ # reattach should fail
+ self.assertRaises(tkinter.TclError,
+ self.tv.reattach, item_id, '', 'end')
+
+ # test multiple item delete
+ item1 = self.tv.insert('', 'end')
+ item2 = self.tv.insert('', 'end')
+ self.assertEqual(self.tv.get_children(), (item1, item2))
+
+ self.tv.delete(item1, item2)
+ self.assertFalse(self.tv.get_children())
+
+ def test_detach_reattach(self):
+ item_id = self.tv.insert('', 'end')
+ item2 = self.tv.insert(item_id, 'end')
+
+ # calling detach without items is valid, although it does nothing
+ prev = self.tv.get_children()
+ self.tv.detach() # this should do nothing
+ self.assertEqual(prev, self.tv.get_children())
+
+ self.assertEqual(self.tv.get_children(), (item_id, ))
+ self.assertEqual(self.tv.get_children(item_id), (item2, ))
+
+ # detach item with children
+ self.tv.detach(item_id)
+ self.assertFalse(self.tv.get_children())
+
+ # reattach item with children
+ self.tv.reattach(item_id, '', 'end')
+ self.assertEqual(self.tv.get_children(), (item_id, ))
+ self.assertEqual(self.tv.get_children(item_id), (item2, ))
+
+ # move a children to the root
+ self.tv.move(item2, '', 'end')
+ self.assertEqual(self.tv.get_children(), (item_id, item2))
+ self.assertEqual(self.tv.get_children(item_id), ())
+
+ # bad values
+ self.assertRaises(tkinter.TclError,
+ self.tv.reattach, 'nonexistent', '', 'end')
+ self.assertRaises(tkinter.TclError,
+ self.tv.detach, 'nonexistent')
+ self.assertRaises(tkinter.TclError,
+ self.tv.reattach, item2, 'otherparent', 'end')
+ self.assertRaises(tkinter.TclError,
+ self.tv.reattach, item2, '', 'invalid')
+
+ # multiple detach
+ self.tv.detach(item_id, item2)
+ self.assertEqual(self.tv.get_children(), ())
+ self.assertEqual(self.tv.get_children(item_id), ())
+
+ def test_exists(self):
+ self.assertEqual(self.tv.exists('something'), False)
+ self.assertEqual(self.tv.exists(''), True)
+ self.assertEqual(self.tv.exists({}), False)
+
+ # the following will make a tk.call equivalent to
+ # tk.call(treeview, "exists") which should result in an error
+ # in the tcl interpreter since tk requires an item.
+ self.assertRaises(tkinter.TclError, self.tv.exists, None)
+
+ def test_focus(self):
+ # nothing is focused right now
+ self.assertEqual(self.tv.focus(), '')
+
+ item1 = self.tv.insert('', 'end')
+ self.tv.focus(item1)
+ self.assertEqual(self.tv.focus(), item1)
+
+ self.tv.delete(item1)
+ self.assertEqual(self.tv.focus(), '')
+
+ # try focusing inexistent item
+ self.assertRaises(tkinter.TclError, self.tv.focus, 'hi')
+
+ def test_heading(self):
+ # check a dict is returned
+ self.assertIsInstance(self.tv.heading('#0'), dict)
+
+ # check a value is returned
+ self.tv.heading('#0', text='hi')
+ self.assertEqual(self.tv.heading('#0', 'text'), 'hi')
+ self.assertEqual(self.tv.heading('#0', text=None), 'hi')
+
+ # invalid option
+ self.assertRaises(tkinter.TclError, self.tv.heading, '#0',
+ background=None)
+ # invalid value
+ self.assertRaises(tkinter.TclError, self.tv.heading, '#0',
+ anchor=1)
+
+ def test_heading_callback(self):
+ def simulate_heading_click(x, y):
+ if tcl_version >= (8, 6):
+ self.assertEqual(self.tv.identify_column(x), '#0')
+ self.assertEqual(self.tv.identify_region(x, y), 'heading')
+ simulate_mouse_click(self.tv, x, y)
+ self.tv.update()
+
+ success = [] # no success for now
+
+ self.tv.pack()
+ self.tv.heading('#0', command=lambda: success.append(True))
+ self.tv.column('#0', width=100)
+ self.tv.update()
+
+ # assuming that the coords (5, 5) fall into heading #0
+ simulate_heading_click(5, 5)
+ if not success:
+ self.fail("The command associated to the treeview heading wasn't "
+ "invoked.")
+
+ success = []
+ commands = self.tv.master._tclCommands
+ self.tv.heading('#0', command=str(self.tv.heading('#0', command=None)))
+ self.assertEqual(commands, self.tv.master._tclCommands)
+ simulate_heading_click(5, 5)
+ if not success:
+ self.fail("The command associated to the treeview heading wasn't "
+ "invoked.")
+
+ # XXX The following raises an error in a tcl interpreter, but not in
+ # Python
+ #self.tv.heading('#0', command='I dont exist')
+ #simulate_heading_click(5, 5)
+
+ def test_index(self):
+ # item 'what' doesn't exist
+ self.assertRaises(tkinter.TclError, self.tv.index, 'what')
+
+ self.assertEqual(self.tv.index(''), 0)
+
+ item1 = self.tv.insert('', 'end')
+ item2 = self.tv.insert('', 'end')
+ c1 = self.tv.insert(item1, 'end')
+ c2 = self.tv.insert(item1, 'end')
+ self.assertEqual(self.tv.index(item1), 0)
+ self.assertEqual(self.tv.index(c1), 0)
+ self.assertEqual(self.tv.index(c2), 1)
+ self.assertEqual(self.tv.index(item2), 1)
+
+ self.tv.move(item2, '', 0)
+ self.assertEqual(self.tv.index(item2), 0)
+ self.assertEqual(self.tv.index(item1), 1)
+
+ # check that index still works even after its parent and siblings
+ # have been detached
+ self.tv.detach(item1)
+ self.assertEqual(self.tv.index(c2), 1)
+ self.tv.detach(c1)
+ self.assertEqual(self.tv.index(c2), 0)
+
+ # but it fails after item has been deleted
+ self.tv.delete(item1)
+ self.assertRaises(tkinter.TclError, self.tv.index, c2)
+
+ def test_insert_item(self):
+ # parent 'none' doesn't exist
+ self.assertRaises(tkinter.TclError, self.tv.insert, 'none', 'end')
+
+ # open values
+ self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end',
+ open='')
+ self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end',
+ open='please')
+ self.assertFalse(self.tv.delete(self.tv.insert('', 'end', open=True)))
+ self.assertFalse(self.tv.delete(self.tv.insert('', 'end', open=False)))
+
+ # invalid index
+ self.assertRaises(tkinter.TclError, self.tv.insert, '', 'middle')
+
+ # trying to duplicate item id is invalid
+ itemid = self.tv.insert('', 'end', 'first-item')
+ self.assertEqual(itemid, 'first-item')
+ self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end',
+ 'first-item')
+ self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end',
+ MockTclObj('first-item'))
+
+ # unicode values
+ value = '\xe1ba'
+ item = self.tv.insert('', 'end', values=(value, ))
+ self.assertEqual(self.tv.item(item, 'values'),
+ (value,) if self.wantobjects else value)
+ self.assertEqual(self.tv.item(item, values=None),
+ (value,) if self.wantobjects else value)
+
+ self.tv.item(item, values=self.root.splitlist(self.tv.item(item, values=None)))
+ self.assertEqual(self.tv.item(item, values=None),
+ (value,) if self.wantobjects else value)
+
+ self.assertIsInstance(self.tv.item(item), dict)
+
+ # erase item values
+ self.tv.item(item, values='')
+ self.assertFalse(self.tv.item(item, values=None))
+
+ # item tags
+ item = self.tv.insert('', 'end', tags=[1, 2, value])
+ self.assertEqual(self.tv.item(item, tags=None),
+ ('1', '2', value) if self.wantobjects else
+ '1 2 %s' % value)
+ self.tv.item(item, tags=[])
+ self.assertFalse(self.tv.item(item, tags=None))
+ self.tv.item(item, tags=(1, 2))
+ self.assertEqual(self.tv.item(item, tags=None),
+ ('1', '2') if self.wantobjects else '1 2')
+
+ # values with spaces
+ item = self.tv.insert('', 'end', values=('a b c',
+ '%s %s' % (value, value)))
+ self.assertEqual(self.tv.item(item, values=None),
+ ('a b c', '%s %s' % (value, value)) if self.wantobjects else
+ '{a b c} {%s %s}' % (value, value))
+
+ # text
+ self.assertEqual(self.tv.item(
+ self.tv.insert('', 'end', text="Label here"), text=None),
+ "Label here")
+ self.assertEqual(self.tv.item(
+ self.tv.insert('', 'end', text=value), text=None),
+ value)
+
+ # test for values which are not None
+ itemid = self.tv.insert('', 'end', 0)
+ self.assertEqual(itemid, '0')
+ itemid = self.tv.insert('', 'end', 0.0)
+ self.assertEqual(itemid, '0.0')
+ # this is because False resolves to 0 and element with 0 iid is already present
+ self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', False)
+ self.assertRaises(tkinter.TclError, self.tv.insert, '', 'end', '')
+
+ def test_selection(self):
+ self.assertRaises(TypeError, self.tv.selection, 'spam')
+ # item 'none' doesn't exist
+ self.assertRaises(tkinter.TclError, self.tv.selection_set, 'none')
+ self.assertRaises(tkinter.TclError, self.tv.selection_add, 'none')
+ self.assertRaises(tkinter.TclError, self.tv.selection_remove, 'none')
+ self.assertRaises(tkinter.TclError, self.tv.selection_toggle, 'none')
+
+ item1 = self.tv.insert('', 'end')
+ item2 = self.tv.insert('', 'end')
+ c1 = self.tv.insert(item1, 'end')
+ c2 = self.tv.insert(item1, 'end')
+ c3 = self.tv.insert(item1, 'end')
+ self.assertEqual(self.tv.selection(), ())
+
+ self.tv.selection_set(c1, item2)
+ self.assertEqual(self.tv.selection(), (c1, item2))
+ self.tv.selection_set(c2)
+ self.assertEqual(self.tv.selection(), (c2,))
+
+ self.tv.selection_add(c1, item2)
+ self.assertEqual(self.tv.selection(), (c1, c2, item2))
+ self.tv.selection_add(item1)
+ self.assertEqual(self.tv.selection(), (item1, c1, c2, item2))
+ self.tv.selection_add()
+ self.assertEqual(self.tv.selection(), (item1, c1, c2, item2))
+
+ self.tv.selection_remove(item1, c3)
+ self.assertEqual(self.tv.selection(), (c1, c2, item2))
+ self.tv.selection_remove(c2)
+ self.assertEqual(self.tv.selection(), (c1, item2))
+ self.tv.selection_remove()
+ self.assertEqual(self.tv.selection(), (c1, item2))
+
+ self.tv.selection_toggle(c1, c3)
+ self.assertEqual(self.tv.selection(), (c3, item2))
+ self.tv.selection_toggle(item2)
+ self.assertEqual(self.tv.selection(), (c3,))
+ self.tv.selection_toggle()
+ self.assertEqual(self.tv.selection(), (c3,))
+
+ self.tv.insert('', 'end', id='with spaces')
+ self.tv.selection_set('with spaces')
+ self.assertEqual(self.tv.selection(), ('with spaces',))
+
+ self.tv.insert('', 'end', id='{brace')
+ self.tv.selection_set('{brace')
+ self.assertEqual(self.tv.selection(), ('{brace',))
+
+ self.tv.insert('', 'end', id='unicode\u20ac')
+ self.tv.selection_set('unicode\u20ac')
+ self.assertEqual(self.tv.selection(), ('unicode\u20ac',))
+
+ self.tv.insert('', 'end', id=b'bytes\xe2\x82\xac')
+ self.tv.selection_set(b'bytes\xe2\x82\xac')
+ self.assertEqual(self.tv.selection(), ('bytes\xe2\x82\xac',))
+
+ self.tv.selection_set()
+ self.assertEqual(self.tv.selection(), ())
+
+ # Old interface
+ self.tv.selection_set((c1, item2))
+ self.assertEqual(self.tv.selection(), (c1, item2))
+ self.tv.selection_add((c1, item1))
+ self.assertEqual(self.tv.selection(), (item1, c1, item2))
+ self.tv.selection_remove((item1, c3))
+ self.assertEqual(self.tv.selection(), (c1, item2))
+ self.tv.selection_toggle((c1, c3))
+ self.assertEqual(self.tv.selection(), (c3, item2))
+
+ def test_set(self):
+ self.tv['columns'] = ['A', 'B']
+ item = self.tv.insert('', 'end', values=['a', 'b'])
+ self.assertEqual(self.tv.set(item), {'A': 'a', 'B': 'b'})
+
+ self.tv.set(item, 'B', 'a')
+ self.assertEqual(self.tv.item(item, values=None),
+ ('a', 'a') if self.wantobjects else 'a a')
+
+ self.tv['columns'] = ['B']
+ self.assertEqual(self.tv.set(item), {'B': 'a'})
+
+ self.tv.set(item, 'B', 'b')
+ self.assertEqual(self.tv.set(item, column='B'), 'b')
+ self.assertEqual(self.tv.item(item, values=None),
+ ('b', 'a') if self.wantobjects else 'b a')
+
+ self.tv.set(item, 'B', 123)
+ self.assertEqual(self.tv.set(item, 'B'),
+ 123 if self.wantobjects else '123')
+ self.assertEqual(self.tv.item(item, values=None),
+ (123, 'a') if self.wantobjects else '123 a')
+ self.assertEqual(self.tv.set(item),
+ {'B': 123} if self.wantobjects else {'B': '123'})
+
+ # inexistent column
+ self.assertRaises(tkinter.TclError, self.tv.set, item, 'A')
+ self.assertRaises(tkinter.TclError, self.tv.set, item, 'A', 'b')
+
+ # inexistent item
+ self.assertRaises(tkinter.TclError, self.tv.set, 'notme')
+
+ def test_tag_bind(self):
+ events = []
+ item1 = self.tv.insert('', 'end', tags=['call'])
+ item2 = self.tv.insert('', 'end', tags=['call'])
+ self.tv.tag_bind('call', '<ButtonPress-1>',
+ lambda evt: events.append(1))
+ self.tv.tag_bind('call', '<ButtonRelease-1>',
+ lambda evt: events.append(2))
+
+ self.tv.pack()
+ self.tv.update()
+
+ pos_y = set()
+ found = set()
+ for i in range(0, 100, 10):
+ if len(found) == 2: # item1 and item2 already found
+ break
+ item_id = self.tv.identify_row(i)
+ if item_id and item_id not in found:
+ pos_y.add(i)
+ found.add(item_id)
+
+ self.assertEqual(len(pos_y), 2) # item1 and item2 y pos
+ for y in pos_y:
+ simulate_mouse_click(self.tv, 0, y)
+
+ # by now there should be 4 things in the events list, since each
+ # item had a bind for two events that were simulated above
+ self.assertEqual(len(events), 4)
+ for evt in zip(events[::2], events[1::2]):
+ self.assertEqual(evt, (1, 2))
+
+ def test_tag_configure(self):
+ # Just testing parameter passing for now
+ self.assertRaises(TypeError, self.tv.tag_configure)
+ self.assertRaises(tkinter.TclError, self.tv.tag_configure,
+ 'test', sky='blue')
+ self.tv.tag_configure('test', foreground='blue')
+ self.assertEqual(str(self.tv.tag_configure('test', 'foreground')),
+ 'blue')
+ self.assertEqual(str(self.tv.tag_configure('test', foreground=None)),
+ 'blue')
+ self.assertIsInstance(self.tv.tag_configure('test'), dict)
+
+ def test_tag_has(self):
+ item1 = self.tv.insert('', 'end', text='Item 1', tags=['tag1'])
+ item2 = self.tv.insert('', 'end', text='Item 2', tags=['tag2'])
+ self.assertRaises(TypeError, self.tv.tag_has)
+ self.assertRaises(TclError, self.tv.tag_has, 'tag1', 'non-existing')
+ self.assertTrue(self.tv.tag_has('tag1', item1))
+ self.assertFalse(self.tv.tag_has('tag1', item2))
+ self.assertFalse(self.tv.tag_has('tag2', item1))
+ self.assertTrue(self.tv.tag_has('tag2', item2))
+ self.assertFalse(self.tv.tag_has('tag3', item1))
+ self.assertFalse(self.tv.tag_has('tag3', item2))
+ self.assertEqual(self.tv.tag_has('tag1'), (item1,))
+ self.assertEqual(self.tv.tag_has('tag2'), (item2,))
+ self.assertEqual(self.tv.tag_has('tag3'), ())
+
+
+@add_standard_options(StandardTtkOptionsTests)
+class SeparatorTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'class', 'cursor', 'orient', 'style', 'takefocus',
+ # 'state'?
+ )
+ default_orient = 'horizontal'
+
+ def create(self, **kwargs):
+ return ttk.Separator(self.root, **kwargs)
+
+
+@add_standard_options(StandardTtkOptionsTests)
+class SizegripTest(AbstractWidgetTest, unittest.TestCase):
+ OPTIONS = (
+ 'class', 'cursor', 'style', 'takefocus',
+ # 'state'?
+ )
+
+ def create(self, **kwargs):
+ return ttk.Sizegrip(self.root, **kwargs)
+
+
+class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
+
+ def test_frame(self):
+ self._test_widget(ttk.Frame)
+
+ def test_label(self):
+ self._test_widget(ttk.Label)
+
+
+tests_gui = (
+ ButtonTest, CheckbuttonTest, ComboboxTest, EntryTest,
+ FrameTest, LabelFrameTest, LabelTest, MenubuttonTest,
+ NotebookTest, PanedWindowTest, ProgressbarTest,
+ RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest,
+ SizegripTest, SpinboxTest, TreeviewTest, WidgetTest, DefaultRootTest,
+ )
+
+if __name__ == "__main__":
+ unittest.main()