import collections.abc import functools import os import platform import sys import textwrap import unittest import weakref import tkinter from tkinter import TclError import enum from test import support from test.support import os_helper from test.support.script_helper import assert_python_ok from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import (AbstractTkTest, AbstractDefaultRootTest, requires_tk, get_tk_patchlevel, tcl_version) 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), '') def test_generated_names(self): class Button2(tkinter.Button): pass t = tkinter.Toplevel(self.root) f = tkinter.Frame(t) f2 = tkinter.Frame(t) self.assertNotEqual(str(f), str(f2)) b = tkinter.Button(f2) b2 = Button2(f2) for name in str(b).split('.') + str(b2).split('.'): self.assertFalse(name.isidentifier(), msg=repr(name)) b3 = tkinter.Button(f2) b4 = Button2(f2) self.assertEqual(len({str(b), str(b2), str(b3), str(b4)}), 4) def test_dealloc_in_wrong_thread(self): # gh-83274: deallocating the interpreter in the wrong thread must not # crash. script = textwrap.dedent(""" import threading import tkinter root = tkinter.Tk() root.destroy() # Let another thread drop the last reference. ready = threading.Event() t = threading.Thread(target=lambda obj: ready.wait(), args=(root,)) t.start() del root ready.set() t.join() print('ok') """) rc, out, err = assert_python_ok('-c', script) self.assertEqual(out.strip(), b'ok') if not support.Py_GIL_DISABLED: # On the free-threaded build the interpreter may instead be # deallocated in its own thread (deferred reference counting), so # the warning is not necessarily emitted. The crucial guarantee -- # no crash -- is already checked by assert_python_ok() above. self.assertIn(b'RuntimeWarning', err) self.assertIn(b'gh-83274', err) @requires_tk(8, 6, 6) def test_tk_busy(self): root = self.root f = tkinter.Frame(root, name='myframe') f2 = tkinter.Frame(root) f.pack() f2.pack() b = tkinter.Button(f) b.pack() f.tk_busy_hold() with self.assertRaisesRegex(TclError, 'unknown option "-spam"'): f.tk_busy_configure(spam='eggs') with self.assertRaisesRegex(TclError, 'unknown option "-spam"'): f.tk_busy_cget('spam') with self.assertRaisesRegex(TclError, 'unknown option "-spam"'): f.tk_busy_configure('spam') self.assertIsInstance(f.tk_busy_configure(), dict) self.assertTrue(f.tk_busy_status()) self.assertFalse(root.tk_busy_status()) self.assertFalse(f2.tk_busy_status()) self.assertFalse(b.tk_busy_status()) self.assertIn(f, f.tk_busy_current()) self.assertIn(f, f.tk_busy_current('*.m?f*me')) self.assertNotIn(f, f.tk_busy_current('*spam')) f.tk_busy_forget() self.assertFalse(f.tk_busy_status()) self.assertFalse(f.tk_busy_current()) errmsg = r"can(no|')t find busy window.*" with self.assertRaisesRegex(TclError, errmsg): f.tk_busy_configure() with self.assertRaisesRegex(TclError, errmsg): f.tk_busy_forget() @requires_tk(8, 6, 6) def test_tk_busy_with_cursor(self): root = self.root if root._windowingsystem == 'aqua': self.skipTest('the cursor option is not supported on OSX/Aqua') f = tkinter.Frame(root, name='myframe') f.pack() f.tk_busy_hold(cursor='gumby') self.assertEqual(f.tk_busy_cget('cursor'), 'gumby') f.tk_busy_configure(cursor='heart') self.assertEqual(f.tk_busy_cget('cursor'), 'heart') self.assertEqual(f.tk_busy_configure()['cursor'][4], 'heart') self.assertEqual(f.tk_busy_configure('cursor')[4], 'heart') f.tk_busy_forget() errmsg = r"can(no|')t find busy window.*" with self.assertRaisesRegex(TclError, errmsg): f.tk_busy_cget('cursor') 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, *, end=0): nonlocal count count = start + step + end # 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) # Set up with callback with keyword args. count = 0 timer1 = root.after(0, callback, 42, step=11, end=1) root.update() # Process all pending events. self.assertEqual(count, 54) def test_after_idle(self): root = self.root def callback(start=0, step=1, *, end=0): nonlocal count count = start + step + end # 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) # Set up with callback with keyword args. count = 0 idle1 = root.after_idle(callback, 42, step=11, end=1) root.update() # Process all pending events. self.assertEqual(count, 54) 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_after_info(self): root = self.root # No events. self.assertEqual(root.after_info(), ()) # Add timer. timer = root.after(1, lambda: 'break') # With no parameter, it returns a tuple of the event handler ids. self.assertEqual(root.after_info(), (timer, )) root.after_cancel(timer) timer1 = root.after(5000, lambda: 'break') timer2 = root.after(5000, lambda: 'break') idle1 = root.after_idle(lambda: 'break') # Only contains new events and not 'timer'. self.assertEqual(root.after_info(), (idle1, timer2, timer1)) # With a parameter returns a tuple of (script, type). timer1_info = root.after_info(timer1) self.assertEqual(len(timer1_info), 2) self.assertEqual(timer1_info[1], 'timer') idle1_info = root.after_info(idle1) self.assertEqual(len(idle1_info), 2) self.assertEqual(idle1_info[1], 'idle') root.after_cancel(timer1) with self.assertRaises(tkinter.TclError): root.after_info(timer1) root.after_cancel(timer2) with self.assertRaises(tkinter.TclError): root.after_info(timer2) root.after_cancel(idle1) with self.assertRaises(tkinter.TclError): root.after_info(idle1) # No events. self.assertEqual(root.after_info(), ()) 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_getint(self): self.assertEqual(self.root.getint('42'), 42) self.assertEqual(self.root.getint(42), 42) self.assertEqual(self.root.getint('-5'), -5) self.assertRaises(ValueError, self.root.getint, 'spam') def test_getdouble(self): self.assertEqual(self.root.getdouble('3.5'), 3.5) self.assertEqual(self.root.getdouble(3), 3.0) self.assertRaises(ValueError, self.root.getdouble, 'spam') def test_getvar(self): self.root.setvar('test_var', 'hello') self.assertEqual(self.root.getvar('test_var'), 'hello') def test_register(self): result = [] def callback(): result.append(1) return 'spam' name = self.root.register(callback) self.assertIsInstance(name, str) self.assertEqual(self.root.tk.call(name), 'spam') self.assertEqual(result, [1]) self.root.deletecommand(name) self.assertRaises(TclError, self.root.tk.call, name) def test_createcommand_no_leak(self): # gh-80937: dropping the interpreter must release a command's callback, # even without an explicit deletecommand(). interp = tkinter.Tcl() callback = lambda: '' ref = weakref.ref(callback) interp.tk.createcommand('cb', callback) del callback, interp support.gc_collect() self.assertIsNone(ref()) def test_option(self): self.addCleanup(self.root.option_clear) self.root.option_add('*Button.background', 'red') b = tkinter.Button(self.root) self.assertEqual(b.option_get('background', 'Background'), 'red') self.assertEqual(b.option_get('foreground', 'Foreground'), '') self.root.option_clear() self.assertEqual(b.option_get('background', 'Background'), '') def test_option_readfile(self): self.addCleanup(self.root.option_clear) self.addCleanup(os_helper.unlink, os_helper.TESTFN) with open(os_helper.TESTFN, 'w') as f: f.write('*Button.background: red\n') self.root.option_readfile(os_helper.TESTFN) b = tkinter.Button(self.root) self.assertEqual(b.option_get('background', 'Background'), 'red') self.assertRaises(TclError, self.root.option_readfile, os_helper.TESTFN + '.nonexistent') self.assertRaises(TypeError, self.root.option_readfile) self.assertRaises(TypeError, self.root.option_readfile, 'a', 'b', 'c') def test_nametowidget(self): b = tkinter.Button(self.root, name='btn') self.assertIs(self.root.nametowidget('btn'), b) self.assertIs(self.root.nametowidget(str(b)), b) self.assertRaises(KeyError, self.root.nametowidget, '.nonexistent') def test_nametowidget_menu_clone(self): # A menu used as a menubar or cascade is cloned by Tk under an # auto-generated name (each path component is the original name # prefixed with one or more '#' clone markers). nametowidget() # maps such a name back to the original widget (gh-38464). menubar = tkinter.Menu(self.root) filemenu = tkinter.Menu(menubar, tearoff=0) menubar.add_cascade(label='File', menu=filemenu) submenu = tkinter.Menu(filemenu, tearoff=0) filemenu.add_cascade(label='More', menu=submenu) self.root['menu'] = menubar self.root.update_idletasks() originals = {menubar, filemenu, submenu} clones = [] def collect(parent): for name in self.root.tk.splitlist( self.root.tk.call('winfo', 'children', parent)): clones.append(name) collect(name) collect('.') # Every menu (originals and clones) resolves to an original widget. self.assertTrue(any('#' in name for name in clones)) for name in clones: self.assertIn(self.root.nametowidget(name), originals) def test_focus_methods(self): f = tkinter.Frame(self.root, width=150, height=100) f.pack() self.root.wait_visibility() # needed on Windows self.root.update_idletasks() f.focus_force() self.root.update() self.assertIs(self.root.focus_get(), f) self.assertIs(self.root.focus_displayof(), f) self.assertIs(f.focus_lastfor(), f) b = tkinter.Button(f) b.pack() self.root.update() b.focus_set() self.root.update() self.assertIs(self.root.focus_get(), b) def test_focus_methods_unresolvable(self): # The focus may be on a widget that tkinter did not create and so # cannot map to an instance (e.g. a torn-off menu). The focus # methods return None instead of raising KeyError (gh-88758). menu = tkinter.Menu(self.root, tearoff=1) menu.add_command(label='Hello') tearoff = self.root.tk.call('tk::TearOffMenu', str(menu), 0, 0) self.addCleanup(self.root.tk.call, 'destroy', tearoff) self.root.update() self.assertRaises(KeyError, self.root.nametowidget, tearoff) self.root.tk.call('focus', '-force', tearoff) self.root.update() self.assertIsNone(self.root.focus_get()) self.assertIsNone(self.root.focus_displayof()) def test_grab(self): f = tkinter.Frame(self.root) f.pack() self.root.wait_visibility() self.assertIsNone(self.root.grab_current()) self.assertIsNone(f.grab_status()) f.grab_set() self.assertEqual(f.grab_status(), 'local') self.assertIs(self.root.grab_current(), f) f.grab_release() self.assertIsNone(f.grab_status()) self.assertIsNone(self.root.grab_current()) def test_selection_own(self): self.root.selection_own() self.assertIs(self.root.selection_own_get(), self.root) f = tkinter.Frame(self.root) f.selection_own() self.assertIs(self.root.selection_own_get(), f) def test_event_add_delete_info(self): self.addCleanup(self.root.event_delete, '<>') self.root.event_add('<>', '', '') self.assertEqual(self.root.event_info('<>'), ('', '')) self.assertIn('<>', self.root.event_info()) self.root.event_delete('<>', '') self.assertEqual(self.root.event_info('<>'), ('',)) self.root.event_delete('<>') self.assertEqual(self.root.event_info('<>'), ()) self.assertRaises(TypeError, self.root.event_delete) def test_bell(self): self.root.bell() # No exception. self.root.bell(displayof=self.root) def test_tk_focusNext_focusPrev(self): f = tkinter.Frame(self.root) f.pack() entries = [tkinter.Entry(f) for _ in range(3)] for entry in entries: entry.pack() # tk_focusNext skips widgets that are not viewable. entries[-1].wait_visibility() self.assertIs(entries[0].tk_focusNext(), entries[1]) self.assertIs(entries[1].tk_focusNext(), entries[2]) self.assertIs(entries[2].tk_focusPrev(), entries[1]) self.assertIs(entries[1].tk_focusPrev(), entries[0]) self.assertRaises(TypeError, entries[0].tk_focusNext, 'x') self.assertRaises(TypeError, entries[0].tk_focusPrev, 'x') def test_tk_strictMotif(self): self.addCleanup(self.root.tk_strictMotif, False) self.assertIs(self.root.tk_strictMotif(), False) self.assertIs(self.root.tk_strictMotif(True), True) self.assertIs(self.root.tk_strictMotif(), True) self.assertIs(self.root.tk_strictMotif(False), False) self.assertRaises(TypeError, self.root.tk_strictMotif, 1, 2) def test_tk_bisque(self): # tk_bisque resets the color palette; use a separate root so that # the shared one is not affected. root = tkinter.Tk() self.addCleanup(root.destroy) root.tk_bisque() self.assertEqual(root['background'], '#ffe4c4') self.assertRaises(TypeError, root.tk_bisque, 'x') def test_wait_variable(self): var = tkinter.StringVar(self.root) self.assertEqual(self.root.waitvar, self.root.wait_variable) self.root.after(1, var.set, 'done') self.root.wait_variable(var) # Returns once the variable is set. self.assertEqual(var.get(), 'done') def test_wait_window(self): top = tkinter.Toplevel(self.root) self.root.after(1, top.destroy) self.root.wait_window(top) # Returns once the window is destroyed. self.assertFalse(top.winfo_exists()) def test_tk_focusFollowsMouse(self): self.root.tk_focusFollowsMouse() # No exception. def test_selection_handle(self): f = tkinter.Frame(self.root) def handler(offset, length): return 'PAYLOAD'[int(offset):int(offset) + int(length)] f.selection_handle(handler) f.selection_own() self.assertEqual(f.selection_get(), 'PAYLOAD') def test_grab_set_global(self): # A successful global grab directs all events on the display to this # application, so only the error paths are tested here. self.assertRaises(TypeError, self.root.grab_set_global, 'extra') with self.subTest('non-viewable window'): if self.root._windowingsystem != 'x11': # Grabbing a non-viewable window fails only on X11; elsewhere # it would actually grab the whole display. self.skipTest('only X11 fails the grab') f = tkinter.Frame(self.root) # not yet viewable self.assertRaisesRegex(TclError, 'grab failed', f.grab_set_global) def test_send(self): if self.root._windowingsystem != 'x11': self.skipTest('send is only supported on X11') self.assertRaisesRegex(TclError, 'no application named', self.root.send, 'no_such_interp_xyzzy', 'set x 1') 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), "") 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.assertStartsWith(str(vi), f'{vi.major}.{vi.minor}') def test_embedded_null(self): widget = tkinter.Entry(self.root) widget.insert(0, 'abc\0def') # ASCII-only widget.selection_range(0, 'end') self.assertEqual(widget.selection_get(), 'abc\x00def') widget.insert(0, '\u20ac\0') # non-ASCII widget.selection_range(0, 'end') self.assertEqual(widget.selection_get(), '\u20ac\0abc\x00def') def test_iterable_protocol(self): widget = tkinter.Entry(self.root) self.assertNotIsSubclass(tkinter.Entry, collections.abc.Iterable) self.assertNotIsSubclass(tkinter.Entry, collections.abc.Container) self.assertNotIsInstance(widget, collections.abc.Iterable) self.assertNotIsInstance(widget, collections.abc.Container) with self.assertRaisesRegex(TypeError, 'is not iterable'): iter(widget) with self.assertRaisesRegex(TypeError, 'is not a container or iterable'): widget in widget class TkTest(AbstractTkTest, unittest.TestCase): def test_readprofile(self): # gh-153333: profile scripts are decoded with their own coding cookie, # not the locale encoding. Two cookies so no locale can mask the bug. profiles = { '.RpClass.py': ('latin-1', "self._rp_latin1 = 'caf\xe9'"), '.rpbase.py': ('utf-8', "self._rp_utf8 = 'caf\xe9'"), } self.addCleanup(self.root.__dict__.pop, '_rp_latin1', None) self.addCleanup(self.root.__dict__.pop, '_rp_utf8', None) with (os_helper.temp_dir() as home, os_helper.EnvironmentVarGuard() as env): env['HOME'] = home for filename, (encoding, body) in profiles.items(): script = '# -*- coding: %s -*-\n%s\n' % (encoding, body) with open(os.path.join(home, filename), 'wb') as f: f.write(script.encode(encoding)) self.root.readprofile('rpbase', 'RpClass') self.assertEqual(self.root._rp_latin1, 'caf\xe9') self.assertEqual(self.root._rp_utf8, 'caf\xe9') def test_className(self): # The className argument sets the class of the root window. Tk # title-cases it: the first letter is upper-cased, the rest lower-cased. cases = [ ('fooBAR', 'Foobar'), ('éÉ', 'Éé'), # small and capital E WITH ACUTE ] if tcl_version >= (9, 0): # small and capital DESERET LETTER LONG I (a non-BMP script) cases.append(('\U00010428\U00010400', '\U00010400\U00010428')) for className, klass in cases: root = tkinter.Tk(className=className) try: self.assertEqual(root.winfo_class(), klass) finally: root.destroy() if tcl_version < (9, 0): # gh-126219: title-casing a non-BMP first letter crashed Tcl 8.x; # such a class name is now rejected. self.assertRaises(ValueError, tkinter.Tk, className='\U00010428') class WinfoTest(AbstractTkTest, unittest.TestCase): 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_winfo_pathname(self): t = tkinter.Toplevel(self.root) w = tkinter.Button(t) wid = w.winfo_id() self.assertIsInstance(wid, int) self.assertEqual(self.root.winfo_pathname(hex(wid)), str(w)) self.assertEqual(self.root.winfo_pathname(hex(wid), displayof=None), str(w)) self.assertEqual(self.root.winfo_pathname(hex(wid), displayof=t), str(w)) self.assertEqual(self.root.winfo_pathname(wid), str(w)) self.assertEqual(self.root.winfo_pathname(wid, displayof=None), str(w)) self.assertEqual(self.root.winfo_pathname(wid, displayof=t), str(w)) def test_winfo_class_name_parent(self): f = tkinter.Frame(self.root) b = tkinter.Button(f) self.assertEqual(f.winfo_class(), 'Frame') self.assertEqual(b.winfo_class(), 'Button') self.assertEqual(b.winfo_name(), str(b).rsplit('.', 1)[-1]) self.assertEqual(b.winfo_parent(), str(f)) self.assertEqual(f.winfo_parent(), str(self.root)) self.assertIs(f.winfo_toplevel(), self.root) t = tkinter.Toplevel(self.root) self.assertIs(tkinter.Button(t).winfo_toplevel(), t) self.assertEqual(self.root.nametowidget(b.winfo_parent()), f) def test_winfo_children(self): self.assertEqual(self.root.winfo_children(), []) f = tkinter.Frame(self.root) b = tkinter.Button(f) self.assertEqual(self.root.winfo_children(), [f]) self.assertEqual(f.winfo_children(), [b]) def test_winfo_visual_info(self): f = tkinter.Frame(self.root) self.assertIsInstance(f.winfo_depth(), int) self.assertIsInstance(f.winfo_cells(), int) self.assertIsInstance(f.winfo_visual(), str) self.assertIsInstance(f.winfo_visualid(), str) self.assertIsInstance(f.winfo_colormapfull(), bool) visuals = self.root.winfo_visualsavailable() self.assertIsInstance(visuals, list) for name, depth in visuals: self.assertIsInstance(name, str) self.assertIsInstance(depth, int) def test_winfo_viewable(self): f = tkinter.Frame(self.root) self.assertFalse(f.winfo_viewable()) f.pack() f.wait_visibility() self.root.update() self.assertTrue(f.winfo_viewable()) def test_winfo_atom(self): atom = self.root.winfo_atom('PRIMARY') self.assertIsInstance(atom, int) self.assertEqual(self.root.winfo_atomname(atom), 'PRIMARY') self.assertEqual( self.root.winfo_atomname(atom, displayof=self.root), 'PRIMARY') self.assertEqual( self.root.winfo_atom('PRIMARY', displayof=self.root), atom) self.assertRaisesRegex(TclError, 'no atom exists', self.root.winfo_atomname, 10 ** 9) def test_winfo_pointer(self): self.assertIsInstance(self.root.winfo_pointerx(), int) self.assertIsInstance(self.root.winfo_pointery(), int) xy = self.root.winfo_pointerxy() self.assertIsInstance(xy, tuple) self.assertEqual(len(xy), 2) self.assertTrue(all(isinstance(v, int) for v in xy)) def test_winfo_containing(self): self.root.update() # No window contains a point far off the screen. self.assertIsNone(self.root.winfo_containing(-10000, -10000)) self.assertIsNone( self.root.winfo_containing(-10000, -10000, displayof=self.root)) def test_winfo_fpixels(self): self.assertIsInstance(self.root.winfo_fpixels('1i'), float) self.assertAlmostEqual(self.root.winfo_fpixels('1i'), self.root.winfo_fpixels('72p')) # Tk < 9 reports 'bad screen distance "spam"', Tk 9 reports # 'expected screen distance ... but got "spam"'. self.assertRaisesRegex(TclError, r'(bad|expected) screen distance.*"spam"', self.root.winfo_fpixels, 'spam') def test_winfo_screen(self): for name in ('winfo_screenwidth', 'winfo_screenheight', 'winfo_screenmmwidth', 'winfo_screenmmheight', 'winfo_screencells', 'winfo_screendepth'): value = getattr(self.root, name)() self.assertIsInstance(value, int) self.assertGreater(value, 0) self.assertIsInstance(self.root.winfo_screenvisual(), str) self.assertIsInstance(self.root.winfo_screen(), str) self.assertIsInstance(self.root.winfo_server(), str) def test_winfo_vroot(self): for name in ('winfo_vrootwidth', 'winfo_vrootheight', 'winfo_vrootx', 'winfo_vrooty'): self.assertIsInstance(getattr(self.root, name)(), int) def test_winfo_interps(self): interps = self.root.winfo_interps() self.assertIsInstance(interps, tuple) # The registry of interpreters is only populated where "send" is # supported (i.e. X11), so do not require this interpreter's name. if self.root._windowingsystem == 'x11': self.assertIn(self.root.tk.call('tk', 'appname'), interps) self.assertEqual(self.root.winfo_interps(displayof=self.root), interps) class WmTest(AbstractTkTest, unittest.TestCase): def test_wm_attribute(self): w = self.root attributes = w.wm_attributes(return_python_dict=True) self.assertIsInstance(attributes, dict) attributes2 = w.wm_attributes() self.assertIsInstance(attributes2, tuple) self.assertEqual(attributes2[::2], tuple('-' + k for k in attributes)) self.assertEqual(attributes2[1::2], tuple(attributes.values())) # silently deprecated attributes3 = w.wm_attributes(None) if self.wantobjects: self.assertEqual(attributes3, attributes2) else: self.assertIsInstance(attributes3, str) for name in attributes: self.assertEqual(w.wm_attributes(name), attributes[name]) # silently deprecated for name in attributes: self.assertEqual(w.wm_attributes('-' + name), attributes[name]) self.assertIn('alpha', attributes) self.assertIn('fullscreen', attributes) self.assertIn('topmost', attributes) if w._windowingsystem == "win32": self.assertIn('disabled', attributes) self.assertIn('toolwindow', attributes) self.assertIn('transparentcolor', attributes) if w._windowingsystem == "aqua": self.assertIn('modified', attributes) self.assertIn('notify', attributes) self.assertIn('titlepath', attributes) self.assertIn('transparent', attributes) if w._windowingsystem == "x11": self.assertIn('type', attributes) self.assertIn('zoomed', attributes) w.wm_attributes(alpha=0.5) self.assertEqual(w.wm_attributes('alpha'), 0.5 if self.wantobjects else '0.5') w.wm_attributes(alpha=1.0) self.assertEqual(w.wm_attributes('alpha'), 1.0 if self.wantobjects else '1.0') # silently deprecated w.wm_attributes('-alpha', 0.5) self.assertEqual(w.wm_attributes('alpha'), 0.5 if self.wantobjects else '0.5') w.wm_attributes(alpha=1.0) self.assertEqual(w.wm_attributes('alpha'), 1.0 if self.wantobjects else '1.0') def test_wm_iconbitmap(self): t = tkinter.Toplevel(self.root) patchlevel = get_tk_patchlevel(t) if ( t._windowingsystem == 'aqua' and sys.platform == 'darwin' and platform.machine() == 'x86_64' and platform.mac_ver()[0].startswith('26.') ): # https://github.com/python/cpython/issues/146531 # Tk bug 4a2070f0d3a99aa412bc582d386d575ca2f37323 # Not fixed as of Tk 8.6.18 and 9.0.4. self.skipTest('wm iconbitmap hangs on macOS 26 Intel') self.assertEqual(t.wm_iconbitmap(), '') t.wm_iconbitmap('hourglass') bug = False if t._windowingsystem == 'aqua': # Tk bug 13ac26b35dc55f7c37f70b39d59d7ef3e63017c8. if patchlevel < (8, 6, 17) or (9, 0) <= patchlevel < (9, 0, 2): bug = True if not bug: self.assertEqual(t.wm_iconbitmap(), 'hourglass') self.assertEqual(self.root.wm_iconbitmap(), '') t.wm_iconbitmap('') self.assertEqual(t.wm_iconbitmap(), '') if t._windowingsystem == 'win32': t.wm_iconbitmap(default='hourglass') self.assertEqual(t.wm_iconbitmap(), 'hourglass') self.assertEqual(self.root.wm_iconbitmap(), '') t.wm_iconbitmap(default='') self.assertEqual(t.wm_iconbitmap(), '') t.destroy() def test_wm_iconphoto(self): t = tkinter.Toplevel(self.root) img = tkinter.PhotoImage(master=t, width=16, height=16) t.wm_iconphoto(False, img) # No exception. t.wm_iconphoto(True, img) self.assertRaises(tkinter.TclError, t.wm_iconphoto, False, 'spam') def test_wm_title(self): t = tkinter.Toplevel(self.root) t.title('Hello') self.assertEqual(t.title(), 'Hello') self.assertEqual(t.wm_title(), 'Hello') t.wm_title('Spam') self.assertEqual(t.title(), 'Spam') def test_wm_geometry(self): t = tkinter.Toplevel(self.root) t.geometry('200x100+10+20') t.update() self.assertRegex(t.geometry(), r'^200x100\+-?\d+\+-?\d+$') self.assertEqual(t.wm_geometry(), t.geometry()) def test_wm_minsize_maxsize(self): t = tkinter.Toplevel(self.root) # Use a width above the minimum enforced by some platforms (72 on Aqua). t.minsize(150, 100) self.assertEqual(t.minsize(), (150, 100)) t.maxsize(500, 600) self.assertEqual(t.maxsize(), (500, 600)) def test_wm_resizable(self): t = tkinter.Toplevel(self.root) t.resizable(False, True) self.assertEqual(t.resizable(), (0, 1)) self.assertRaisesRegex(TclError, 'expected boolean value', t.resizable, 'spam', True) def test_wm_aspect(self): t = tkinter.Toplevel(self.root) self.assertEqual(t.aspect(), None) t.aspect(1, 2, 3, 4) self.assertEqual(t.aspect(), (1, 2, 3, 4)) def test_wm_grid(self): t = tkinter.Toplevel(self.root) t.wm_grid(10, 10, 5, 5) self.assertEqual(t.wm_grid(), (10, 10, 5, 5)) def test_wm_positionfrom_sizefrom(self): # These set X11 size hints and may be no-ops on other platforms. t = tkinter.Toplevel(self.root) t.positionfrom('user') self.assertIn(t.positionfrom(), ('user', '')) t.sizefrom('program') self.assertIn(t.sizefrom(), ('program', '')) def test_wm_focusmodel(self): t = tkinter.Toplevel(self.root) self.assertEqual(t.focusmodel(), 'passive') t.focusmodel('active') self.assertEqual(t.focusmodel(), 'active') self.assertRaises(TclError, t.focusmodel, 'spam') def test_wm_iconname(self): # WM_ICON_NAME is an X11 property and may be a no-op elsewhere. t = tkinter.Toplevel(self.root) t.iconname('Icon') self.assertIn(t.iconname(), ('Icon', '')) def test_wm_iconposition(self): t = tkinter.Toplevel(self.root) t.wm_iconposition(3, 4) # An X11 hint; may be a no-op elsewhere. if t._windowingsystem == 'x11': self.assertEqual(t.wm_iconposition(), (3, 4)) def test_wm_iconmask_iconwindow(self): if self.root._windowingsystem != 'x11': self.skipTest('iconmask and iconwindow are X11-specific') t = tkinter.Toplevel(self.root) t.wm_iconmask('gray50') # No exception. icon = tkinter.Toplevel(self.root) t.wm_iconwindow(icon) self.assertEqual(str(t.wm_iconwindow()), str(icon)) def test_wm_colormapwindows(self): if self.root._windowingsystem != 'x11': self.skipTest('colormapwindows is X11-specific') t = tkinter.Toplevel(self.root) self.assertEqual(t.wm_colormapwindows(), []) f = tkinter.Frame(t) t.wm_colormapwindows(f) self.assertEqual([str(w) for w in t.wm_colormapwindows()], [str(f)]) def test_wm_manage_forget(self): f = tkinter.Frame(self.root) self.root.wm_manage(f) # Make the frame a top-level window. self.root.wm_forget(f) # Revert it; no exception either way. def test_wm_client_command(self): t = tkinter.Toplevel(self.root) t.client('myhost') t.wm_command('myapp -x') # WM_CLIENT_MACHINE and WM_COMMAND are X11 properties; elsewhere the # setters may be no-ops and wm_command may return a split list. if t._windowingsystem == 'x11': self.assertEqual(t.client(), 'myhost') self.assertEqual(t.wm_command(), 'myapp -x') def test_wm_overrideredirect(self): t = tkinter.Toplevel(self.root) self.assertFalse(t.overrideredirect()) t.overrideredirect(True) self.assertTrue(t.overrideredirect()) def test_wm_state(self): t = tkinter.Toplevel(self.root) t.update() self.assertEqual(t.state(), 'normal') t.withdraw() self.assertEqual(t.state(), 'withdrawn') t.deiconify() t.update() self.assertEqual(t.state(), 'normal') self.assertRaises(TclError, t.state, 'spam') def test_wm_frame(self): t = tkinter.Toplevel(self.root) t.update() self.assertIsInstance(t.frame(), str) def test_wm_group(self): # The window group is an X11 concept and may be a no-op elsewhere. t = tkinter.Toplevel(self.root) t.group(self.root) self.assertIn(t.group(), (str(self.root), '')) def test_wm_protocol(self): t = tkinter.Toplevel(self.root) self.assertIsInstance(t.protocol(), tuple) t.protocol('WM_SAVE_YOURSELF', lambda: None) self.assertIn('WM_SAVE_YOURSELF', t.protocol()) # Querying a single protocol returns the bound command name. self.assertTrue(t.protocol('WM_SAVE_YOURSELF')) def test_wm_transient(self): t = tkinter.Toplevel(self.root) self.assertEqual(t.transient(), '') t.transient(self.root) self.assertEqual(str(t.transient()), str(self.root)) class EventTest(AbstractTkTest, unittest.TestCase): def test_focus(self): f = tkinter.Frame(self.root, width=150, height=100) f.pack() self.root.wait_visibility() # needed on Windows self.root.update_idletasks() events = [] f.bind('', events.append) f.focus_force() self.root.update() self.assertEqual(len(events), 1, events) e = events[0] self.assertIs(e.type, tkinter.EventType.FocusIn) self.assertIs(e.widget, f) self.assertIsInstance(e.serial, int) self.assertEqual(e.time, '??') self.assertIs(e.send_event, False) self.assertNotHasAttr(e, 'focus') self.assertEqual(e.num, '??') self.assertEqual(e.state, '??') self.assertEqual(e.char, '??') self.assertEqual(e.keycode, '??') self.assertEqual(e.keysym, '??') self.assertEqual(e.keysym_num, '??') self.assertEqual(e.width, '??') self.assertEqual(e.height, '??') self.assertEqual(e.x, '??') self.assertEqual(e.y, '??') self.assertEqual(e.x_root, '??') self.assertEqual(e.y_root, '??') self.assertEqual(e.delta, 0) self.assertEqual(e.user_data, '??') self.assertEqual(e.detail, 'NotifyAncestor') self.assertEqual(repr(e), '') def test_configure(self): f = tkinter.Frame(self.root, width=150, height=100) f.pack() self.root.wait_visibility() # needed on Windows self.root.update_idletasks() events = [] f.bind('', events.append) f.configure(height=120, borderwidth=10) self.assertEqual(len(events), 1, events) e = events[0] self.assertIs(e.type, tkinter.EventType.Configure) self.assertIs(e.widget, f) self.assertIsInstance(e.serial, int) self.assertEqual(e.time, '??') self.assertIs(e.send_event, False) self.assertNotHasAttr(e, 'focus') self.assertEqual(e.num, '??') self.assertEqual(e.state, '??') self.assertEqual(e.char, '??') self.assertEqual(e.keycode, '??') self.assertEqual(e.keysym, '??') self.assertEqual(e.keysym_num, '??') self.assertEqual(e.width, 150) self.assertEqual(e.height, 100) self.assertEqual(e.x, 0) self.assertEqual(e.y, 0) self.assertEqual(e.x_root, '??') self.assertEqual(e.y_root, '??') self.assertEqual(e.delta, 0) self.assertEqual(e.user_data, '??') self.assertEqual(e.detail, '??') self.assertEqual(repr(e), '') def test_event_generate_key_press(self): f = tkinter.Frame(self.root, width=150, height=100) f.pack() self.root.wait_visibility() # needed on Windows self.root.update_idletasks() events = [] f.bind('', events.append) f.focus_force() f.event_generate('') self.assertEqual(len(events), 1, events) e = events[0] self.assertIs(e.type, tkinter.EventType.KeyPress) self.assertIs(e.widget, f) self.assertIsInstance(e.serial, int) self.assertEqual(e.time, 0) self.assertIs(e.send_event, False) self.assertNotHasAttr(e, 'focus') self.assertEqual(e.num, '??') self.assertIsInstance(e.state, int) self.assertNotEqual(e.state, 0) self.assertEqual(e.char, 'z') self.assertIsInstance(e.keycode, int) self.assertNotEqual(e.keycode, 0) self.assertEqual(e.keysym, 'z') self.assertEqual(e.keysym_num, ord('z')) self.assertEqual(e.width, '??') self.assertEqual(e.height, '??') self.assertEqual(e.x, -1 - f.winfo_rootx()) self.assertEqual(e.y, -1 - f.winfo_rooty()) self.assertEqual(e.x_root, -1) self.assertEqual(e.y_root, -1) self.assertEqual(e.delta, 0) self.assertEqual(e.user_data, '??') self.assertEqual(e.detail, '??') self.assertEqual(repr(e), f"") def test_event_generate_enter(self): f = tkinter.Frame(self.root, width=150, height=100) f.pack() self.root.wait_visibility() # needed on Windows self.root.update_idletasks() events = [] f.bind('', events.append) f.event_generate('', x=100, y=50) self.assertEqual(len(events), 1, events) e = events[0] self.assertIs(e.type, tkinter.EventType.Enter) self.assertIs(e.widget, f) self.assertIsInstance(e.serial, int) self.assertEqual(e.time, 0) self.assertIs(e.send_event, False) self.assertIs(e.focus, False) self.assertEqual(e.num, '??') self.assertEqual(e.state, 0) self.assertEqual(e.char, '??') self.assertEqual(e.keycode, '??') self.assertEqual(e.keysym, '??') self.assertEqual(e.keysym_num, '??') self.assertEqual(e.width, '??') self.assertEqual(e.height, '??') self.assertEqual(e.x, 100) self.assertEqual(e.y, 50) self.assertEqual(e.x_root, 100 + f.winfo_rootx()) self.assertEqual(e.y_root, 50 + f.winfo_rooty()) self.assertEqual(e.delta, 0) self.assertEqual(e.user_data, '??') self.assertEqual(e.detail, 'NotifyAncestor') self.assertEqual(repr(e), '') f.event_generate('', x=100, y=50, detail='NotifyPointer') self.assertEqual(len(events), 2, events) e = events[1] self.assertIs(e.type, tkinter.EventType.Enter) self.assertEqual(e.user_data, '??') self.assertEqual(e.detail, 'NotifyPointer') def test_event_generate_button_press(self): f = tkinter.Frame(self.root, width=150, height=100) f.pack() self.root.wait_visibility() # needed on Windows self.root.update_idletasks() events = [] f.bind('', events.append) f.focus_force() f.event_generate('', x=100, y=50) self.assertEqual(len(events), 1, events) e = events[0] self.assertIs(e.type, tkinter.EventType.ButtonPress) self.assertIs(e.widget, f) self.assertIsInstance(e.serial, int) self.assertEqual(e.time, 0) self.assertIs(e.send_event, False) self.assertNotHasAttr(e, 'focus') self.assertEqual(e.num, 1) self.assertEqual(e.state, 0) self.assertEqual(e.char, '??') self.assertEqual(e.keycode, '??') self.assertEqual(e.keysym, '??') self.assertEqual(e.keysym_num, '??') self.assertEqual(e.width, '??') self.assertEqual(e.height, '??') self.assertEqual(e.x, 100) self.assertEqual(e.y, 50) self.assertEqual(e.x_root, f.winfo_rootx() + 100) self.assertEqual(e.y_root, f.winfo_rooty() + 50) self.assertEqual(e.delta, 0) self.assertEqual(e.user_data, '??') self.assertEqual(e.detail, '??') self.assertEqual(repr(e), '') def test_event_generate_motion(self): f = tkinter.Frame(self.root, width=150, height=100) f.pack() self.root.wait_visibility() # needed on Windows self.root.update_idletasks() events = [] f.bind('', events.append) f.focus_force() f.event_generate('', x=100, y=50) self.assertEqual(len(events), 1, events) e = events[0] self.assertIs(e.type, tkinter.EventType.Motion) self.assertIs(e.widget, f) self.assertIsInstance(e.serial, int) self.assertEqual(e.time, 0) self.assertIs(e.send_event, False) self.assertNotHasAttr(e, 'focus') self.assertEqual(e.num, '??') self.assertEqual(e.state, 0x100) self.assertEqual(e.char, '??') self.assertEqual(e.keycode, '??') self.assertEqual(e.keysym, '??') self.assertEqual(e.keysym_num, '??') self.assertEqual(e.width, '??') self.assertEqual(e.height, '??') self.assertEqual(e.x, 100) self.assertEqual(e.y, 50) self.assertEqual(e.x_root, f.winfo_rootx() + 100) self.assertEqual(e.y_root, f.winfo_rooty() + 50) self.assertEqual(e.delta, 0) self.assertEqual(e.user_data, '??') self.assertEqual(e.detail, '??') self.assertEqual(repr(e), '') def test_event_generate_mouse_wheel(self): f = tkinter.Frame(self.root, width=150, height=100) f.pack() self.root.wait_visibility() # needed on Windows self.root.update_idletasks() events = [] f.bind('', events.append) f.focus_force() f.event_generate('', x=100, y=50, delta=-5) self.assertEqual(len(events), 1, events) e = events[0] self.assertIs(e.type, tkinter.EventType.MouseWheel) self.assertIs(e.widget, f) self.assertIsInstance(e.serial, int) self.assertIs(e.send_event, False) self.assertNotHasAttr(e, 'focus') self.assertEqual(e.time, 0) self.assertEqual(e.num, '??') self.assertEqual(e.state, 0) self.assertEqual(e.char, '??') self.assertEqual(e.keycode, '??') self.assertEqual(e.keysym, '??') self.assertEqual(e.keysym_num, '??') self.assertEqual(e.width, '??') self.assertEqual(e.height, '??') self.assertEqual(e.x, 100) self.assertEqual(e.y, 50) self.assertEqual(e.x_root, f.winfo_rootx() + 100) self.assertEqual(e.y_root, f.winfo_rooty() + 50) self.assertEqual(e.delta, -5) self.assertEqual(e.user_data, '??') self.assertEqual(e.detail, '??') self.assertEqual(repr(e), '') def test_event_generate_virtual_event(self): f = tkinter.Frame(self.root, width=150, height=100) f.pack() self.root.wait_visibility() # needed on Windows self.root.update_idletasks() events = [] f.bind('<>', events.append) f.focus_force() f.event_generate('<>', x=50) self.assertEqual(len(events), 1, events) e = events[0] self.assertIs(e.type, tkinter.EventType.VirtualEvent) self.assertIs(e.widget, f) self.assertIsInstance(e.serial, int) self.assertEqual(e.time, 0) self.assertIs(e.send_event, False) self.assertNotHasAttr(e, 'focus') self.assertEqual(e.num, '??') self.assertEqual(e.state, 0) self.assertEqual(e.char, '??') self.assertEqual(e.keycode, '??') self.assertEqual(e.keysym, '??') self.assertEqual(e.keysym_num, '??') self.assertEqual(e.width, '??') self.assertEqual(e.height, '??') self.assertEqual(e.x, 50) self.assertEqual(e.y, 0) self.assertEqual(e.x_root, f.winfo_rootx() + 50) self.assertEqual(e.y_root, -1) self.assertEqual(e.delta, 0) self.assertEqual(e.user_data, '') self.assertEqual(e.detail, '??') self.assertEqual(repr(e), f"") f.event_generate('<>', data='spam') self.assertEqual(len(events), 2, events) e = events[1] self.assertIs(e.type, tkinter.EventType.VirtualEvent) self.assertEqual(e.user_data, 'spam') self.assertEqual(e.detail, '??') class BindTest(AbstractTkTest, unittest.TestCase): def setUp(self): super().setUp() root = self.root self.frame = tkinter.Frame(self.root, class_='Test', width=150, height=100) self.frame.pack() def assertCommandExist(self, funcid): self.assertEqual(_info_commands(self.root, funcid), (funcid,)) def assertCommandNotExist(self, funcid): self.assertEqual(_info_commands(self.root, funcid), ()) def test_bind(self): event = '' f = self.frame self.assertEqual(f.bind(), ()) self.assertEqual(f.bind(event), '') def test1(e): pass def test2(e): pass funcid = f.bind(event, test1) self.assertEqual(f.bind(), (event,)) script = f.bind(event) self.assertIn(funcid, script) self.assertCommandExist(funcid) funcid2 = f.bind(event, test2, add=True) script = f.bind(event) self.assertIn(funcid, script) self.assertIn(funcid2, script) self.assertCommandExist(funcid) self.assertCommandExist(funcid2) def test_unbind(self): event = '' f = self.frame self.assertEqual(f.bind(), ()) self.assertEqual(f.bind(event), '') def test1(e): pass def test2(e): pass funcid = f.bind(event, test1) funcid2 = f.bind(event, test2, add=True) self.assertRaises(TypeError, f.unbind) f.unbind(event) self.assertEqual(f.bind(event), '') self.assertEqual(f.bind(), ()) def test_unbind2(self): f = self.frame f.wait_visibility() f.focus_force() f.update_idletasks() event = '' self.assertEqual(f.bind(), ()) self.assertEqual(f.bind(event), '') def test1(e): events.append('a') def test2(e): events.append('b') def test3(e): events.append('c') funcid = f.bind(event, test1) funcid2 = f.bind(event, test2, add=True) funcid3 = f.bind(event, test3, add=True) events = [] f.event_generate(event) self.assertEqual(events, ['a', 'b', 'c']) f.unbind(event, funcid2) script = f.bind(event) self.assertNotIn(funcid2, script) self.assertIn(funcid, script) self.assertIn(funcid3, script) self.assertEqual(f.bind(), (event,)) self.assertCommandNotExist(funcid2) self.assertCommandExist(funcid) self.assertCommandExist(funcid3) events = [] f.event_generate(event) self.assertEqual(events, ['a', 'c']) f.unbind(event, funcid) f.unbind(event, funcid3) self.assertEqual(f.bind(event), '') self.assertEqual(f.bind(), ()) self.assertCommandNotExist(funcid) self.assertCommandNotExist(funcid2) self.assertCommandNotExist(funcid3) events = [] f.event_generate(event) self.assertEqual(events, []) # non-idempotent self.assertRaises(tkinter.TclError, f.unbind, event, funcid2) def test_bind_rebind(self): event = '' f = self.frame self.assertEqual(f.bind(), ()) self.assertEqual(f.bind(event), '') def test1(e): pass def test2(e): pass def test3(e): pass funcid = f.bind(event, test1) funcid2 = f.bind(event, test2, add=True) script = f.bind(event) self.assertIn(funcid2, script) self.assertIn(funcid, script) self.assertCommandExist(funcid) self.assertCommandExist(funcid2) funcid3 = f.bind(event, test3) script = f.bind(event) self.assertNotIn(funcid, script) self.assertNotIn(funcid2, script) self.assertIn(funcid3, script) self.assertCommandNotExist(funcid) self.assertCommandNotExist(funcid2) self.assertCommandExist(funcid3) def test_bind_class(self): event = '' bind_class = self.root.bind_class unbind_class = self.root.unbind_class self.assertRaises(TypeError, bind_class) self.assertEqual(bind_class('Test'), ()) self.assertEqual(bind_class('Test', event), '') self.addCleanup(unbind_class, 'Test', event) def test1(e): pass def test2(e): pass funcid = bind_class('Test', event, test1) self.assertEqual(bind_class('Test'), (event,)) script = bind_class('Test', event) self.assertIn(funcid, script) self.assertCommandExist(funcid) funcid2 = bind_class('Test', event, test2, add=True) script = bind_class('Test', event) self.assertIn(funcid, script) self.assertIn(funcid2, script) self.assertCommandExist(funcid) self.assertCommandExist(funcid2) def test_unbind_class(self): event = '' bind_class = self.root.bind_class unbind_class = self.root.unbind_class self.assertEqual(bind_class('Test'), ()) self.assertEqual(bind_class('Test', event), '') self.addCleanup(unbind_class, 'Test', event) def test1(e): pass def test2(e): pass funcid = bind_class('Test', event, test1) funcid2 = bind_class('Test', event, test2, add=True) self.assertRaises(TypeError, unbind_class) self.assertRaises(TypeError, unbind_class, 'Test') unbind_class('Test', event) self.assertEqual(bind_class('Test', event), '') self.assertEqual(bind_class('Test'), ()) self.assertCommandNotExist(funcid) self.assertCommandNotExist(funcid2) unbind_class('Test', event) # idempotent def test_bind_class_rebind(self): event = '' bind_class = self.root.bind_class unbind_class = self.root.unbind_class self.assertEqual(bind_class('Test'), ()) self.assertEqual(bind_class('Test', event), '') self.addCleanup(unbind_class, 'Test', event) def test1(e): pass def test2(e): pass def test3(e): pass funcid = bind_class('Test', event, test1) funcid2 = bind_class('Test', event, test2, add=True) script = bind_class('Test', event) self.assertIn(funcid2, script) self.assertIn(funcid, script) self.assertCommandExist(funcid) self.assertCommandExist(funcid2) funcid3 = bind_class('Test', event, test3) script = bind_class('Test', event) self.assertNotIn(funcid, script) self.assertNotIn(funcid2, script) self.assertIn(funcid3, script) self.assertCommandNotExist(funcid) self.assertCommandNotExist(funcid2) self.assertCommandExist(funcid3) def test_bind_all(self): event = '' bind_all = self.root.bind_all unbind_all = self.root.unbind_all self.assertNotIn(event, bind_all()) self.assertEqual(bind_all(event), '') self.addCleanup(unbind_all, event) def test1(e): pass def test2(e): pass funcid = bind_all(event, test1) self.assertIn(event, bind_all()) script = bind_all(event) self.assertIn(funcid, script) self.assertCommandExist(funcid) funcid2 = bind_all(event, test2, add=True) script = bind_all(event) self.assertIn(funcid, script) self.assertIn(funcid2, script) self.assertCommandExist(funcid) self.assertCommandExist(funcid2) def test_unbind_all(self): event = '' bind_all = self.root.bind_all unbind_all = self.root.unbind_all self.assertNotIn(event, bind_all()) self.assertEqual(bind_all(event), '') self.addCleanup(unbind_all, event) def test1(e): pass def test2(e): pass funcid = bind_all(event, test1) funcid2 = bind_all(event, test2, add=True) unbind_all(event) self.assertEqual(bind_all(event), '') self.assertNotIn(event, bind_all()) self.assertCommandNotExist(funcid) self.assertCommandNotExist(funcid2) unbind_all(event) # idempotent def test_bind_all_rebind(self): event = '' bind_all = self.root.bind_all unbind_all = self.root.unbind_all self.assertNotIn(event, bind_all()) self.assertEqual(bind_all(event), '') self.addCleanup(unbind_all, event) def test1(e): pass def test2(e): pass def test3(e): pass funcid = bind_all(event, test1) funcid2 = bind_all(event, test2, add=True) script = bind_all(event) self.assertIn(funcid2, script) self.assertIn(funcid, script) self.assertCommandExist(funcid) self.assertCommandExist(funcid2) funcid3 = bind_all(event, test3) script = bind_all(event) self.assertNotIn(funcid, script) self.assertNotIn(funcid2, script) self.assertIn(funcid3, script) self.assertCommandNotExist(funcid) self.assertCommandNotExist(funcid2) self.assertCommandExist(funcid3) def _test_tag_bind(self, w): tag = 'sel' event = '' w.pack() self.assertRaises(TypeError, w.tag_bind) tag_bind = w._tag_bind if isinstance(w, tkinter.Text) else w.tag_bind if isinstance(w, tkinter.Text): self.assertRaises(TypeError, w.tag_bind, tag) self.assertRaises(TypeError, w.tag_bind, tag, event) self.assertEqual(tag_bind(tag), ()) self.assertEqual(tag_bind(tag, event), '') def test1(e): pass def test2(e): pass funcid = w.tag_bind(tag, event, test1) self.assertEqual(tag_bind(tag), (event,)) script = tag_bind(tag, event) self.assertIn(funcid, script) self.assertCommandExist(funcid) funcid2 = w.tag_bind(tag, event, test2, add=True) script = tag_bind(tag, event) self.assertIn(funcid, script) self.assertIn(funcid2, script) self.assertCommandExist(funcid) self.assertCommandExist(funcid2) def _test_tag_unbind(self, w): tag = 'sel' event = '' w.pack() tag_bind = w._tag_bind if isinstance(w, tkinter.Text) else w.tag_bind self.assertEqual(tag_bind(tag), ()) self.assertEqual(tag_bind(tag, event), '') def test1(e): pass def test2(e): pass funcid = w.tag_bind(tag, event, test1) funcid2 = w.tag_bind(tag, event, test2, add=True) self.assertRaises(TypeError, w.tag_unbind, tag) w.tag_unbind(tag, event) self.assertEqual(tag_bind(tag, event), '') self.assertEqual(tag_bind(tag), ()) def _test_tag_bind_rebind(self, w): tag = 'sel' event = '' w.pack() tag_bind = w._tag_bind if isinstance(w, tkinter.Text) else w.tag_bind self.assertEqual(tag_bind(tag), ()) self.assertEqual(tag_bind(tag, event), '') def test1(e): pass def test2(e): pass def test3(e): pass funcid = w.tag_bind(tag, event, test1) funcid2 = w.tag_bind(tag, event, test2, add=True) script = tag_bind(tag, event) self.assertIn(funcid2, script) self.assertIn(funcid, script) self.assertCommandExist(funcid) self.assertCommandExist(funcid2) funcid3 = w.tag_bind(tag, event, test3) script = tag_bind(tag, event) self.assertNotIn(funcid, script) self.assertNotIn(funcid2, script) self.assertIn(funcid3, script) self.assertCommandExist(funcid3) def test_canvas_tag_bind(self): c = tkinter.Canvas(self.frame) self._test_tag_bind(c) def test_canvas_tag_unbind(self): c = tkinter.Canvas(self.frame) self._test_tag_unbind(c) def test_canvas_tag_bind_rebind(self): c = tkinter.Canvas(self.frame) self._test_tag_bind_rebind(c) def test_text_tag_bind(self): t = tkinter.Text(self.frame) self._test_tag_bind(t) def test_text_tag_unbind(self): t = tkinter.Text(self.frame) self._test_tag_unbind(t) def test_text_tag_bind_rebind(self): t = tkinter.Text(self.frame) self._test_tag_bind_rebind(t) def test_bindtags(self): f = self.frame self.assertEqual(self.root.bindtags(), ('.', 'Tk', 'all')) self.assertEqual(f.bindtags(), (str(f), 'Test', '.', 'all')) f.bindtags(('a', 'b c')) self.assertEqual(f.bindtags(), ('a', 'b c')) def test_bind_events(self): event = '' root = self.root t = tkinter.Toplevel(root) f = tkinter.Frame(t, class_='Test', width=150, height=100) f.pack() root.wait_visibility() # needed on Windows root.update_idletasks() self.addCleanup(root.unbind_class, 'Test', event) self.addCleanup(root.unbind_class, 'Toplevel', event) self.addCleanup(root.unbind_class, 'tag', event) self.addCleanup(root.unbind_class, 'tag2', event) self.addCleanup(root.unbind_all, event) def test(what): return lambda e: events.append((what, e.widget)) root.bind_all(event, test('all')) root.bind_class('Test', event, test('frame class')) root.bind_class('Toplevel', event, test('toplevel class')) root.bind_class('tag', event, test('tag')) root.bind_class('tag2', event, test('tag2')) f.bind(event, test('frame')) t.bind(event, test('toplevel')) events = [] f.event_generate(event) self.assertEqual(events, [ ('frame', f), ('frame class', f), ('toplevel', f), ('all', f), ]) events = [] t.event_generate(event) self.assertEqual(events, [ ('toplevel', t), ('toplevel class', t), ('all', t), ]) f.bindtags(('tag', 'tag3')) events = [] f.event_generate(event) self.assertEqual(events, [('tag', f)]) 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.assertNotHasAttr(tkinter, '_default_root') # repeated call is no-op tkinter.NoDefaultRoot() self.assertIs(tkinter._support_default_root, False) self.assertNotHasAttr(tkinter, '_default_root') root.destroy() self.assertIs(tkinter._support_default_root, False) self.assertNotHasAttr(tkinter, '_default_root') root = tkinter.Tk() self.assertIs(tkinter._support_default_root, False) self.assertNotHasAttr(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) def _info_commands(widget, pattern=None): return widget.tk.splitlist(widget.tk.call('info', 'commands', pattern)) if __name__ == "__main__": unittest.main()