bpo-32585: Add tkinter.ttk.Spinbox. (#5221)
This commit is contained in:
parent
32921f9082
commit
a48e78a0b7
|
@ -66,13 +66,13 @@ for improved styling effects.
|
||||||
Ttk Widgets
|
Ttk Widgets
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Ttk comes with 17 widgets, eleven of which already existed in tkinter:
|
Ttk comes with 18 widgets, twelve of which already existed in tkinter:
|
||||||
:class:`Button`, :class:`Checkbutton`, :class:`Entry`, :class:`Frame`,
|
:class:`Button`, :class:`Checkbutton`, :class:`Entry`, :class:`Frame`,
|
||||||
:class:`Label`, :class:`LabelFrame`, :class:`Menubutton`, :class:`PanedWindow`,
|
:class:`Label`, :class:`LabelFrame`, :class:`Menubutton`, :class:`PanedWindow`,
|
||||||
:class:`Radiobutton`, :class:`Scale` and :class:`Scrollbar`. The other six are
|
:class:`Radiobutton`, :class:`Scale`, :class:`Scrollbar`, and :class:`Spinbox`.
|
||||||
new: :class:`Combobox`, :class:`Notebook`, :class:`Progressbar`,
|
The other six are new: :class:`Combobox`, :class:`Notebook`,
|
||||||
:class:`Separator`, :class:`Sizegrip` and :class:`Treeview`. And all them are
|
:class:`Progressbar`, :class:`Separator`, :class:`Sizegrip` and
|
||||||
subclasses of :class:`Widget`.
|
:class:`Treeview`. And all them are subclasses of :class:`Widget`.
|
||||||
|
|
||||||
Using the Ttk widgets gives the application an improved look and feel.
|
Using the Ttk widgets gives the application an improved look and feel.
|
||||||
As discussed above, there are differences in how the styling is coded.
|
As discussed above, there are differences in how the styling is coded.
|
||||||
|
@ -381,6 +381,87 @@ ttk.Combobox
|
||||||
Sets the value of the combobox to *value*.
|
Sets the value of the combobox to *value*.
|
||||||
|
|
||||||
|
|
||||||
|
Spinbox
|
||||||
|
-------
|
||||||
|
The :class:`ttk.Spinbox` widget is a :class:`ttk.Entry` enhanced with increment
|
||||||
|
and decrement arrows. It can be used for numbers or lists of string values.
|
||||||
|
This widget is a subclass of :class:`Entry`.
|
||||||
|
|
||||||
|
Besides the methods inherited from :class:`Widget`: :meth:`Widget.cget`,
|
||||||
|
:meth:`Widget.configure`, :meth:`Widget.identify`, :meth:`Widget.instate`
|
||||||
|
and :meth:`Widget.state`, and the following inherited from :class:`Entry`:
|
||||||
|
:meth:`Entry.bbox`, :meth:`Entry.delete`, :meth:`Entry.icursor`,
|
||||||
|
:meth:`Entry.index`, :meth:`Entry.insert`, :meth:`Entry.xview`,
|
||||||
|
it has some other methods, described at :class:`ttk.Spinbox`.
|
||||||
|
|
||||||
|
Options
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
This widget accepts the following specific options:
|
||||||
|
|
||||||
|
.. tabularcolumns:: |l|L|
|
||||||
|
|
||||||
|
+----------------------+------------------------------------------------------+
|
||||||
|
| Option | Description |
|
||||||
|
+======================+======================================================+
|
||||||
|
| from | Float value. If set, this is the minimum value to |
|
||||||
|
| | which the decrement button will decrement. Must be |
|
||||||
|
| | spelled as ``from_`` when used as an argument, since |
|
||||||
|
| | ``from`` is a Python keyword. |
|
||||||
|
+----------------------+------------------------------------------------------+
|
||||||
|
| to | Float value. If set, this is the maximum value to |
|
||||||
|
| | which the increment button will increment. |
|
||||||
|
+----------------------+------------------------------------------------------+
|
||||||
|
| increment | Float value. Specifies the amount which the |
|
||||||
|
| | increment/decrement buttons change the |
|
||||||
|
| | value. Defaults to 1.0. |
|
||||||
|
+----------------------+------------------------------------------------------+
|
||||||
|
| values | Sequence of string or float values. If specified, |
|
||||||
|
| | the increment/decrement buttons will cycle through |
|
||||||
|
| | the items in this sequence rather than incrementing |
|
||||||
|
| | or decrementing numbers. |
|
||||||
|
| | |
|
||||||
|
+----------------------+------------------------------------------------------+
|
||||||
|
| wrap | Boolean value. If ``True``, increment and decrement |
|
||||||
|
| | buttons will cycle from the ``to`` value to the |
|
||||||
|
| | ``from`` value or the ``from`` value to the ``to`` |
|
||||||
|
| | value, respectively. |
|
||||||
|
+----------------------+------------------------------------------------------+
|
||||||
|
| format | String value. This specifies the format of numbers |
|
||||||
|
| | set by the increment/decrement buttons. It must be |
|
||||||
|
| | in the form "%W.Pf", where W is the padded width of |
|
||||||
|
| | the value, P is the precision, and '%' and 'f' are |
|
||||||
|
| | literal. |
|
||||||
|
+----------------------+------------------------------------------------------+
|
||||||
|
| command | Python callable. Will be called with no arguments |
|
||||||
|
| | whenever either of the increment or decrement buttons|
|
||||||
|
| | are pressed. |
|
||||||
|
| | |
|
||||||
|
+----------------------+------------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
Virtual events
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The spinbox widget generates an **<<Increment>>** virtual event when the
|
||||||
|
user presses <Up>, and a **<<Decrement>>** virtual event when the user
|
||||||
|
presses <Down>.
|
||||||
|
|
||||||
|
ttk.Spinbox
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. class:: Spinbox
|
||||||
|
|
||||||
|
.. method:: get()
|
||||||
|
|
||||||
|
Returns the current value of the spinbox.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: set(value)
|
||||||
|
|
||||||
|
Sets the value of the spinbox to *value*.
|
||||||
|
|
||||||
|
|
||||||
Notebook
|
Notebook
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
|
|
@ -663,6 +663,12 @@ Added :attr:`sys.flags.dev_mode` flag for the new development mode.
|
||||||
Deprecated :func:`sys.set_coroutine_wrapper` and
|
Deprecated :func:`sys.set_coroutine_wrapper` and
|
||||||
:func:`sys.get_coroutine_wrapper`.
|
:func:`sys.get_coroutine_wrapper`.
|
||||||
|
|
||||||
|
|
||||||
|
tkinter
|
||||||
|
-------
|
||||||
|
|
||||||
|
Added :class:`tkinter.ttk.Spinbox`.
|
||||||
|
|
||||||
time
|
time
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
|
@ -1105,6 +1105,183 @@ class NotebookTest(AbstractWidgetTest, unittest.TestCase):
|
||||||
self.nb.event_generate('<Alt-a>')
|
self.nb.event_generate('<Alt-a>')
|
||||||
self.assertEqual(self.nb.select(), str(self.child1))
|
self.assertEqual(self.nb.select(), str(self.child1))
|
||||||
|
|
||||||
|
@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests)
|
||||||
|
class SpinboxTest(EntryTest, unittest.TestCase):
|
||||||
|
OPTIONS = (
|
||||||
|
'background', 'class', 'command', 'cursor', 'exportselection',
|
||||||
|
'font', 'foreground', 'format', 'from', 'increment',
|
||||||
|
'invalidcommand', 'justify', 'show', 'state', 'style',
|
||||||
|
'takefocus', 'textvariable', 'to', 'validate', 'validatecommand',
|
||||||
|
'values', 'width', 'wrap', 'xscrollcommand',
|
||||||
|
)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.spin = self.create()
|
||||||
|
self.spin.pack()
|
||||||
|
|
||||||
|
def create(self, **kwargs):
|
||||||
|
return ttk.Spinbox(self.root, **kwargs)
|
||||||
|
|
||||||
|
def _click_increment_arrow(self):
|
||||||
|
width = self.spin.winfo_width()
|
||||||
|
height = self.spin.winfo_height()
|
||||||
|
x = width - 5
|
||||||
|
y = height//2 - 5
|
||||||
|
self.spin.event_generate('<ButtonPress-1>', x=x, y=y)
|
||||||
|
self.spin.event_generate('<ButtonRelease-1>', x=x, y=y)
|
||||||
|
self.spin.update_idletasks()
|
||||||
|
|
||||||
|
def _click_decrement_arrow(self):
|
||||||
|
width = self.spin.winfo_width()
|
||||||
|
height = self.spin.winfo_height()
|
||||||
|
x = width - 5
|
||||||
|
y = height//2 + 4
|
||||||
|
self.spin.event_generate('<ButtonPress-1>', x=x, y=y)
|
||||||
|
self.spin.event_generate('<ButtonRelease-1>', x=x, y=y)
|
||||||
|
self.spin.update_idletasks()
|
||||||
|
|
||||||
|
def test_command(self):
|
||||||
|
success = []
|
||||||
|
|
||||||
|
self.spin['command'] = lambda: success.append(True)
|
||||||
|
self.spin.update()
|
||||||
|
self._click_increment_arrow()
|
||||||
|
self.spin.update()
|
||||||
|
self.assertTrue(success)
|
||||||
|
|
||||||
|
self._click_decrement_arrow()
|
||||||
|
self.assertEqual(len(success), 2)
|
||||||
|
|
||||||
|
# testing postcommand removal
|
||||||
|
self.spin['command'] = ''
|
||||||
|
self.spin.update_idletasks()
|
||||||
|
self._click_increment_arrow()
|
||||||
|
self._click_decrement_arrow()
|
||||||
|
self.spin.update()
|
||||||
|
self.assertEqual(len(success), 2)
|
||||||
|
|
||||||
|
def test_to(self):
|
||||||
|
self.spin['from'] = 0
|
||||||
|
self.spin['to'] = 5
|
||||||
|
self.spin.set(4)
|
||||||
|
self.spin.update()
|
||||||
|
self._click_increment_arrow() # 5
|
||||||
|
|
||||||
|
self.assertEqual(self.spin.get(), '5')
|
||||||
|
|
||||||
|
self._click_increment_arrow() # 5
|
||||||
|
self.assertEqual(self.spin.get(), '5')
|
||||||
|
|
||||||
|
def test_from(self):
|
||||||
|
self.spin['from'] = 1
|
||||||
|
self.spin['to'] = 10
|
||||||
|
self.spin.set(2)
|
||||||
|
self.spin.update()
|
||||||
|
self._click_decrement_arrow() # 1
|
||||||
|
self.assertEqual(self.spin.get(), '1')
|
||||||
|
self._click_decrement_arrow() # 1
|
||||||
|
self.assertEqual(self.spin.get(), '1')
|
||||||
|
|
||||||
|
def test_increment(self):
|
||||||
|
self.spin['from'] = 0
|
||||||
|
self.spin['to'] = 10
|
||||||
|
self.spin['increment'] = 4
|
||||||
|
self.spin.set(1)
|
||||||
|
self.spin.update()
|
||||||
|
|
||||||
|
self._click_increment_arrow() # 5
|
||||||
|
self.assertEqual(self.spin.get(), '5')
|
||||||
|
self.spin['increment'] = 2
|
||||||
|
self.spin.update()
|
||||||
|
self._click_decrement_arrow() # 3
|
||||||
|
self.assertEqual(self.spin.get(), '3')
|
||||||
|
|
||||||
|
def test_format(self):
|
||||||
|
self.spin.set(1)
|
||||||
|
self.spin['format'] = '%10.3f'
|
||||||
|
self.spin.update()
|
||||||
|
self._click_increment_arrow()
|
||||||
|
value = self.spin.get()
|
||||||
|
|
||||||
|
self.assertEqual(len(value), 10)
|
||||||
|
self.assertEqual(value.index('.'), 6)
|
||||||
|
|
||||||
|
self.spin['format'] = ''
|
||||||
|
self.spin.update()
|
||||||
|
self._click_increment_arrow()
|
||||||
|
value = self.spin.get()
|
||||||
|
self.assertTrue('.' not in value)
|
||||||
|
self.assertEqual(len(value), 1)
|
||||||
|
|
||||||
|
def test_wrap(self):
|
||||||
|
self.spin['to'] = 10
|
||||||
|
self.spin['from'] = 1
|
||||||
|
self.spin.set(1)
|
||||||
|
self.spin['wrap'] = True
|
||||||
|
self.spin.update()
|
||||||
|
|
||||||
|
self._click_decrement_arrow()
|
||||||
|
self.assertEqual(self.spin.get(), '10')
|
||||||
|
|
||||||
|
self._click_increment_arrow()
|
||||||
|
self.assertEqual(self.spin.get(), '1')
|
||||||
|
|
||||||
|
self.spin['wrap'] = False
|
||||||
|
self.spin.update()
|
||||||
|
|
||||||
|
self._click_decrement_arrow()
|
||||||
|
self.assertEqual(self.spin.get(), '1')
|
||||||
|
|
||||||
|
def test_values(self):
|
||||||
|
self.assertEqual(self.spin['values'],
|
||||||
|
() if tcl_version < (8, 5) else '')
|
||||||
|
self.checkParam(self.spin, 'values', 'mon tue wed thur',
|
||||||
|
expected=('mon', 'tue', 'wed', 'thur'))
|
||||||
|
self.checkParam(self.spin, 'values', ('mon', 'tue', 'wed', 'thur'))
|
||||||
|
self.checkParam(self.spin, 'values', (42, 3.14, '', 'any string'))
|
||||||
|
self.checkParam(
|
||||||
|
self.spin,
|
||||||
|
'values',
|
||||||
|
'',
|
||||||
|
expected='' if get_tk_patchlevel() < (8, 5, 10) else ()
|
||||||
|
)
|
||||||
|
|
||||||
|
self.spin['values'] = ['a', 1, 'c']
|
||||||
|
|
||||||
|
# test incrementing / decrementing values
|
||||||
|
self.spin.set('a')
|
||||||
|
self.spin.update()
|
||||||
|
self._click_increment_arrow()
|
||||||
|
self.assertEqual(self.spin.get(), '1')
|
||||||
|
|
||||||
|
self._click_decrement_arrow()
|
||||||
|
self.assertEqual(self.spin.get(), 'a')
|
||||||
|
|
||||||
|
# testing values with empty string set through configure
|
||||||
|
self.spin.configure(values=[1, '', 2])
|
||||||
|
self.assertEqual(self.spin['values'],
|
||||||
|
('1', '', '2') if self.wantobjects else
|
||||||
|
'1 {} 2')
|
||||||
|
|
||||||
|
# testing values with spaces
|
||||||
|
self.spin['values'] = ['a b', 'a\tb', 'a\nb']
|
||||||
|
self.assertEqual(self.spin['values'],
|
||||||
|
('a b', 'a\tb', 'a\nb') if self.wantobjects else
|
||||||
|
'{a b} {a\tb} {a\nb}')
|
||||||
|
|
||||||
|
# testing values with special characters
|
||||||
|
self.spin['values'] = [r'a\tb', '"a"', '} {']
|
||||||
|
self.assertEqual(self.spin['values'],
|
||||||
|
(r'a\tb', '"a"', '} {') if self.wantobjects else
|
||||||
|
r'a\\tb {"a"} \}\ \{')
|
||||||
|
|
||||||
|
# testing creating spinbox with empty string in values
|
||||||
|
spin2 = ttk.Spinbox(self.root, values=[1, 2, ''])
|
||||||
|
self.assertEqual(spin2['values'],
|
||||||
|
('1', '2', '') if self.wantobjects else '1 2 {}')
|
||||||
|
spin2.destroy()
|
||||||
|
|
||||||
|
|
||||||
@add_standard_options(StandardTtkOptionsTests)
|
@add_standard_options(StandardTtkOptionsTests)
|
||||||
class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
|
class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
|
||||||
|
@ -1679,7 +1856,7 @@ tests_gui = (
|
||||||
FrameTest, LabelFrameTest, LabelTest, MenubuttonTest,
|
FrameTest, LabelFrameTest, LabelTest, MenubuttonTest,
|
||||||
NotebookTest, PanedWindowTest, ProgressbarTest,
|
NotebookTest, PanedWindowTest, ProgressbarTest,
|
||||||
RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest,
|
RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest,
|
||||||
SizegripTest, TreeviewTest, WidgetTest,
|
SizegripTest, SpinboxTest, TreeviewTest, WidgetTest,
|
||||||
)
|
)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -19,7 +19,7 @@ __author__ = "Guilherme Polo <ggpolo@gmail.com>"
|
||||||
__all__ = ["Button", "Checkbutton", "Combobox", "Entry", "Frame", "Label",
|
__all__ = ["Button", "Checkbutton", "Combobox", "Entry", "Frame", "Label",
|
||||||
"Labelframe", "LabelFrame", "Menubutton", "Notebook", "Panedwindow",
|
"Labelframe", "LabelFrame", "Menubutton", "Notebook", "Panedwindow",
|
||||||
"PanedWindow", "Progressbar", "Radiobutton", "Scale", "Scrollbar",
|
"PanedWindow", "Progressbar", "Radiobutton", "Scale", "Scrollbar",
|
||||||
"Separator", "Sizegrip", "Style", "Treeview",
|
"Separator", "Sizegrip", "Spinbox", "Style", "Treeview",
|
||||||
# Extensions
|
# Extensions
|
||||||
"LabeledScale", "OptionMenu",
|
"LabeledScale", "OptionMenu",
|
||||||
# functions
|
# functions
|
||||||
|
@ -1149,6 +1149,33 @@ class Sizegrip(Widget):
|
||||||
Widget.__init__(self, master, "ttk::sizegrip", kw)
|
Widget.__init__(self, master, "ttk::sizegrip", kw)
|
||||||
|
|
||||||
|
|
||||||
|
class Spinbox(Entry):
|
||||||
|
"""Ttk Spinbox is an Entry with increment and decrement arrows
|
||||||
|
|
||||||
|
It is commonly used for number entry or to select from a list of
|
||||||
|
string values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, master=None, **kw):
|
||||||
|
"""Construct a Ttk Spinbox widget with the parent master.
|
||||||
|
|
||||||
|
STANDARD OPTIONS
|
||||||
|
|
||||||
|
class, cursor, style, takefocus, validate,
|
||||||
|
validatecommand, xscrollcommand, invalidcommand
|
||||||
|
|
||||||
|
WIDGET-SPECIFIC OPTIONS
|
||||||
|
|
||||||
|
to, from_, increment, values, wrap, format, command
|
||||||
|
"""
|
||||||
|
Entry.__init__(self, master, "ttk::spinbox", **kw)
|
||||||
|
|
||||||
|
|
||||||
|
def set(self, value):
|
||||||
|
"""Sets the value of the Spinbox to value."""
|
||||||
|
self.tk.call(self._w, "set", value)
|
||||||
|
|
||||||
|
|
||||||
class Treeview(Widget, tkinter.XView, tkinter.YView):
|
class Treeview(Widget, tkinter.XView, tkinter.YView):
|
||||||
"""Ttk Treeview widget displays a hierarchical collection of items.
|
"""Ttk Treeview widget displays a hierarchical collection of items.
|
||||||
|
|
||||||
|
|
|
@ -1073,6 +1073,7 @@ The Dragon De Monsyne
|
||||||
Bastien Montagne
|
Bastien Montagne
|
||||||
Skip Montanaro
|
Skip Montanaro
|
||||||
Peter Moody
|
Peter Moody
|
||||||
|
Alan D. Moore
|
||||||
Paul Moore
|
Paul Moore
|
||||||
Ross Moore
|
Ross Moore
|
||||||
Ben Morgan
|
Ben Morgan
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Add Ttk spinbox widget to to tkinter.ttk. Patch by Alan D Moore.
|
Loading…
Reference in New Issue