mirror of https://github.com/python/cpython
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 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:`Label`, :class:`LabelFrame`, :class:`Menubutton`, :class:`PanedWindow`,
|
||||
:class:`Radiobutton`, :class:`Scale` and :class:`Scrollbar`. The other six are
|
||||
new: :class:`Combobox`, :class:`Notebook`, :class:`Progressbar`,
|
||||
:class:`Separator`, :class:`Sizegrip` and :class:`Treeview`. And all them are
|
||||
subclasses of :class:`Widget`.
|
||||
:class:`Radiobutton`, :class:`Scale`, :class:`Scrollbar`, and :class:`Spinbox`.
|
||||
The other six are new: :class:`Combobox`, :class:`Notebook`,
|
||||
:class:`Progressbar`, :class:`Separator`, :class:`Sizegrip` and
|
||||
:class:`Treeview`. And all them are subclasses of :class:`Widget`.
|
||||
|
||||
Using the Ttk widgets gives the application an improved look and feel.
|
||||
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*.
|
||||
|
||||
|
||||
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
|
||||
--------
|
||||
|
||||
|
|
|
@ -663,6 +663,12 @@ Added :attr:`sys.flags.dev_mode` flag for the new development mode.
|
|||
Deprecated :func:`sys.set_coroutine_wrapper` and
|
||||
:func:`sys.get_coroutine_wrapper`.
|
||||
|
||||
|
||||
tkinter
|
||||
-------
|
||||
|
||||
Added :class:`tkinter.ttk.Spinbox`.
|
||||
|
||||
time
|
||||
----
|
||||
|
||||
|
|
|
@ -1105,6 +1105,183 @@ class NotebookTest(AbstractWidgetTest, unittest.TestCase):
|
|||
self.nb.event_generate('<Alt-a>')
|
||||
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)
|
||||
class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
|
||||
|
@ -1679,7 +1856,7 @@ tests_gui = (
|
|||
FrameTest, LabelFrameTest, LabelTest, MenubuttonTest,
|
||||
NotebookTest, PanedWindowTest, ProgressbarTest,
|
||||
RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest,
|
||||
SizegripTest, TreeviewTest, WidgetTest,
|
||||
SizegripTest, SpinboxTest, TreeviewTest, WidgetTest,
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -19,7 +19,7 @@ __author__ = "Guilherme Polo <ggpolo@gmail.com>"
|
|||
__all__ = ["Button", "Checkbutton", "Combobox", "Entry", "Frame", "Label",
|
||||
"Labelframe", "LabelFrame", "Menubutton", "Notebook", "Panedwindow",
|
||||
"PanedWindow", "Progressbar", "Radiobutton", "Scale", "Scrollbar",
|
||||
"Separator", "Sizegrip", "Style", "Treeview",
|
||||
"Separator", "Sizegrip", "Spinbox", "Style", "Treeview",
|
||||
# Extensions
|
||||
"LabeledScale", "OptionMenu",
|
||||
# functions
|
||||
|
@ -1149,6 +1149,33 @@ class Sizegrip(Widget):
|
|||
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):
|
||||
"""Ttk Treeview widget displays a hierarchical collection of items.
|
||||
|
||||
|
|
|
@ -1073,6 +1073,7 @@ The Dragon De Monsyne
|
|||
Bastien Montagne
|
||||
Skip Montanaro
|
||||
Peter Moody
|
||||
Alan D. Moore
|
||||
Paul Moore
|
||||
Ross Moore
|
||||
Ben Morgan
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Add Ttk spinbox widget to to tkinter.ttk. Patch by Alan D Moore.
|
Loading…
Reference in New Issue