Kaydet (Commit) 5f238485 authored tarafından Guilherme Polo's avatar Guilherme Polo

Merged revisions 69050 via svnmerge from

svn+ssh://pythondev/python/trunk

........
  r69050 | guilherme.polo | 2009-01-28 11:09:03 -0200 (Wed, 28 Jan 2009) | 2 lines

  Added the ttk module. See issue #2983: Ttk support for Tkinter.
........
üst 9844a1ff
......@@ -12,8 +12,8 @@ Graphical User Interfaces with Tk
Tk/Tcl has long been an integral part of Python. It provides a robust and
platform independent windowing toolkit, that is available to Python programmers
using the :mod:`tkinter` package, and its extension, the :mod:`tkinter.tix`
module.
using the :mod:`tkinter` package, and its extension, the :mod:`tkinter.tix` and
the :mod:`tkinter.ttk` modules.
The :mod:`tkinter` package is a thin object-oriented layer on top of Tcl/Tk. To
use :mod:`tkinter`, you don't need to write Tcl code, but you will need to
......@@ -33,6 +33,7 @@ alternatives, see the :ref:`other-gui-packages` section.
.. toctree::
tkinter.rst
tkinter.ttk.rst
tkinter.tix.rst
tkinter.scrolledtext.rst
turtle.rst
......
This diff is collapsed.
from test import support
from tkinter.test import runtktests
def test_main(enable_gui=False):
if enable_gui:
if support.use_resources is None:
support.use_resources = ['gui']
elif 'gui' not in support.use_resources:
support.use_resources.append('gui')
support.run_unittest(*runtktests.get_tests(text=False))
if __name__ == '__main__':
test_main(enable_gui=True)
from test import support
from tkinter.test import runtktests
def test_main():
support.run_unittest(*runtktests.get_tests(gui=False))
if __name__ == '__main__':
test_main()
Writing new tests
=================
Precaution
----------
New tests should always use only one Tk window at once, like all the
current tests do. This means that you have to destroy the current window
before creating another one, and clean up after the test. The motivation
behind this is that some tests may depend on having its window focused
while it is running to work properly, and it may be hard to force focus
on your window across platforms (right now only test_traversal at
test_ttk.test_widgets.NotebookTest depends on this).
"""
Use this module to get and run all tk tests.
tkinter tests should live in a package inside the directory where this file
lives, like test_tkinter.
Extensions also should live in packages following the same rule as above.
"""
import os
import sys
import unittest
import test.support
this_dir_path = os.path.abspath(os.path.dirname(__file__))
def is_package(path):
for name in os.listdir(path):
if name in ('__init__.py', '__init__.pyc', '__init.pyo'):
return True
return False
def get_tests_modules(basepath=this_dir_path, gui=True):
"""This will import and yield modules whose names start with test_
and are inside packages found in the path starting at basepath."""
py_ext = '.py'
for dirpath, dirnames, filenames in os.walk(basepath):
for dirname in list(dirnames):
if dirname[0] == '.':
dirnames.remove(dirname)
if is_package(dirpath) and filenames:
pkg_name = dirpath[len(basepath) + len(os.sep):].replace('/', '.')
filenames = filter(
lambda x: x.startswith('test_') and x.endswith(py_ext),
filenames)
for name in filenames:
try:
yield __import__(
"%s.%s.%s" % (
"tkinter.test",
pkg_name,
name[:-len(py_ext)]),
fromlist=['']
)
except test.support.ResourceDenied:
if gui:
raise
def get_tests(text=True, gui=True):
"""Yield all the tests in the modules found by get_tests_modules.
If nogui is True, only tests that do not require a GUI will be
returned."""
attrs = []
if text:
attrs.append('tests_nogui')
if gui:
attrs.append('tests_gui')
for module in get_tests_modules(gui=gui):
for attr in attrs:
for test in getattr(module, attr, ()):
yield test
if __name__ == "__main__":
test.support.use_resources = ['gui']
test.support.run_unittest(*get_tests())
import tkinter
def get_tk_root():
try:
root = tkinter._default_root
except AttributeError:
# it is possible to disable default root in Tkinter, although
# I haven't seen people doing it (but apparently someone did it
# here).
root = None
if root is None:
# create a new master only if there isn't one already
root = tkinter.Tk()
return root
def simulate_mouse_click(widget, x, y):
"""Generate proper events to click at the x, y position (tries to act
like an X server)."""
widget.event_generate('<Enter>', x=0, y=0)
widget.event_generate('<Motion>', x=x, y=y)
widget.event_generate('<ButtonPress-1>', x=x, y=y)
widget.event_generate('<ButtonRelease-1>', x=x, y=y)
import sys
import unittest
import tkinter
from tkinter import ttk
from test.support import requires, run_unittest
import tkinter.test.support as support
requires('gui')
class LabeledScaleTest(unittest.TestCase):
def test_widget_destroy(self):
# automatically created variable
x = ttk.LabeledScale()
var = x._variable._name
x.destroy()
self.failUnlessRaises(tkinter.TclError, x.tk.globalgetvar, var)
# manually created variable
myvar = tkinter.DoubleVar()
name = myvar._name
x = ttk.LabeledScale(variable=myvar)
x.destroy()
self.failUnlessEqual(x.tk.globalgetvar(name), myvar.get())
del myvar
self.failUnlessRaises(tkinter.TclError, x.tk.globalgetvar, name)
# checking that the tracing callback is properly removed
myvar = tkinter.IntVar()
# LabeledScale will start tracing myvar
x = ttk.LabeledScale(variable=myvar)
x.destroy()
# Unless the tracing callback was removed, creating a new
# LabeledScale with the same var will cause an error now. This
# happens because the variable will be set to (possibly) a new
# value which causes the tracing callback to be called and then
# it tries calling instance attributes not yet defined.
ttk.LabeledScale(variable=myvar)
if hasattr(sys, 'last_type'):
self.failIf(sys.last_type == tkinter.TclError)
def test_initialization(self):
# master passing
x = ttk.LabeledScale()
self.failUnlessEqual(x.master, tkinter._default_root)
x.destroy()
master = tkinter.Frame()
x = ttk.LabeledScale(master)
self.failUnlessEqual(x.master, master)
x.destroy()
# variable initialization/passing
passed_expected = ((2.5, 2), ('0', 0), (0, 0), (10, 10),
(-1, -1), (sys.maxsize + 1, sys.maxsize + 1))
for pair in passed_expected:
x = ttk.LabeledScale(from_=pair[0])
self.failUnlessEqual(x.value, pair[1])
x.destroy()
x = ttk.LabeledScale(from_='2.5')
self.failUnlessRaises(ValueError, x._variable.get)
x.destroy()
x = ttk.LabeledScale(from_=None)
self.failUnlessRaises(ValueError, x._variable.get)
x.destroy()
# variable should have its default value set to the from_ value
myvar = tkinter.DoubleVar(value=20)
x = ttk.LabeledScale(variable=myvar)
self.failUnlessEqual(x.value, 0)
x.destroy()
# check that it is really using a DoubleVar
x = ttk.LabeledScale(variable=myvar, from_=0.5)
self.failUnlessEqual(x.value, 0.5)
self.failUnlessEqual(x._variable._name, myvar._name)
x.destroy()
# widget positionment
def check_positions(scale, scale_pos, label, label_pos):
self.failUnlessEqual(scale.pack_info()['side'], scale_pos)
self.failUnlessEqual(label.place_info()['anchor'], label_pos)
x = ttk.LabeledScale(compound='top')
check_positions(x.scale, 'bottom', x.label, 'n')
x.destroy()
x = ttk.LabeledScale(compound='bottom')
check_positions(x.scale, 'top', x.label, 's')
x.destroy()
x = ttk.LabeledScale(compound='unknown') # invert default positions
check_positions(x.scale, 'top', x.label, 's')
x.destroy()
x = ttk.LabeledScale() # take default positions
check_positions(x.scale, 'bottom', x.label, 'n')
x.destroy()
# extra, and invalid, kwargs
self.failUnlessRaises(tkinter.TclError, ttk.LabeledScale, a='b')
def test_horizontal_range(self):
lscale = ttk.LabeledScale(from_=0, to=10)
lscale.pack()
lscale.wait_visibility()
lscale.update()
linfo_1 = lscale.label.place_info()
prev_xcoord = lscale.scale.coords()[0]
self.failUnlessEqual(prev_xcoord, int(linfo_1['x']))
# change range to: from -5 to 5. This should change the x coord of
# the scale widget, since 0 is at the middle of the new
# range.
lscale.scale.configure(from_=-5, to=5)
# The following update is needed since the test doesn't use mainloop,
# at the same time this shouldn't affect test outcome
lscale.update()
curr_xcoord = lscale.scale.coords()[0]
self.failUnless(prev_xcoord != curr_xcoord)
# the label widget should have been repositioned too
linfo_2 = lscale.label.place_info()
self.failUnlessEqual(lscale.label['text'], 0)
self.failUnlessEqual(curr_xcoord, int(linfo_2['x']))
# change the range back
lscale.scale.configure(from_=0, to=10)
self.failUnless(prev_xcoord != curr_xcoord)
self.failUnlessEqual(prev_xcoord, int(linfo_1['x']))
lscale.destroy()
def test_variable_change(self):
x = ttk.LabeledScale()
x.pack()
x.wait_visibility()
x.update()
curr_xcoord = x.scale.coords()[0]
newval = x.value + 1
x.value = newval
# The following update is needed since the test doesn't use mainloop,
# at the same time this shouldn't affect test outcome
x.update()
self.failUnlessEqual(x.label['text'], newval)
self.failUnless(x.scale.coords()[0] > curr_xcoord)
self.failUnlessEqual(x.scale.coords()[0],
int(x.label.place_info()['x']))
# value outside range
x.value = x.scale['to'] + 1 # no changes shouldn't happen
x.update()
self.failUnlessEqual(x.label['text'], newval)
self.failUnlessEqual(x.scale.coords()[0],
int(x.label.place_info()['x']))
x.destroy()
def test_resize(self):
x = ttk.LabeledScale()
x.pack(expand=True, fill='both')
x.wait_visibility()
x.update()
width, height = x.master.winfo_width(), x.master.winfo_height()
width, height = width * 2, height * 2
x.value = 3
x.update()
x.master.wm_geometry("%dx%d" % (width, height))
self.failUnlessEqual(int(x.label.place_info()['x']),
x.scale.coords()[0])
x.master.wm_geometry("%dx%d" % (width, height))
x.destroy()
class OptionMenuTest(unittest.TestCase):
def setUp(self):
self.root = support.get_tk_root()
self.textvar = tkinter.StringVar(self.root)
def tearDown(self):
del self.textvar
self.root.destroy()
def test_widget_destroy(self):
var = tkinter.StringVar()
optmenu = ttk.OptionMenu(None, var)
name = var._name
optmenu.update_idletasks()
optmenu.destroy()
self.failUnlessEqual(optmenu.tk.globalgetvar(name), var.get())
del var
self.failUnlessRaises(tkinter.TclError, optmenu.tk.globalgetvar, name)
def test_initialization(self):
self.failUnlessRaises(tkinter.TclError,
ttk.OptionMenu, None, self.textvar, invalid='thing')
optmenu = ttk.OptionMenu(None, self.textvar, 'b', 'a', 'b')
self.failUnlessEqual(optmenu._variable.get(), 'b')
self.failUnless(optmenu['menu'])
self.failUnless(optmenu['textvariable'])
optmenu.destroy()
def test_menu(self):
items = ('a', 'b', 'c')
default = 'a'
optmenu = ttk.OptionMenu(None, self.textvar, default, *items)
found_default = False
for i in range(len(items)):
value = optmenu['menu'].entrycget(i, 'value')
self.failUnlessEqual(value, items[i])
if value == default:
found_default = True
self.failUnless(found_default)
optmenu.destroy()
# default shouldn't be in menu if it is not part of values
default = 'd'
optmenu = ttk.OptionMenu(None, self.textvar, default, *items)
curr = None
i = 0
while True:
last, curr = curr, optmenu['menu'].entryconfigure(i, 'value')
if last == curr:
# no more menu entries
break
self.failIf(curr == default)
i += 1
self.failUnlessEqual(i, len(items))
# check that variable is updated correctly
optmenu.pack()
optmenu.wait_visibility()
optmenu['menu'].invoke(0)
self.failUnlessEqual(optmenu._variable.get(), items[0])
# changing to an invalid index shouldn't change the variable
self.failUnlessRaises(tkinter.TclError, optmenu['menu'].invoke, -1)
self.failUnlessEqual(optmenu._variable.get(), items[0])
optmenu.destroy()
# specifying a callback
success = []
def cb_test(item):
self.failUnlessEqual(item, items[1])
success.append(True)
optmenu = ttk.OptionMenu(None, self.textvar, 'a', command=cb_test,
*items)
optmenu['menu'].invoke(1)
if not success:
self.fail("Menu callback not invoked")
optmenu.destroy()
tests_gui = (LabeledScaleTest, OptionMenuTest)
if __name__ == "__main__":
run_unittest(*tests_gui)
This diff is collapsed.
import unittest
import tkinter
from tkinter import ttk
from test.support import requires, run_unittest
import tkinter.test.support as support
requires('gui')
class StyleTest(unittest.TestCase):
def setUp(self):
self.root = support.get_tk_root()
self.style = ttk.Style(self.root)
def tearDown(self):
# As tests have shown, these tests are likely to deliver
# <<ThemeChanged>> events after the root is destroyed, so
# lets let them happen now.
self.root.update_idletasks()
self.root.destroy()
def test_configure(self):
style = self.style
style.configure('TButton', background='yellow')
self.failUnlessEqual(style.configure('TButton', 'background'),
'yellow')
self.failUnless(isinstance(style.configure('TButton'), dict))
def test_map(self):
style = self.style
style.map('TButton', background=[('active', 'background', 'blue')])
self.failUnlessEqual(style.map('TButton', 'background'),
[('active', 'background', 'blue')])
self.failUnless(isinstance(style.map('TButton'), dict))
def test_lookup(self):
style = self.style
style.configure('TButton', background='yellow')
style.map('TButton', background=[('active', 'background', 'blue')])
self.failUnlessEqual(style.lookup('TButton', 'background'), 'yellow')
self.failUnlessEqual(style.lookup('TButton', 'background',
['active', 'background']), 'blue')
self.failUnlessEqual(style.lookup('TButton', 'optionnotdefined',
default='iknewit'), 'iknewit')
def test_layout(self):
style = self.style
self.failUnlessRaises(tkinter.TclError, style.layout, 'NotALayout')
tv_style = style.layout('Treeview')
# "erase" Treeview layout
style.layout('Treeview', '')
self.failUnlessEqual(style.layout('Treeview'),
[('null', {'sticky': 'nswe'})]
)
# restore layout
style.layout('Treeview', tv_style)
self.failUnlessEqual(style.layout('Treeview'), tv_style)
# should return a list
self.failUnless(isinstance(style.layout('TButton'), list))
# correct layout, but "option" doesn't exist as option
self.failUnlessRaises(tkinter.TclError, style.layout, 'Treeview',
[('name', {'option': 'inexistant'})])
def test_theme_use(self):
self.failUnlessRaises(tkinter.TclError, self.style.theme_use,
'nonexistingname')
curr_theme = self.style.theme_use()
new_theme = None
for theme in self.style.theme_names():
if theme != curr_theme:
new_theme = theme
self.style.theme_use(theme)
break
else:
# just one theme available, can't go on with tests
return
self.failIf(curr_theme == new_theme)
self.failIf(new_theme != self.style.theme_use())
self.style.theme_use(curr_theme)
tests_gui = (StyleTest, )
if __name__ == "__main__":
run_unittest(*tests_gui)
This diff is collapsed.
This diff is collapsed.
......@@ -144,6 +144,8 @@ Core and Builtins
Library
-------
- Added the ttk module. See issue #2983: Ttk support for Tkinter.
- Removed isSequenceType(), isMappingType, and isNumberType() from the
operator module; use the abstract base classes instead. Also removed
the repeat() function; use mul() instead.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment