bpo-42630: Improve error reporting in Tkinter for absent default root (GH-23781)
* Tkinter functions and constructors which need a default root window raise now RuntimeError with descriptive message instead of obscure AttributeError or NameError if it is not created yet or cannot be created automatically. * Add tests for all functions which use default root window. * Fix import in the pynche script.
This commit is contained in:
parent
1e27b57dbc
commit
3d569fd6dc
|
@ -1061,8 +1061,10 @@ class PyShell(OutputWindow):
|
||||||
(sys.version, sys.platform, self.COPYRIGHT, nosub))
|
(sys.version, sys.platform, self.COPYRIGHT, nosub))
|
||||||
self.text.focus_force()
|
self.text.focus_force()
|
||||||
self.showprompt()
|
self.showprompt()
|
||||||
|
# User code should use separate default Tk root window
|
||||||
import tkinter
|
import tkinter
|
||||||
tkinter._default_root = None # 03Jan04 KBK What's this?
|
tkinter._support_default_root = True
|
||||||
|
tkinter._default_root = None
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def stop_readline(self):
|
def stop_readline(self):
|
||||||
|
|
|
@ -20,5 +20,5 @@ from idlelib.idle_test import load_tests
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
tk.NoDefaultRoot()
|
tk.NoDefaultRoot()
|
||||||
unittest.main(exit=False)
|
unittest.main(exit=False)
|
||||||
tk._support_default_root = 1
|
tk._support_default_root = True
|
||||||
tk._default_root = None
|
tk._default_root = None
|
||||||
|
|
|
@ -270,7 +270,7 @@ class Event:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
_support_default_root = 1
|
_support_default_root = True
|
||||||
_default_root = None
|
_default_root = None
|
||||||
|
|
||||||
|
|
||||||
|
@ -280,13 +280,26 @@ def NoDefaultRoot():
|
||||||
Call this function to inhibit that the first instance of
|
Call this function to inhibit that the first instance of
|
||||||
Tk is used for windows without an explicit parent window.
|
Tk is used for windows without an explicit parent window.
|
||||||
"""
|
"""
|
||||||
global _support_default_root
|
global _support_default_root, _default_root
|
||||||
_support_default_root = 0
|
_support_default_root = False
|
||||||
global _default_root
|
# Delete, so any use of _default_root will immediately raise an exception.
|
||||||
|
# Rebind before deletion, so repeated calls will not fail.
|
||||||
_default_root = None
|
_default_root = None
|
||||||
del _default_root
|
del _default_root
|
||||||
|
|
||||||
|
|
||||||
|
def _get_default_root(what=None):
|
||||||
|
if not _support_default_root:
|
||||||
|
raise RuntimeError("No master specified and tkinter is "
|
||||||
|
"configured to not support default root")
|
||||||
|
if not _default_root:
|
||||||
|
if what:
|
||||||
|
raise RuntimeError(f"Too early to {what}: no default root window")
|
||||||
|
root = Tk()
|
||||||
|
assert _default_root is root
|
||||||
|
return _default_root
|
||||||
|
|
||||||
|
|
||||||
def _tkerror(err):
|
def _tkerror(err):
|
||||||
"""Internal function."""
|
"""Internal function."""
|
||||||
pass
|
pass
|
||||||
|
@ -330,7 +343,7 @@ class Variable:
|
||||||
raise TypeError("name must be a string")
|
raise TypeError("name must be a string")
|
||||||
global _varnum
|
global _varnum
|
||||||
if not master:
|
if not master:
|
||||||
master = _default_root
|
master = _get_default_root('create variable')
|
||||||
self._root = master._root()
|
self._root = master._root()
|
||||||
self._tk = master.tk
|
self._tk = master.tk
|
||||||
if name:
|
if name:
|
||||||
|
@ -591,7 +604,7 @@ class BooleanVar(Variable):
|
||||||
|
|
||||||
def mainloop(n=0):
|
def mainloop(n=0):
|
||||||
"""Run the main loop of Tcl."""
|
"""Run the main loop of Tcl."""
|
||||||
_default_root.tk.mainloop(n)
|
_get_default_root('run the main loop').tk.mainloop(n)
|
||||||
|
|
||||||
|
|
||||||
getint = int
|
getint = int
|
||||||
|
@ -600,9 +613,9 @@ getdouble = float
|
||||||
|
|
||||||
|
|
||||||
def getboolean(s):
|
def getboolean(s):
|
||||||
"""Convert true and false to integer values 1 and 0."""
|
"""Convert Tcl object to True or False."""
|
||||||
try:
|
try:
|
||||||
return _default_root.tk.getboolean(s)
|
return _get_default_root('use getboolean()').tk.getboolean(s)
|
||||||
except TclError:
|
except TclError:
|
||||||
raise ValueError("invalid literal for getboolean()")
|
raise ValueError("invalid literal for getboolean()")
|
||||||
|
|
||||||
|
@ -2248,7 +2261,7 @@ class Tk(Misc, Wm):
|
||||||
is the name of the widget class."""
|
is the name of the widget class."""
|
||||||
self.master = None
|
self.master = None
|
||||||
self.children = {}
|
self.children = {}
|
||||||
self._tkloaded = 0
|
self._tkloaded = False
|
||||||
# to avoid recursions in the getattr code in case of failure, we
|
# to avoid recursions in the getattr code in case of failure, we
|
||||||
# ensure that self.tk is always _something_.
|
# ensure that self.tk is always _something_.
|
||||||
self.tk = None
|
self.tk = None
|
||||||
|
@ -2272,7 +2285,7 @@ class Tk(Misc, Wm):
|
||||||
self._loadtk()
|
self._loadtk()
|
||||||
|
|
||||||
def _loadtk(self):
|
def _loadtk(self):
|
||||||
self._tkloaded = 1
|
self._tkloaded = True
|
||||||
global _default_root
|
global _default_root
|
||||||
# Version sanity checks
|
# Version sanity checks
|
||||||
tk_version = self.tk.getvar('tk_version')
|
tk_version = self.tk.getvar('tk_version')
|
||||||
|
@ -2521,12 +2534,8 @@ class BaseWidget(Misc):
|
||||||
|
|
||||||
def _setup(self, master, cnf):
|
def _setup(self, master, cnf):
|
||||||
"""Internal function. Sets up information about children."""
|
"""Internal function. Sets up information about children."""
|
||||||
if _support_default_root:
|
|
||||||
global _default_root
|
|
||||||
if not master:
|
if not master:
|
||||||
if not _default_root:
|
master = _get_default_root()
|
||||||
_default_root = Tk()
|
|
||||||
master = _default_root
|
|
||||||
self.master = master
|
self.master = master
|
||||||
self.tk = master.tk
|
self.tk = master.tk
|
||||||
name = None
|
name = None
|
||||||
|
@ -3990,9 +3999,7 @@ class Image:
|
||||||
def __init__(self, imgtype, name=None, cnf={}, master=None, **kw):
|
def __init__(self, imgtype, name=None, cnf={}, master=None, **kw):
|
||||||
self.name = None
|
self.name = None
|
||||||
if not master:
|
if not master:
|
||||||
master = _default_root
|
master = _get_default_root('create image')
|
||||||
if not master:
|
|
||||||
raise RuntimeError('Too early to create image')
|
|
||||||
self.tk = getattr(master, 'tk', master)
|
self.tk = getattr(master, 'tk', master)
|
||||||
if not name:
|
if not name:
|
||||||
Image._last_id += 1
|
Image._last_id += 1
|
||||||
|
@ -4146,11 +4153,13 @@ class BitmapImage(Image):
|
||||||
|
|
||||||
|
|
||||||
def image_names():
|
def image_names():
|
||||||
return _default_root.tk.splitlist(_default_root.tk.call('image', 'names'))
|
tk = _get_default_root('use image_names()').tk
|
||||||
|
return tk.splitlist(tk.call('image', 'names'))
|
||||||
|
|
||||||
|
|
||||||
def image_types():
|
def image_types():
|
||||||
return _default_root.tk.splitlist(_default_root.tk.call('image', 'types'))
|
tk = _get_default_root('use image_types()').tk
|
||||||
|
return tk.splitlist(tk.call('image', 'types'))
|
||||||
|
|
||||||
|
|
||||||
class Spinbox(Widget, XView):
|
class Spinbox(Widget, XView):
|
||||||
|
|
|
@ -18,10 +18,10 @@ class Dialog:
|
||||||
command = None
|
command = None
|
||||||
|
|
||||||
def __init__(self, master=None, **options):
|
def __init__(self, master=None, **options):
|
||||||
|
if not master:
|
||||||
|
master = options.get('parent')
|
||||||
self.master = master
|
self.master = master
|
||||||
self.options = options
|
self.options = options
|
||||||
if not master and options.get('parent'):
|
|
||||||
self.master = options['parent']
|
|
||||||
|
|
||||||
def _fixoptions(self):
|
def _fixoptions(self):
|
||||||
pass # hook
|
pass # hook
|
||||||
|
|
|
@ -69,7 +69,7 @@ class Font:
|
||||||
def __init__(self, root=None, font=None, name=None, exists=False,
|
def __init__(self, root=None, font=None, name=None, exists=False,
|
||||||
**options):
|
**options):
|
||||||
if not root:
|
if not root:
|
||||||
root = tkinter._default_root
|
root = tkinter._get_default_root('use font')
|
||||||
tk = getattr(root, 'tk', root)
|
tk = getattr(root, 'tk', root)
|
||||||
if font:
|
if font:
|
||||||
# get actual settings corresponding to the given font
|
# get actual settings corresponding to the given font
|
||||||
|
@ -184,7 +184,7 @@ class Font:
|
||||||
def families(root=None, displayof=None):
|
def families(root=None, displayof=None):
|
||||||
"Get font families (as a tuple)"
|
"Get font families (as a tuple)"
|
||||||
if not root:
|
if not root:
|
||||||
root = tkinter._default_root
|
root = tkinter._get_default_root('use font.families()')
|
||||||
args = ()
|
args = ()
|
||||||
if displayof:
|
if displayof:
|
||||||
args = ('-displayof', displayof)
|
args = ('-displayof', displayof)
|
||||||
|
@ -194,7 +194,7 @@ def families(root=None, displayof=None):
|
||||||
def names(root=None):
|
def names(root=None):
|
||||||
"Get names of defined fonts (as a tuple)"
|
"Get names of defined fonts (as a tuple)"
|
||||||
if not root:
|
if not root:
|
||||||
root = tkinter._default_root
|
root = tkinter._get_default_root('use font.names()')
|
||||||
return root.tk.splitlist(root.tk.call("font", "names"))
|
return root.tk.splitlist(root.tk.call("font", "names"))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,7 @@ askstring -- get a string from the user
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from tkinter import *
|
from tkinter import *
|
||||||
from tkinter import messagebox
|
from tkinter import messagebox, _get_default_root
|
||||||
|
|
||||||
import tkinter # used at _QueryDialog for tkinter._default_root
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleDialog:
|
class SimpleDialog:
|
||||||
|
@ -128,13 +126,17 @@ class Dialog(Toplevel):
|
||||||
|
|
||||||
title -- the dialog title
|
title -- the dialog title
|
||||||
'''
|
'''
|
||||||
Toplevel.__init__(self, parent)
|
master = parent
|
||||||
|
if not master:
|
||||||
|
master = _get_default_root('create dialog window')
|
||||||
|
|
||||||
|
Toplevel.__init__(self, master)
|
||||||
|
|
||||||
self.withdraw() # remain invisible for now
|
self.withdraw() # remain invisible for now
|
||||||
# If the master is not viewable, don't
|
# If the parent is not viewable, don't
|
||||||
# make the child transient, or else it
|
# make the child transient, or else it
|
||||||
# would be opened withdrawn
|
# would be opened withdrawn
|
||||||
if parent.winfo_viewable():
|
if parent is not None and parent.winfo_viewable():
|
||||||
self.transient(parent)
|
self.transient(parent)
|
||||||
|
|
||||||
if title:
|
if title:
|
||||||
|
@ -155,7 +157,7 @@ class Dialog(Toplevel):
|
||||||
|
|
||||||
self.protocol("WM_DELETE_WINDOW", self.cancel)
|
self.protocol("WM_DELETE_WINDOW", self.cancel)
|
||||||
|
|
||||||
if self.parent is not None:
|
if parent is not None:
|
||||||
self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
|
self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
|
||||||
parent.winfo_rooty()+50))
|
parent.winfo_rooty()+50))
|
||||||
|
|
||||||
|
@ -259,9 +261,6 @@ class _QueryDialog(Dialog):
|
||||||
minvalue = None, maxvalue = None,
|
minvalue = None, maxvalue = None,
|
||||||
parent = None):
|
parent = None):
|
||||||
|
|
||||||
if not parent:
|
|
||||||
parent = tkinter._default_root
|
|
||||||
|
|
||||||
self.prompt = prompt
|
self.prompt = prompt
|
||||||
self.minvalue = minvalue
|
self.minvalue = minvalue
|
||||||
self.maxvalue = maxvalue
|
self.maxvalue = maxvalue
|
||||||
|
|
|
@ -36,6 +36,33 @@ class AbstractTkTest:
|
||||||
w.destroy()
|
w.destroy()
|
||||||
self.root.withdraw()
|
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():
|
def destroy_default_root():
|
||||||
if getattr(tkinter, '_default_root', None):
|
if getattr(tkinter, '_default_root', None):
|
||||||
tkinter._default_root.update_idletasks()
|
tkinter._default_root.update_idletasks()
|
||||||
|
|
|
@ -2,7 +2,7 @@ import unittest
|
||||||
import tkinter
|
import tkinter
|
||||||
from tkinter import font
|
from tkinter import font
|
||||||
from test.support import requires, run_unittest, gc_collect, ALWAYS_EQ
|
from test.support import requires, run_unittest, gc_collect, ALWAYS_EQ
|
||||||
from tkinter.test.support import AbstractTkTest
|
from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest
|
||||||
|
|
||||||
requires('gui')
|
requires('gui')
|
||||||
|
|
||||||
|
@ -107,7 +107,37 @@ class FontTest(AbstractTkTest, unittest.TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
tests_gui = (FontTest, )
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
tests_gui = (FontTest, DefaultRootTest)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
run_unittest(*tests_gui)
|
run_unittest(*tests_gui)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import unittest
|
||||||
import tkinter
|
import tkinter
|
||||||
from test import support
|
from test import support
|
||||||
from test.support import os_helper
|
from test.support import os_helper
|
||||||
from tkinter.test.support import AbstractTkTest, requires_tcl
|
from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest, requires_tcl
|
||||||
|
|
||||||
support.requires('gui')
|
support.requires('gui')
|
||||||
|
|
||||||
|
@ -20,6 +20,47 @@ class MiscTest(AbstractTkTest, unittest.TestCase):
|
||||||
self.assertIsInstance(image_names, tuple)
|
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):
|
class BitmapImageTest(AbstractTkTest, unittest.TestCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -331,7 +372,7 @@ class PhotoImageTest(AbstractTkTest, unittest.TestCase):
|
||||||
self.assertEqual(image.transparency_get(4, 6), False)
|
self.assertEqual(image.transparency_get(4, 6), False)
|
||||||
|
|
||||||
|
|
||||||
tests_gui = (MiscTest, BitmapImageTest, PhotoImageTest,)
|
tests_gui = (MiscTest, DefaultRootTest, BitmapImageTest, PhotoImageTest,)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
support.run_unittest(*tests_gui)
|
support.run_unittest(*tests_gui)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import unittest
|
import unittest
|
||||||
import tkinter
|
import tkinter
|
||||||
from test import support
|
from test import support
|
||||||
from tkinter.test.support import AbstractTkTest
|
from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest
|
||||||
|
|
||||||
support.requires('gui')
|
support.requires('gui')
|
||||||
|
|
||||||
|
@ -241,7 +241,85 @@ class MiscTest(AbstractTkTest, unittest.TestCase):
|
||||||
" num=3 delta=-1 focus=True"
|
" num=3 delta=-1 focus=True"
|
||||||
" x=10 y=20 width=300 height=200>")
|
" x=10 y=20 width=300 height=200>")
|
||||||
|
|
||||||
tests_gui = (MiscTest, )
|
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())
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
tests_gui = (MiscTest, DefaultRootTest)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
support.run_unittest(*tests_gui)
|
support.run_unittest(*tests_gui)
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import unittest
|
||||||
|
import tkinter
|
||||||
|
from test.support import requires, run_unittest, swap_attr
|
||||||
|
from tkinter.test.support import AbstractDefaultRootTest
|
||||||
|
from tkinter.simpledialog import Dialog, askinteger
|
||||||
|
|
||||||
|
requires('gui')
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
|
||||||
|
|
||||||
|
def test_askinteger(self):
|
||||||
|
self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number")
|
||||||
|
root = tkinter.Tk()
|
||||||
|
with swap_attr(Dialog, 'wait_window', lambda self, w: w.destroy()):
|
||||||
|
askinteger("Go To Line", "Line number")
|
||||||
|
root.destroy()
|
||||||
|
tkinter.NoDefaultRoot()
|
||||||
|
self.assertRaises(RuntimeError, askinteger, "Go To Line", "Line number")
|
||||||
|
|
||||||
|
|
||||||
|
tests_gui = (DefaultRootTest,)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run_unittest(*tests_gui)
|
|
@ -1,8 +1,10 @@
|
||||||
import unittest
|
import unittest
|
||||||
import gc
|
import gc
|
||||||
|
import tkinter
|
||||||
from tkinter import (Variable, StringVar, IntVar, DoubleVar, BooleanVar, Tcl,
|
from tkinter import (Variable, StringVar, IntVar, DoubleVar, BooleanVar, Tcl,
|
||||||
TclError)
|
TclError)
|
||||||
from test.support import ALWAYS_EQ
|
from test.support import ALWAYS_EQ
|
||||||
|
from tkinter.test.support import AbstractDefaultRootTest
|
||||||
|
|
||||||
|
|
||||||
class Var(Variable):
|
class Var(Variable):
|
||||||
|
@ -308,8 +310,21 @@ class TestBooleanVar(TestBase):
|
||||||
v.get()
|
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)
|
||||||
|
|
||||||
|
|
||||||
tests_gui = (TestVariable, TestStringVar, TestIntVar,
|
tests_gui = (TestVariable, TestStringVar, TestIntVar,
|
||||||
TestDoubleVar, TestBooleanVar)
|
TestDoubleVar, TestBooleanVar, DefaultRootTest)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -5,7 +5,8 @@ import os
|
||||||
from test.support import requires
|
from test.support import requires
|
||||||
|
|
||||||
from tkinter.test.support import (tcl_version, requires_tcl,
|
from tkinter.test.support import (tcl_version, requires_tcl,
|
||||||
get_tk_patchlevel, widget_eq)
|
get_tk_patchlevel, widget_eq,
|
||||||
|
AbstractDefaultRootTest)
|
||||||
from tkinter.test.widget_tests import (
|
from tkinter.test.widget_tests import (
|
||||||
add_standard_options, noconv, pixels_round,
|
add_standard_options, noconv, pixels_round,
|
||||||
AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests,
|
AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests,
|
||||||
|
@ -1295,12 +1296,21 @@ class MessageTest(AbstractWidgetTest, unittest.TestCase):
|
||||||
self.checkIntegerParam(widget, 'aspect', 250, 0, -300)
|
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 = (
|
tests_gui = (
|
||||||
ButtonTest, CanvasTest, CheckbuttonTest, EntryTest,
|
ButtonTest, CanvasTest, CheckbuttonTest, EntryTest,
|
||||||
FrameTest, LabelFrameTest,LabelTest, ListboxTest,
|
FrameTest, LabelFrameTest,LabelTest, ListboxTest,
|
||||||
MenubuttonTest, MenuTest, MessageTest, OptionMenuTest,
|
MenubuttonTest, MenuTest, MessageTest, OptionMenuTest,
|
||||||
PanedWindowTest, RadiobuttonTest, ScaleTest, ScrollbarTest,
|
PanedWindowTest, RadiobuttonTest, ScaleTest, ScrollbarTest,
|
||||||
SpinboxTest, TextTest, ToplevelTest,
|
SpinboxTest, TextTest, ToplevelTest, DefaultRootTest,
|
||||||
)
|
)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -2,8 +2,8 @@ import sys
|
||||||
import unittest
|
import unittest
|
||||||
import tkinter
|
import tkinter
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from test.support import requires, run_unittest, swap_attr
|
from test.support import requires, run_unittest
|
||||||
from tkinter.test.support import AbstractTkTest, destroy_default_root
|
from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest
|
||||||
|
|
||||||
requires('gui')
|
requires('gui')
|
||||||
|
|
||||||
|
@ -46,20 +46,6 @@ class LabeledScaleTest(AbstractTkTest, unittest.TestCase):
|
||||||
if hasattr(sys, 'last_type'):
|
if hasattr(sys, 'last_type'):
|
||||||
self.assertNotEqual(sys.last_type, tkinter.TclError)
|
self.assertNotEqual(sys.last_type, tkinter.TclError)
|
||||||
|
|
||||||
|
|
||||||
def test_initialization_no_master(self):
|
|
||||||
# no master passing
|
|
||||||
with swap_attr(tkinter, '_default_root', None), \
|
|
||||||
swap_attr(tkinter, '_support_default_root', True):
|
|
||||||
try:
|
|
||||||
x = ttk.LabeledScale()
|
|
||||||
self.assertIsNotNone(tkinter._default_root)
|
|
||||||
self.assertEqual(x.master, tkinter._default_root)
|
|
||||||
self.assertEqual(x.tk, tkinter._default_root.tk)
|
|
||||||
x.destroy()
|
|
||||||
finally:
|
|
||||||
destroy_default_root()
|
|
||||||
|
|
||||||
def test_initialization(self):
|
def test_initialization(self):
|
||||||
# master passing
|
# master passing
|
||||||
master = tkinter.Frame(self.root)
|
master = tkinter.Frame(self.root)
|
||||||
|
@ -311,7 +297,13 @@ class OptionMenuTest(AbstractTkTest, unittest.TestCase):
|
||||||
optmenu2.destroy()
|
optmenu2.destroy()
|
||||||
|
|
||||||
|
|
||||||
tests_gui = (LabeledScaleTest, OptionMenuTest)
|
class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
|
||||||
|
|
||||||
|
def test_labeledscale(self):
|
||||||
|
self._test_widget(ttk.LabeledScale)
|
||||||
|
|
||||||
|
|
||||||
|
tests_gui = (LabeledScaleTest, OptionMenuTest, DefaultRootTest)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
run_unittest(*tests_gui)
|
run_unittest(*tests_gui)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import sys
|
||||||
|
|
||||||
from tkinter.test.test_ttk.test_functions import MockTclObj
|
from tkinter.test.test_ttk.test_functions import MockTclObj
|
||||||
from tkinter.test.support import (AbstractTkTest, tcl_version, get_tk_patchlevel,
|
from tkinter.test.support import (AbstractTkTest, tcl_version, get_tk_patchlevel,
|
||||||
simulate_mouse_click)
|
simulate_mouse_click, AbstractDefaultRootTest)
|
||||||
from tkinter.test.widget_tests import (add_standard_options, noconv,
|
from tkinter.test.widget_tests import (add_standard_options, noconv,
|
||||||
AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests,
|
AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests,
|
||||||
setUpModule)
|
setUpModule)
|
||||||
|
@ -1860,12 +1860,22 @@ class SizegripTest(AbstractWidgetTest, unittest.TestCase):
|
||||||
def create(self, **kwargs):
|
def create(self, **kwargs):
|
||||||
return ttk.Sizegrip(self.root, **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 = (
|
tests_gui = (
|
||||||
ButtonTest, CheckbuttonTest, ComboboxTest, EntryTest,
|
ButtonTest, CheckbuttonTest, ComboboxTest, EntryTest,
|
||||||
FrameTest, LabelFrameTest, LabelTest, MenubuttonTest,
|
FrameTest, LabelFrameTest, LabelTest, MenubuttonTest,
|
||||||
NotebookTest, PanedWindowTest, ProgressbarTest,
|
NotebookTest, PanedWindowTest, ProgressbarTest,
|
||||||
RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest,
|
RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest,
|
||||||
SizegripTest, SpinboxTest, TreeviewTest, WidgetTest,
|
SizegripTest, SpinboxTest, TreeviewTest, WidgetTest, DefaultRootTest,
|
||||||
)
|
)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -387,9 +387,7 @@ class TixWidget(tkinter.Widget):
|
||||||
# These are missing from Tkinter
|
# These are missing from Tkinter
|
||||||
def image_create(self, imgtype, cnf={}, master=None, **kw):
|
def image_create(self, imgtype, cnf={}, master=None, **kw):
|
||||||
if not master:
|
if not master:
|
||||||
master = tkinter._default_root
|
master = self
|
||||||
if not master:
|
|
||||||
raise RuntimeError('Too early to create image')
|
|
||||||
if kw and cnf: cnf = _cnfmerge((cnf, kw))
|
if kw and cnf: cnf = _cnfmerge((cnf, kw))
|
||||||
elif kw: cnf = kw
|
elif kw: cnf = kw
|
||||||
options = ()
|
options = ()
|
||||||
|
@ -475,10 +473,7 @@ class DisplayStyle:
|
||||||
elif 'refwindow' in cnf:
|
elif 'refwindow' in cnf:
|
||||||
master = cnf['refwindow']
|
master = cnf['refwindow']
|
||||||
else:
|
else:
|
||||||
master = tkinter._default_root
|
master = tkinter._get_default_root('create display style')
|
||||||
if not master:
|
|
||||||
raise RuntimeError("Too early to create display style: "
|
|
||||||
"no root window")
|
|
||||||
self.tk = master.tk
|
self.tk = master.tk
|
||||||
self.stylename = self.tk.call('tixDisplayStyle', itemtype,
|
self.stylename = self.tk.call('tixDisplayStyle', itemtype,
|
||||||
*self._options(cnf,kw) )
|
*self._options(cnf,kw) )
|
||||||
|
|
|
@ -349,12 +349,7 @@ def setup_master(master=None):
|
||||||
If it is not allowed to use the default root and master is None,
|
If it is not allowed to use the default root and master is None,
|
||||||
RuntimeError is raised."""
|
RuntimeError is raised."""
|
||||||
if master is None:
|
if master is None:
|
||||||
if tkinter._support_default_root:
|
master = tkinter._get_default_root()
|
||||||
master = tkinter._default_root or tkinter.Tk()
|
|
||||||
else:
|
|
||||||
raise RuntimeError(
|
|
||||||
"No master specified and tkinter is "
|
|
||||||
"configured to not support default root")
|
|
||||||
return master
|
return master
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
:mod:`tkinter` functions and constructors which need a default root window
|
||||||
|
raise now :exc:`RuntimeError` with descriptive message instead of obscure
|
||||||
|
:exc:`AttributeError` or :exc:`NameError` if it is not created yet or cannot
|
||||||
|
be created automatically.
|
|
@ -36,15 +36,11 @@ class PyncheWidget:
|
||||||
else:
|
else:
|
||||||
# Is there already a default root for Tk, say because we're
|
# Is there already a default root for Tk, say because we're
|
||||||
# running under Guido's IDE? :-) Two conditions say no, either the
|
# running under Guido's IDE? :-) Two conditions say no, either the
|
||||||
# import fails or _default_root is None.
|
# _default_root is None or it is unset.
|
||||||
tkroot = None
|
tkroot = getattr(tkinter, '_default_root', None)
|
||||||
try:
|
|
||||||
from Tkinter import _default_root
|
|
||||||
tkroot = self.__tkroot = _default_root
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
if not tkroot:
|
if not tkroot:
|
||||||
tkroot = self.__tkroot = Tk(className='Pynche')
|
tkroot = Tk(className='Pynche')
|
||||||
|
self.__tkroot = tkroot
|
||||||
# but this isn't our top level widget, so make it invisible
|
# but this isn't our top level widget, so make it invisible
|
||||||
tkroot.withdraw()
|
tkroot.withdraw()
|
||||||
# create the menubar
|
# create the menubar
|
||||||
|
|
Loading…
Reference in New Issue