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.
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
--------
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):
FrameTest, LabelFrameTest, LabelTest, MenubuttonTest,
NotebookTest, PanedWindowTest, ProgressbarTest,
RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest,
- SizegripTest, TreeviewTest, WidgetTest,
+ SizegripTest, SpinboxTest, TreeviewTest, WidgetTest,
)
if __name__ == "__main__":
__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
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.