[Python-checkins] r66033 - in sandbox/trunk/ttk-gsoc/src: 2.x/test/test_extensions.py 2.x/ttk.py 3.x/test/test_extensions.py 3.x/ttk.py
guilherme.polo
python-checkins at python.org
Mon Aug 25 22:23:58 CEST 2008
Author: guilherme.polo
Date: Mon Aug 25 22:23:57 2008
New Revision: 66033
Log:
ttk.Scale now emits a <<RangedChanged>> event when its range is changed;
Assorted fixes in LabeledScale, tests added.
Modified:
sandbox/trunk/ttk-gsoc/src/2.x/test/test_extensions.py
sandbox/trunk/ttk-gsoc/src/2.x/ttk.py
sandbox/trunk/ttk-gsoc/src/3.x/test/test_extensions.py
sandbox/trunk/ttk-gsoc/src/3.x/ttk.py
Modified: sandbox/trunk/ttk-gsoc/src/2.x/test/test_extensions.py
==============================================================================
--- sandbox/trunk/ttk-gsoc/src/2.x/test/test_extensions.py (original)
+++ sandbox/trunk/ttk-gsoc/src/2.x/test/test_extensions.py Mon Aug 25 22:23:57 2008
@@ -6,14 +6,6 @@
import support
class LabeledScaleTest(unittest.TestCase):
- # XXX
-
-# def setUp(self):
-# self.lscale = ttk.LabeledScale()
-
-# def tearDown(self):
-# self.lscale.destroy()
-
def test_widget_destroy(self):
# automatically created variable
@@ -59,18 +51,99 @@
self.failUnlessEqual(x.value, 0.5)
self.failUnlessEqual(x._variable._name, myvar._name)
+ # 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 = ttk.LabeledScale(compound='bottom')
+ check_positions(x.scale, 'top', x.label, 's')
+ x = ttk.LabeledScale(compound='unknown') # invert default positions
+ check_positions(x.scale, 'top', x.label, 's')
+ x = ttk.LabeledScale() # take default positions
+ check_positions(x.scale, 'bottom', x.label, 'n')
+
# extra, and invalid, kwargs
self.failUnlessRaises(Tkinter.TclError, ttk.LabeledScale, a='b')
+ x.destroy()
+
+
+ def test_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])
-# def test_ranges(self): # XXX finish this
-# self.lscale.pack()
-# self.lscale.wait_visibility()
-# self.lscale.update()
-
-# # test decreasing range
-# self.lscale.scale.configure(from_=1, to=-1)
-# self.failUnlessEqual(self.lscale.label['text'], 1)
+ x.master.wm_geometry("%dx%d" % (width, height))
+ x.destroy()
class OptionMenuTest(unittest.TestCase):
@@ -104,6 +177,8 @@
self.failUnless(optmenu['menu'])
self.failUnless(optmenu['textvariable'])
+ optmenu.destroy()
+
def test_menu(self):
items = ('a', 'b', 'c')
@@ -152,6 +227,7 @@
if not success:
self.fail("Menu callback not invoked")
+ optmenu.destroy()
def test_main():
Modified: sandbox/trunk/ttk-gsoc/src/2.x/ttk.py
==============================================================================
--- sandbox/trunk/ttk-gsoc/src/2.x/ttk.py (original)
+++ sandbox/trunk/ttk-gsoc/src/2.x/ttk.py Mon Aug 25 22:23:57 2008
@@ -1053,6 +1053,16 @@
Widget.__init__(self, master, "ttk::scale", kw)
+ def configure(self, **kw):
+ """Modify or query scale options.
+
+ Changing "from", "from_" or "to" options generates a
+ <<RangeChanged>> event."""
+ Widget.configure(self, **kw)
+ if any(['from' in kw, 'from_' in kw, 'to' in kw]):
+ self.event_generate('<<RangeChanged>>')
+
+
def get(self, x=None, y=None):
"""Get the current value of the value option, or the value
corresponding to the coordinates x, y if they are specified.
@@ -1436,9 +1446,9 @@
can be accessed through instance.label"""
def __init__(self, master=None, variable=None, from_=0, to=10, **kw):
- """Construct a LabeledScale with parent master, a variable to be
- associated with the Ttk Scale widget and its range. If variable is
- not specified, a Tkinter.IntVar is created.
+ """Construct an horizontal LabeledScale with parent master, a
+ variable to be associated with the Ttk Scale widget and its range.
+ If variable is not specified, a Tkinter.IntVar is created.
WIDGET-SPECIFIC OPTIONS
@@ -1450,9 +1460,11 @@
Frame.__init__(self, master, **kw)
self._variable = variable or Tkinter.IntVar(master)
self._variable.set(from_)
+ self._last_valid = from_
self.label = Label(self)
self.scale = Scale(self, variable=self._variable, from_=from_, to=to)
+ self.scale.bind('<<RangeChanged>>', self._adjust)
# position scale and label according to the compound option
scale_side = 'bottom' if self._label_top else 'top'
@@ -1463,8 +1475,8 @@
# update the label as scale or variable changes
self._variable.trace_variable('w', self._adjust)
- self.scale.bind('<Configure>', self._adjust)
- self.scale.bind('<Map>', self._adjust)
+ self.bind('<Configure>', self._adjust)
+ self.bind('<Map>', self._adjust)
def destroy(self):
@@ -1475,24 +1487,29 @@
def _adjust(self, *args):
"""Adjust the label position according to the scale."""
+ def adjust_label():
+ self.update_idletasks() # "force" scale redraw
+
+ x, y = self.scale.coords()
+ if self._label_top:
+ y = self.scale.winfo_y() - self.label.winfo_reqheight()
+ else:
+ y = self.scale.winfo_reqheight() + self.label.winfo_reqheight()
+
+ self.label.place_configure(x=x, y=y)
+
from_, to = self.scale['from'], self.scale['to']
if to < from_:
from_, to = to, from_
newval = self._variable.get()
if not from_ <= newval <= to:
+ # value outside range, set value back to the last valid one
+ self.value = self._last_valid
return
- self.update()
+ self._last_valid = newval
self.label['text'] = newval
-
- x, y = self.scale.coords()
- if self._label_top:
- y = self.scale.winfo_y() - self.label.winfo_reqheight()
- else:
- y = self.scale.winfo_reqheight() + self.label.winfo_reqheight()
- x = x + self.scale.winfo_x()
-
- self.label.place_configure(x=x, y=y)
+ self.after_idle(adjust_label)
def _get_value(self):
@@ -1516,7 +1533,9 @@
"""Construct a themed OptionMenu widget with master as the parent,
the resource textvariable set to variable, the initially selected
value specified by the default parameter, the menu values given by
- *values and additional keywords:
+ *values and additional keywords.
+
+ WIDGET-SPECIFIC OPTIONS
style: stylename
Menubutton style.
Modified: sandbox/trunk/ttk-gsoc/src/3.x/test/test_extensions.py
==============================================================================
--- sandbox/trunk/ttk-gsoc/src/3.x/test/test_extensions.py (original)
+++ sandbox/trunk/ttk-gsoc/src/3.x/test/test_extensions.py Mon Aug 25 22:23:57 2008
@@ -6,14 +6,6 @@
import support
class LabeledScaleTest(unittest.TestCase):
- # XXX
-
-# def setUp(self):
-# self.lscale = ttk.LabeledScale()
-
-# def tearDown(self):
-# self.lscale.destroy()
-
def test_widget_destroy(self):
# automatically created variable
@@ -59,18 +51,99 @@
self.failUnlessEqual(x.value, 0.5)
self.failUnlessEqual(x._variable._name, myvar._name)
+ # 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 = ttk.LabeledScale(compound='bottom')
+ check_positions(x.scale, 'top', x.label, 's')
+ x = ttk.LabeledScale(compound='unknown') # invert default positions
+ check_positions(x.scale, 'top', x.label, 's')
+ x = ttk.LabeledScale() # take default positions
+ check_positions(x.scale, 'bottom', x.label, 'n')
+
# extra, and invalid, kwargs
self.failUnlessRaises(tkinter.TclError, ttk.LabeledScale, a='b')
+ x.destroy()
+
+
+ def test_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])
-# def test_ranges(self): # XXX finish this
-# self.lscale.pack()
-# self.lscale.wait_visibility()
-# self.lscale.update()
-
-# # test decreasing range
-# self.lscale.scale.configure(from_=1, to=-1)
-# self.failUnlessEqual(self.lscale.label['text'], 1)
+ x.master.wm_geometry("%dx%d" % (width, height))
+ x.destroy()
class OptionMenuTest(unittest.TestCase):
@@ -104,6 +177,8 @@
self.failUnless(optmenu['menu'])
self.failUnless(optmenu['textvariable'])
+ optmenu.destroy()
+
def test_menu(self):
items = ('a', 'b', 'c')
@@ -152,6 +227,7 @@
if not success:
self.fail("Menu callback not invoked")
+ optmenu.destroy()
def test_main():
Modified: sandbox/trunk/ttk-gsoc/src/3.x/ttk.py
==============================================================================
--- sandbox/trunk/ttk-gsoc/src/3.x/ttk.py (original)
+++ sandbox/trunk/ttk-gsoc/src/3.x/ttk.py Mon Aug 25 22:23:57 2008
@@ -1053,6 +1053,16 @@
Widget.__init__(self, master, "ttk::scale", kw)
+ def configure(self, **kw):
+ """Modify or query scale options.
+
+ Changing "from", "from_" or "to" options generates a
+ <<RangeChanged>> event."""
+ Widget.configure(self, **kw)
+ if any(['from' in kw, 'from_' in kw, 'to' in kw]):
+ self.event_generate('<<RangeChanged>>')
+
+
def get(self, x=None, y=None):
"""Get the current value of the value option, or the value
corresponding to the coordinates x, y if they are specified.
@@ -1436,9 +1446,9 @@
can be accessed through instance.label"""
def __init__(self, master=None, variable=None, from_=0, to=10, **kw):
- """Construct a LabeledScale with parent master, a variable to be
- associated with the Ttk Scale widget and its range. If variable is
- not specified, a tkinter.IntVar is created.
+ """Construct an horizontal LabeledScale with parent master, a
+ variable to be associated with the Ttk Scale widget and its range.
+ If variable is not specified, a tkinter.IntVar is created.
WIDGET-SPECIFIC OPTIONS
@@ -1450,9 +1460,11 @@
Frame.__init__(self, master, **kw)
self._variable = variable or tkinter.IntVar(master)
self._variable.set(from_)
+ self._last_valid = from_
self.label = Label(self)
self.scale = Scale(self, variable=self._variable, from_=from_, to=to)
+ self.scale.bind('<<RangeChanged>>', self._adjust)
# position scale and label according to the compound option
scale_side = 'bottom' if self._label_top else 'top'
@@ -1463,8 +1475,8 @@
# update the label as scale or variable changes
self._variable.trace_variable('w', self._adjust)
- self.scale.bind('<Configure>', self._adjust)
- self.scale.bind('<Map>', self._adjust)
+ self.bind('<Configure>', self._adjust)
+ self.bind('<Map>', self._adjust)
def destroy(self):
@@ -1475,24 +1487,29 @@
def _adjust(self, *args):
"""Adjust the label position according to the scale."""
+ def adjust_label():
+ self.update_idletasks() # "force" scale redraw
+
+ x, y = self.scale.coords()
+ if self._label_top:
+ y = self.scale.winfo_y() - self.label.winfo_reqheight()
+ else:
+ y = self.scale.winfo_reqheight() + self.label.winfo_reqheight()
+
+ self.label.place_configure(x=x, y=y)
+
from_, to = self.scale['from'], self.scale['to']
if to < from_:
from_, to = to, from_
newval = self._variable.get()
if not from_ <= newval <= to:
+ # value outside range, set value back to the last valid one
+ self.value = self._last_valid
return
- self.update()
+ self._last_valid = newval
self.label['text'] = newval
-
- x, y = self.scale.coords()
- if self._label_top:
- y = self.scale.winfo_y() - self.label.winfo_reqheight()
- else:
- y = self.scale.winfo_reqheight() + self.label.winfo_reqheight()
- x = x + self.scale.winfo_x()
-
- self.label.place_configure(x=x, y=y)
+ self.after_idle(adjust_label)
def _get_value(self):
@@ -1516,7 +1533,9 @@
"""Construct a themed OptionMenu widget with master as the parent,
the resource textvariable set to variable, the initially selected
value specified by the default parameter, the menu values given by
- *values and additional keywords:
+ *values and additional keywords.
+
+ WIDGET-SPECIFIC OPTIONS
style: stylename
Menubutton style.
More information about the Python-checkins
mailing list