1import functools
2import re
3import tkinter
4import unittest
5
6class AbstractTkTest:
7
8    @classmethod
9    def setUpClass(cls):
10        cls._old_support_default_root = tkinter._support_default_root
11        destroy_default_root()
12        tkinter.NoDefaultRoot()
13        cls.root = tkinter.Tk()
14        cls.wantobjects = cls.root.wantobjects()
15        # De-maximize main window.
16        # Some window managers can maximize new windows.
17        cls.root.wm_state('normal')
18        try:
19            cls.root.wm_attributes('-zoomed', False)
20        except tkinter.TclError:
21            pass
22
23    @classmethod
24    def tearDownClass(cls):
25        cls.root.update_idletasks()
26        cls.root.destroy()
27        del cls.root
28        tkinter._default_root = None
29        tkinter._support_default_root = cls._old_support_default_root
30
31    def setUp(self):
32        self.root.deiconify()
33
34    def tearDown(self):
35        for w in self.root.winfo_children():
36            w.destroy()
37        self.root.withdraw()
38
39
40class AbstractDefaultRootTest:
41
42    def setUp(self):
43        self._old_support_default_root = tkinter._support_default_root
44        destroy_default_root()
45        tkinter._support_default_root = True
46        self.wantobjects = tkinter.wantobjects
47
48    def tearDown(self):
49        destroy_default_root()
50        tkinter._default_root = None
51        tkinter._support_default_root = self._old_support_default_root
52
53    def _test_widget(self, constructor):
54        # no master passing
55        x = constructor()
56        self.assertIsNotNone(tkinter._default_root)
57        self.assertIs(x.master, tkinter._default_root)
58        self.assertIs(x.tk, tkinter._default_root.tk)
59        x.destroy()
60        destroy_default_root()
61        tkinter.NoDefaultRoot()
62        self.assertRaises(RuntimeError, constructor)
63        self.assertFalse(hasattr(tkinter, '_default_root'))
64
65
66def destroy_default_root():
67    if getattr(tkinter, '_default_root', None):
68        tkinter._default_root.update_idletasks()
69        tkinter._default_root.destroy()
70        tkinter._default_root = None
71
72def simulate_mouse_click(widget, x, y):
73    """Generate proper events to click at the x, y position (tries to act
74    like an X server)."""
75    widget.event_generate('<Enter>', x=0, y=0)
76    widget.event_generate('<Motion>', x=x, y=y)
77    widget.event_generate('<ButtonPress-1>', x=x, y=y)
78    widget.event_generate('<ButtonRelease-1>', x=x, y=y)
79
80
81import _tkinter
82tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.')))
83
84def requires_tcl(*version):
85    if len(version) <= 2:
86        return unittest.skipUnless(tcl_version >= version,
87            'requires Tcl version >= ' + '.'.join(map(str, version)))
88
89    def deco(test):
90        @functools.wraps(test)
91        def newtest(self):
92            if get_tk_patchlevel() < version:
93                self.skipTest('requires Tcl version >= ' +
94                                '.'.join(map(str, version)))
95            test(self)
96        return newtest
97    return deco
98
99_tk_patchlevel = None
100def get_tk_patchlevel():
101    global _tk_patchlevel
102    if _tk_patchlevel is None:
103        tcl = tkinter.Tcl()
104        _tk_patchlevel = tcl.info_patchlevel()
105    return _tk_patchlevel
106
107units = {
108    'c': 72 / 2.54,     # centimeters
109    'i': 72,            # inches
110    'm': 72 / 25.4,     # millimeters
111    'p': 1,             # points
112}
113
114def pixels_conv(value):
115    return float(value[:-1]) * units[value[-1:]]
116
117def tcl_obj_eq(actual, expected):
118    if actual == expected:
119        return True
120    if isinstance(actual, _tkinter.Tcl_Obj):
121        if isinstance(expected, str):
122            return str(actual) == expected
123    if isinstance(actual, tuple):
124        if isinstance(expected, tuple):
125            return (len(actual) == len(expected) and
126                    all(tcl_obj_eq(act, exp)
127                        for act, exp in zip(actual, expected)))
128    return False
129
130def widget_eq(actual, expected):
131    if actual == expected:
132        return True
133    if isinstance(actual, (str, tkinter.Widget)):
134        if isinstance(expected, (str, tkinter.Widget)):
135            return str(actual) == str(expected)
136    return False
137