• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
39def destroy_default_root():
40    if getattr(tkinter, '_default_root', None):
41        tkinter._default_root.update_idletasks()
42        tkinter._default_root.destroy()
43        tkinter._default_root = None
44
45def simulate_mouse_click(widget, x, y):
46    """Generate proper events to click at the x, y position (tries to act
47    like an X server)."""
48    widget.event_generate('<Enter>', x=0, y=0)
49    widget.event_generate('<Motion>', x=x, y=y)
50    widget.event_generate('<ButtonPress-1>', x=x, y=y)
51    widget.event_generate('<ButtonRelease-1>', x=x, y=y)
52
53
54import _tkinter
55tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.')))
56
57def requires_tcl(*version):
58    if len(version) <= 2:
59        return unittest.skipUnless(tcl_version >= version,
60            'requires Tcl version >= ' + '.'.join(map(str, version)))
61
62    def deco(test):
63        @functools.wraps(test)
64        def newtest(self):
65            if get_tk_patchlevel() < version:
66                self.skipTest('requires Tcl version >= ' +
67                                '.'.join(map(str, version)))
68            test(self)
69        return newtest
70    return deco
71
72_tk_patchlevel = None
73def get_tk_patchlevel():
74    global _tk_patchlevel
75    if _tk_patchlevel is None:
76        tcl = tkinter.Tcl()
77        patchlevel = tcl.call('info', 'patchlevel')
78        m = re.fullmatch(r'(\d+)\.(\d+)([ab.])(\d+)', patchlevel)
79        major, minor, releaselevel, serial = m.groups()
80        major, minor, serial = int(major), int(minor), int(serial)
81        releaselevel = {'a': 'alpha', 'b': 'beta', '.': 'final'}[releaselevel]
82        if releaselevel == 'final':
83            _tk_patchlevel = major, minor, serial, releaselevel, 0
84        else:
85            _tk_patchlevel = major, minor, 0, releaselevel, serial
86    return _tk_patchlevel
87
88units = {
89    'c': 72 / 2.54,     # centimeters
90    'i': 72,            # inches
91    'm': 72 / 25.4,     # millimeters
92    'p': 1,             # points
93}
94
95def pixels_conv(value):
96    return float(value[:-1]) * units[value[-1:]]
97
98def tcl_obj_eq(actual, expected):
99    if actual == expected:
100        return True
101    if isinstance(actual, _tkinter.Tcl_Obj):
102        if isinstance(expected, str):
103            return str(actual) == expected
104    if isinstance(actual, tuple):
105        if isinstance(expected, tuple):
106            return (len(actual) == len(expected) and
107                    all(tcl_obj_eq(act, exp)
108                        for act, exp in zip(actual, expected)))
109    return False
110
111def widget_eq(actual, expected):
112    if actual == expected:
113        return True
114    if isinstance(actual, (str, tkinter.Widget)):
115        if isinstance(expected, (str, tkinter.Widget)):
116            return str(actual) == str(expected)
117    return False
118