Skip to content

Commit 8bb376b

Browse files
Adding SmartListBox, with docstrings, docs, and examples. Bumping version.
1 parent 7332912 commit 8bb376b

5 files changed

Lines changed: 135 additions & 5 deletions

File tree

docs/smart_widgets.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ Smart widgets consist of existing widgets with improved API. In most cases, the
2121
.. autoclass:: widgets.SmartCheckbutton
2222
:members:
2323

24+
``SmartListBox``
25+
--------------------
26+
27+
.. autoclass:: widgets.SmartListBox
28+
:members:
29+
2430
``BinaryLabel``
2531
--------------------
2632

examples/smart_listbox.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import tkinter as tk
2+
import tk_tools
3+
4+
5+
if __name__ == '__main__':
6+
root = tk.Tk()
7+
8+
tk.Label(root, text="The variable value: ").grid(row=0, column=0)
9+
value_label = tk.Label(root, text="")
10+
value_label.grid(row=0, column=1)
11+
12+
def callback(values):
13+
string = ''.join(values)
14+
value_label.config(text=string)
15+
16+
# specify a callback, then specify the normal spinbox options (such as "from_", "to", and "increment"
17+
tk.Label(root, text='selectmode="browse"').grid(row=1, column=0)
18+
tk_tools.SmartListBox(root, on_select_callback=callback, selectmode='browse',
19+
options=['1', '2', '3']).grid(row=2, column=0)
20+
tk.Label(root, text='selectmode="multiple"').grid(row=1, column=0)
21+
tk_tools.SmartListBox(root, on_select_callback=callback, selectmode='multiple',
22+
options=['a', 'b', 'c']).grid(row=4, column=0)
23+
root.mainloop()

tk_tools/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
KeyValueEntry, ButtonGrid, Calendar, MultiSlotFrame, \
44
SevenSegment, SevenSegmentDigits
55
from tk_tools.widgets import SmartOptionMenu, SmartSpinBox, \
6-
SmartCheckbutton, BinaryLabel, ByteLabel
6+
SmartCheckbutton, SmartListBox, BinaryLabel, ByteLabel
77
from tk_tools.tooltips import ToolTip
88

99
from tk_tools.version import __version__
@@ -12,8 +12,8 @@
1212
__all__ = [
1313
'RotaryScale', 'Gauge', 'Graph', 'Led',
1414
'EntryGrid', 'LabelGrid', 'ButtonGrid', 'KeyValueEntry',
15-
'SmartOptionMenu', 'SmartSpinBox',
16-
'SmartCheckbutton', 'Calendar', 'MultiSlotFrame',
15+
'SmartOptionMenu', 'SmartSpinBox', 'SmartCheckbutton',
16+
'SmartListBox', 'Calendar', 'MultiSlotFrame',
1717
'SevenSegment', 'SevenSegmentDigits',
1818
'BinaryLabel', 'ByteLabel', 'ToolTip',
1919
'__version__'

tk_tools/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.12.1'
1+
__version__ = '0.13.0'

tk_tools/widgets.py

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import logging
12
import tkinter as tk
23
import tkinter.ttk as ttk
3-
import logging
4+
from typing import List
5+
46

57
logger = logging.getLogger(__name__)
68

@@ -180,6 +182,105 @@ def internal_callback(*args):
180182
self._var.trace('w', internal_callback)
181183

182184

185+
class SmartListBox(SmartWidget):
186+
"""
187+
Easy-to-use List Box. Takes most options that work with
188+
a normal CheckButton. Attempts to call your callback
189+
function - if assigned - whenever there is a change to
190+
the list box selections.::
191+
192+
# create the smart spinbox and grid
193+
scb = SmartListBox(root, options=['one', 'two', 'three'])
194+
scb.grid()
195+
196+
# define a callback function that retrieves
197+
# the currently selected option
198+
def callback():
199+
print(scb.get_selected())
200+
201+
# add the callback function to the checkbutton
202+
scb.add_callback(callback)
203+
204+
:param parent: the tk parent frame
205+
:param options: any options that are valid for tkinter.Checkbutton
206+
:param on_select_callback: python callable
207+
:param selectmode: the selector mode (supports "browse" and "multiple")
208+
"""
209+
def __init__(self, parent, options: List[str],
210+
width: int = 12, height: int = 5,
211+
on_select_callback: callable = None,
212+
selectmode: str = 'browse'):
213+
super().__init__(parent=parent)
214+
215+
self._on_select_callback = on_select_callback
216+
self._values = {}
217+
218+
r = 0
219+
self._lb = tk.Listbox(self, width=width, height=height, selectmode=selectmode, exportselection=0)
220+
self._lb.grid(row=r, column=0, sticky='ew')
221+
[self._lb.insert('end', option) for option in options]
222+
self._lb.bind('<<ListboxSelect>>', lambda _: self._on_select())
223+
224+
r += 1
225+
clear_label = tk.Label(self, text='clear', fg='blue')
226+
clear_label.grid(row=r, column=0, sticky='ew')
227+
clear_label.bind('<Button-1>', lambda _: self._clear_selected())
228+
229+
def _on_select(self):
230+
self.after(200, self.__on_select)
231+
232+
def _clear_selected(self):
233+
for i in self._lb.curselection():
234+
self._lb.selection_clear(i, 'end')
235+
236+
while len(self._values):
237+
self._values.popitem()
238+
239+
if self._on_select_callback is not None:
240+
self._on_select_callback()
241+
242+
def __on_select(self):
243+
value = self._lb.get('active')
244+
245+
if self._lb.cget('selectmode') == 'multiple':
246+
if value in self._values.keys():
247+
self._values.pop(value)
248+
else:
249+
self._values[value] = True
250+
else:
251+
while len(self._values):
252+
self._values.popitem()
253+
self._values[value] = True
254+
255+
if self._on_select_callback is not None:
256+
values = list(self._values.keys())
257+
try:
258+
self._on_select_callback(values)
259+
except TypeError:
260+
self._on_select_callback()
261+
262+
def add_callback(self, callback: callable):
263+
"""
264+
Associates a callback function when the user makes a selection.
265+
266+
:param callback: a callable function
267+
"""
268+
self._on_select_callback = callback
269+
270+
def get_selected(self):
271+
return list(self._values.keys())
272+
273+
def select(self, value):
274+
options = self._lb.get(0, 'end')
275+
if value not in options:
276+
raise ValueError('Not a valid selection')
277+
278+
option = options.index(value)
279+
280+
self._lb.activate(option)
281+
self._values[value] = True
282+
283+
183284
class BinaryLabel(ttk.Label):
184285
"""
185286
Displays a value binary. Provides methods for

0 commit comments

Comments
 (0)