support.py 4.3 KB
Newer Older
1
import sys
2
import tkinter
3 4
import unittest

5
_tk_unavailable = None
6 7 8

def check_tk_availability():
    """Check that Tk is installed and available."""
9
    global _tk_unavailable
10

11 12 13 14 15 16 17 18 19 20 21
    if _tk_unavailable is None:
        _tk_unavailable = False
        if sys.platform == 'darwin':
            # The Aqua Tk implementations on OS X can abort the process if
            # being called in an environment where a window server connection
            # cannot be made, for instance when invoked by a buildbot or ssh
            # process not running under the same user id as the current console
            # user.  To avoid that, raise an exception if the window manager
            # connection is not available.
            from ctypes import cdll, c_int, pointer, Structure
            from ctypes.util import find_library
22

23
            app_services = cdll.LoadLibrary(find_library("ApplicationServices"))
24

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
            if app_services.CGMainDisplayID() == 0:
                _tk_unavailable = "cannot run without OS X window manager"
            else:
                class ProcessSerialNumber(Structure):
                    _fields_ = [("highLongOfPSN", c_int),
                                ("lowLongOfPSN", c_int)]
                psn = ProcessSerialNumber()
                psn_p = pointer(psn)
                if (  (app_services.GetCurrentProcess(psn_p) < 0) or
                      (app_services.SetFrontProcess(psn_p) < 0) ):
                    _tk_unavailable = "cannot run without OS X gui process"
        else:   # not OS X
            import tkinter
            try:
                tkinter.Button()
            except tkinter.TclError as msg:
                # assuming tk is not available
                _tk_unavailable = "tk not available: %s" % msg

    if _tk_unavailable:
        raise unittest.SkipTest(_tk_unavailable)
46
    return
47 48

def get_tk_root():
49
    check_tk_availability()     # raise exception if tk unavailable
50 51 52 53 54 55 56 57 58 59 60 61 62 63
    try:
        root = tkinter._default_root
    except AttributeError:
        # it is possible to disable default root in Tkinter, although
        # I haven't seen people doing it (but apparently someone did it
        # here).
        root = None

    if root is None:
        # create a new master only if there isn't one already
        root = tkinter.Tk()

    return root

64 65 66 67 68 69 70 71
def root_deiconify():
    root = get_tk_root()
    root.deiconify()

def root_withdraw():
    root = get_tk_root()
    root.withdraw()

72 73 74 75 76 77 78 79

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)
80 81 82 83 84 85 86 87 88


import _tkinter
tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.')))

def requires_tcl(*version):
    return unittest.skipUnless(tcl_version >= version,
            'requires Tcl version >= ' + '.'.join(map(str, version)))

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
_tk_patchlevel = None
def get_tk_patchlevel():
    global _tk_patchlevel
    if _tk_patchlevel is None:
        tcl = tkinter.Tcl()
        patchlevel = []
        for x in tcl.call('info', 'patchlevel').split('.'):
            try:
                x = int(x, 10)
            except ValueError:
                x = -1
            patchlevel.append(x)
        _tk_patchlevel = tuple(patchlevel)
    return _tk_patchlevel

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
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