psiutils 0.2.18__tar.gz → 0.2.20__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. {psiutils-0.2.18 → psiutils-0.2.20}/PKG-INFO +1 -1
  2. {psiutils-0.2.18 → psiutils-0.2.20}/pyproject.toml +1 -1
  3. psiutils-0.2.20/src/psiutils/_date_picker.py +210 -0
  4. psiutils-0.2.20/src/psiutils/_version.py +1 -0
  5. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/buttons.py +2 -0
  6. psiutils-0.2.20/src/psiutils/icons/checkbox_checked.png +0 -0
  7. psiutils-0.2.20/src/psiutils/icons/checkbox_unchecked.png +0 -0
  8. psiutils-0.2.20/src/psiutils/icons/download.png +0 -0
  9. psiutils-0.2.20/src/psiutils/icons/upload.png +0 -0
  10. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/text.py +5 -0
  11. psiutils-0.2.20/src/psiutils/treeview.py +149 -0
  12. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/widgets.py +6 -5
  13. psiutils-0.2.18/src/psiutils/_version.py +0 -1
  14. psiutils-0.2.18/src/psiutils/treeview.py +0 -85
  15. {psiutils-0.2.18 → psiutils-0.2.20}/README.md +0 -0
  16. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/__init__.py +0 -0
  17. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/_about_frame.py +0 -0
  18. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/_logger.py +0 -0
  19. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/_notify.py +0 -0
  20. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/constants.py +0 -0
  21. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/drag_manager.py +0 -0
  22. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/errors.py +0 -0
  23. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icecream_init.py +0 -0
  24. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/backup.png +0 -0
  25. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/build.png +0 -0
  26. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/cancel.png +0 -0
  27. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/check.png +0 -0
  28. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/clear.png +0 -0
  29. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/code.png +0 -0
  30. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/compare.png +0 -0
  31. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/copy_clipboard.png +0 -0
  32. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/copy_docs.png +0 -0
  33. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/delete.png +0 -0
  34. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/diff.png +0 -0
  35. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/done.png +0 -0
  36. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/edit.png +0 -0
  37. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/gear.png +0 -0
  38. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/new.png +0 -0
  39. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/next.png +0 -0
  40. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/open.png +0 -0
  41. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/pause.png +0 -0
  42. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/preferences.png +0 -0
  43. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/previous.png +0 -0
  44. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/process.png +0 -0
  45. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/redo.png +0 -0
  46. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/refresh.png +0 -0
  47. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/rename.png +0 -0
  48. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/report.png +0 -0
  49. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/reset.png +0 -0
  50. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/restore.png +0 -0
  51. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/restore_database.png +0 -0
  52. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/restore_page.png +0 -0
  53. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/revert.png +0 -0
  54. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/save.png +0 -0
  55. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/script.png +0 -0
  56. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/search.png +0 -0
  57. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/send.png +0 -0
  58. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/start.png +0 -0
  59. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/update.png +0 -0
  60. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/upgrade.png +0 -0
  61. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/icons/windows.png +0 -0
  62. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/images/icon-error.png +0 -0
  63. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/images/icon-info.png +0 -0
  64. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/images/icon-query.png +0 -0
  65. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/known_paths.py +0 -0
  66. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/menus.py +0 -0
  67. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/messagebox.py +0 -0
  68. {psiutils-0.2.18 → psiutils-0.2.20}/src/psiutils/utilities.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: psiutils
3
- Version: 0.2.18
3
+ Version: 0.2.20
4
4
  Summary: Various TKinter utilities.
5
5
  Author: Jeff
6
6
  Author-email: Jeff <<jeffwatkins2000@gmail.com>>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "psiutils"
3
- version = "0.2.18"
3
+ version = "0.2.20"
4
4
  description = "Various TKinter utilities."
5
5
  authors = [{name = "Jeff", "email" = "<jeffwatkins2000@gmail.com>"}]
6
6
  requires-python = '>= 3.10, < 3.13'
@@ -0,0 +1,210 @@
1
+ import tkinter as tk
2
+ from tkinter import ttk
3
+ import tkinter.font as tkFont
4
+ from tkcalendar import DateEntry
5
+ from functools import partial
6
+ from datetime import datetime, timedelta
7
+ from dateutil.parser import parse
8
+
9
+ from text import Text
10
+
11
+ txt = Text()
12
+
13
+ PAD = 2
14
+ TIME_WIDTH = 3
15
+ INCREMENT_BUTTON_SIZE = 2
16
+ INCREMENT_BUTTON_FONT_SIZE = 8
17
+ DATE_FORMAT = '%d/%m/%Y'
18
+ PICKER_DATE_PATTERN = 'dd/mm/yyyy',
19
+ MAX_HOURS = 23
20
+ MAX_MINS = 59
21
+ TALL_COMBO_PADDING = 6
22
+
23
+
24
+ class DatePicker(tk.Frame):
25
+ def __init__(
26
+ self, master: tk.Frame,
27
+ initial_date: datetime = None,
28
+ date_format: str = ''):
29
+ super().__init__(master)
30
+ if not initial_date:
31
+ initial_date = datetime.now()
32
+ if not date_format:
33
+ date_format = DATE_FORMAT
34
+
35
+ self._date_input = tk.StringVar(
36
+ value=initial_date.strftime(DATE_FORMAT))
37
+
38
+ style = ttk.Style()
39
+ style.configure(
40
+ 'Increment.TButton',
41
+ font=('Helvetica', INCREMENT_BUTTON_FONT_SIZE),
42
+ padding=0,)
43
+ style.configure('Tall.TCombobox', padding=TALL_COMBO_PADDING)
44
+
45
+ main_frame = self._picker()
46
+ main_frame.pack()
47
+
48
+ def _picker(self) -> tk.Frame:
49
+ frame = ttk.Frame(self)
50
+
51
+ column = 0
52
+ date_picker = self._date_picker(frame, self._date_input)
53
+ date_picker.grid(row=0, column=column, rowspan=2, sticky=tk.NS)
54
+
55
+ column += 1
56
+ button = ttk.Button(
57
+ frame,
58
+ text=txt.INCREMENT_ARROW,
59
+ command=partial(self._date_increment, self._date_input),
60
+ width=INCREMENT_BUTTON_SIZE,
61
+ style='Increment.TButton',
62
+ )
63
+ button.grid(row=0, column=column, padx=PAD)
64
+
65
+ button = ttk.Button(
66
+ frame,
67
+ text=txt.DECREMENT_ARROW,
68
+ command=partial(self._date_increment, self._date_input, -1),
69
+ width=INCREMENT_BUTTON_SIZE,
70
+ style='Increment.TButton',
71
+ )
72
+ button.grid(row=1, column=column, padx=PAD)
73
+
74
+ column += 1
75
+
76
+ return frame
77
+
78
+ def _date_picker(
79
+ self, master: tk.Frame, textvariable: tk.StringVar) -> DateEntry:
80
+ event_date = datetime.now()
81
+ return DateEntry(
82
+ master,
83
+ date_pattern=PICKER_DATE_PATTERN,
84
+ year=event_date.year,
85
+ month=event_date.month,
86
+ day=event_date.day,
87
+ textvariable=textvariable,
88
+ )
89
+
90
+ @property
91
+ def date(self) -> datetime:
92
+ return parse(self._date_input.get())
93
+
94
+ def _date_increment(
95
+ self,
96
+ textvariable: tk.StringVar,
97
+ increment: int = 1, *args) -> None:
98
+ date = parse(textvariable.get(), dayfirst=True).date()
99
+ new_date = date + timedelta(days=increment)
100
+ textvariable.set(new_date.strftime(DATE_FORMAT))
101
+
102
+
103
+ class TimePicker(tk.Frame):
104
+ def __init__(
105
+ self, master: tk.Frame,
106
+ use_seconds: bool = False,
107
+ use_labels: bool = False):
108
+ super().__init__(master)
109
+ self.use_seconds = use_seconds
110
+ self.use_labels = use_labels
111
+
112
+ self._hour_input = tk.StringVar(value='00')
113
+ self._minute_input = tk.StringVar(value='00')
114
+ self._second_input = tk.StringVar(value='00')
115
+
116
+ style = ttk.Style()
117
+ style.configure('Increment.TButton', font=('Helvetica', 8))
118
+
119
+ main_frame = self._picker()
120
+ main_frame.grid(row=0, column=0)
121
+
122
+ def _picker(self) -> tk.Frame:
123
+ frame = ttk.Frame(self)
124
+
125
+ column = 0
126
+ row = 0
127
+ if self.use_labels:
128
+ label = ttk.Label(frame, text='Hour')
129
+ label.grid(row=row, column=column, sticky=tk.W)
130
+
131
+ label = ttk.Label(frame, text='Mins')
132
+ label.grid(row=row, column=column+1, sticky=tk.W)
133
+
134
+ if self.use_seconds:
135
+ label = ttk.Label(frame, text='Secs')
136
+ label.grid(row=row, column=column+5, sticky=tk.E)
137
+
138
+ row += 1
139
+ hour_timer = self._timer_element(frame, self._hour_input, MAX_HOURS)
140
+ hour_timer.grid(row=row, column=column)
141
+
142
+ column += 1
143
+ minute_timer = self._timer_element(frame, self._minute_input)
144
+ minute_timer.grid(row=row, column=column)
145
+
146
+ if self.use_seconds:
147
+ column += 1
148
+ second_timer = self._timer_element(frame, self._second_input)
149
+ second_timer.grid(row=row, column=column)
150
+
151
+ return frame
152
+
153
+ def _timer_element(
154
+ self,
155
+ master: tk.Frame,
156
+ textvariable: tk.StringVar,
157
+ max_value: int = MAX_MINS,
158
+ ) -> tk.Frame:
159
+ frame = ttk.Frame(master)
160
+ column = 0
161
+
162
+ combobox = ttk.Combobox(
163
+ frame,
164
+ textvariable=textvariable,
165
+ values=[f'{x:02d}' for x in range(max_value+1)],
166
+ width=TIME_WIDTH,
167
+ style='Tall.TCombobox',
168
+ )
169
+ combobox.grid(row=0, column=column, rowspan=2, sticky=tk.W)
170
+
171
+ column += 1
172
+
173
+ button = ttk.Button(
174
+ frame,
175
+ text=txt.INCREMENT_ARROW,
176
+ command=partial(self._time_increment, textvariable, 1, max_value),
177
+ width=INCREMENT_BUTTON_SIZE,
178
+ style='Increment.TButton',
179
+ )
180
+ button.grid(row=0, column=column, padx=PAD)
181
+
182
+ button = ttk.Button(
183
+ frame,
184
+ text=txt.DECREMENT_ARROW,
185
+ command=partial(self._time_increment, textvariable, -1, max_value),
186
+ width=INCREMENT_BUTTON_SIZE,
187
+ style='Increment.TButton',
188
+ )
189
+ button.grid(row=1, column=column, padx=PAD)
190
+ return frame
191
+
192
+ def _time_increment(
193
+ self,
194
+ textvariable: tk.StringVar,
195
+ increment,
196
+ max_value) -> None:
197
+ value = int(textvariable.get()) + increment
198
+ if value < 0:
199
+ value = max_value
200
+ if value > max_value:
201
+ value = 0
202
+ textvariable.set(f'{value:02d}')
203
+
204
+ @property
205
+ def time(self) -> datetime:
206
+ return datetime(
207
+ hours=int(self._hour_input.get()),
208
+ minutes=int(self._minute_input.get()),
209
+ seconds=int(self._second_input.get()),
210
+ )
@@ -0,0 +1 @@
1
+ __version__ = '0.2.20'
@@ -228,6 +228,7 @@ icon_buttons = {
228
228
  'copy_docs': (txt.COPY, 'copy_docs'),
229
229
  'copy_clipboard': (txt.COPY, 'copy_clipboard'),
230
230
  'delete': (txt.DELETE, 'delete'),
231
+ 'download': (txt.DOWNLOAD, 'download'),
231
232
  'diff': (txt.DIFF, 'diff'),
232
233
  'done': (txt.DONE, 'done'),
233
234
  'edit': (txt.EDIT, 'edit'),
@@ -256,6 +257,7 @@ icon_buttons = {
256
257
  'start': (txt.START, 'start'),
257
258
  'update': (txt.UPDATE, 'update'),
258
259
  'upgrade': (txt.UPGRADE, 'upgrade'),
260
+ 'upload': (txt.UPLOAD, 'upload'),
259
261
  'use': (txt.USE, 'done'),
260
262
  'windows': (txt.WINDOWS, 'windows'),
261
263
  }
@@ -14,15 +14,19 @@ psi_strings = {
14
14
  'COMPARE': 'Compare',
15
15
  'CONFIG': 'Config',
16
16
  'COPY': 'Copy',
17
+ 'DECREMENT_ARROW': '▼',
17
18
  'DELETE': 'Delete',
18
19
  'DELETE_THESE_ITEMS': 'Are you sure you want to delete these item(s)?',
19
20
  'DIFF': 'Diff',
20
21
  'DONE': 'Done',
22
+ 'DOWNLOAD': 'Download',
21
23
  'EDIT': 'Edit',
22
24
  'ELLIPSIS': ' ...',
23
25
  'EVENT': 'Event',
24
26
  'EXIT': 'Exit',
25
27
  'HELP': 'Help',
28
+ 'INCREMENT_ARROW': '▲',
29
+ 'LOGS': 'Logs',
26
30
  'NEW': 'New',
27
31
  'NEXT': 'Next',
28
32
  'NO': 'No',
@@ -50,6 +54,7 @@ psi_strings = {
50
54
  'START': 'Start',
51
55
  'UPDATE': 'Update',
52
56
  'UPGRADE': 'Upgrade',
57
+ 'UPLOAD': 'Upload',
53
58
  'USE': 'Use',
54
59
  'WINDOWS': 'Windows',
55
60
  'YES': 'Yes',
@@ -0,0 +1,149 @@
1
+
2
+ from pathlib import Path
3
+ import tkinter as tk
4
+ from tkinter import ttk
5
+ import dateutil # type: ignore
6
+ from dateutil.parser import parse # type: ignore
7
+ from PIL import Image, ImageTk
8
+
9
+ CHECK_BOX_SIZE = (20, 20)
10
+
11
+
12
+ def sort_treeview(tree: ttk.Treeview, col: int, reverse: bool) -> None:
13
+ """Sort the Treeview by column."""
14
+ children = [
15
+ (tree.set(child, col), child) for child in tree.get_children('')
16
+ ]
17
+ is_date = True
18
+ try:
19
+ date_children = []
20
+ for child in children:
21
+ if len(child[0]) < 8:
22
+ is_date = False
23
+ break
24
+ date = parse(child[0])
25
+ date_children.append((date, child[1]))
26
+ except dateutil.parser._parser.ParserError:
27
+ is_date = False
28
+ if is_date:
29
+ children = date_children
30
+ try:
31
+ children.sort(key=lambda t: float(t[0]), reverse=reverse)
32
+ except TypeError:
33
+ children.sort(key=lambda t: t[0], reverse=reverse)
34
+ except ValueError:
35
+ children.sort(reverse=reverse)
36
+
37
+ for index, (val, child) in enumerate(children):
38
+ tree.move(child, '', index)
39
+
40
+ tree.heading(col, command=lambda: sort_treeview(tree, col, not reverse))
41
+
42
+
43
+ class CheckTreeView(ttk.Treeview):
44
+ def __init__(
45
+ self,
46
+ master,
47
+ column_defs,
48
+ **kwargs):
49
+ """
50
+
51
+ :param column_defs: a tuple defining column (key, text, width)
52
+ Other parameters are passed to the `TreeView`.
53
+ """
54
+ super().__init__(master, **kwargs)
55
+ self.column_defs = column_defs
56
+ self["show"] = "tree headings"
57
+ self._configure_columns()
58
+
59
+ (
60
+ self.unchecked_image,
61
+ self.checked_image
62
+ ) = self._get_checkbox_images()
63
+
64
+ if "selectmode" not in kwargs:
65
+ kwargs["selectmode"] = "none"
66
+ if "show" not in kwargs:
67
+ kwargs["show"] = "tree"
68
+
69
+ def _get_checkbox_images(
70
+ self) -> tuple[ImageTk.PhotoImage, ImageTk.PhotoImage]:
71
+
72
+ icon_path = f'{Path(__file__).parent}/icons/'
73
+ unchecked_img = Image.open(f"{icon_path}checkbox_unchecked.png")
74
+ unchecked_img = unchecked_img.resize(CHECK_BOX_SIZE, Image.LANCZOS)
75
+ unchecked = ImageTk.PhotoImage(unchecked_img)
76
+
77
+ checked_img = tk.PhotoImage(file=f"{icon_path}checkbox_checked.png")
78
+ checked_img = Image.open(f"{icon_path}checkbox_checked.png")
79
+ checked_img = checked_img.resize(CHECK_BOX_SIZE, Image.LANCZOS)
80
+ checked = ImageTk.PhotoImage(checked_img)
81
+ return (unchecked, checked)
82
+
83
+ def _configure_columns(self) -> None:
84
+ column_ids = [col[0] for col in self.column_defs]
85
+ self["columns"] = column_ids[1:]
86
+
87
+ # Configure each column
88
+ for index, (col_id, heading, width) in enumerate(self.column_defs):
89
+ if index == 0:
90
+ self.column(
91
+ "#0",
92
+ width=width,
93
+ minwidth=width,
94
+ stretch=False,
95
+ anchor="center")
96
+ self.heading("#0", text=heading)
97
+ else:
98
+ self.column(col_id, width=width, anchor="w", stretch=True)
99
+ self.heading(col_id, text=heading)
100
+
101
+ def populate(self, items: list[tuple], checked: bool = False) -> None:
102
+ self.delete(*self.get_children())
103
+ item_checked = (self.checked_image
104
+ if checked else self.unchecked_image)
105
+ for item in items:
106
+ iid = self.insert(
107
+ parent='',
108
+ index='end',
109
+ image=item_checked,
110
+ values=item
111
+ )
112
+ if checked:
113
+ self.item(iid, tags=("checked",))
114
+ else:
115
+ self.item(iid, tags=("unchecked"))
116
+
117
+ def item_click(self, event) -> int:
118
+ iid = self.identify_row(event.y)
119
+ if not iid:
120
+ return
121
+
122
+ current_img = self.item(iid, "image")[0]
123
+ if current_img == str(self.unchecked_image):
124
+ self.item(iid, image=self.checked_image, tags=("checked",))
125
+ else:
126
+ self.item(iid, image=self.unchecked_image, tags=("unchecked"))
127
+
128
+ return "break"
129
+
130
+ def checked_items(self) -> list[tuple]:
131
+ """
132
+ Returns a list of the values (text columns) for all currently
133
+ checked rows.
134
+
135
+ Each returned tuple contains the values from the data columns only
136
+ (excludes the checkbox image in the tree column).
137
+
138
+ Example return value:
139
+ [("docs", "report.pdf", "Read this file"),
140
+ ("code", "main.py", "Fix bug")]
141
+ """
142
+ checked_items = []
143
+
144
+ for iid in self.get_children(''):
145
+ tags = self.item(iid, "tags")
146
+ if "checked" in tags:
147
+ values = self.item(iid, "values")
148
+ checked_items.append(values)
149
+ return checked_items
@@ -6,6 +6,7 @@ import contextlib
6
6
 
7
7
  from .constants import PAD, COLOURS
8
8
  from ._about_frame import AboutFrame
9
+ from psiutils._date_picker import DatePicker, TimePicker
9
10
 
10
11
  HAND = 'hand2'
11
12
  DIM_TEXT = '#555'
@@ -23,16 +24,16 @@ class PsiText(tk.Text):
23
24
  tk.Text.__init__(self, *args, **kwargs)
24
25
 
25
26
  # create a proxy for the underlying widget
26
- self._orig = f"{self._w}_orig"
27
- self.tk.call("rename", self._w, self._orig)
27
+ self._orig = f'{self._w}_orig'
28
+ self.tk.call('rename', self._w, self._orig)
28
29
  self.tk.createcommand(self._w, self._proxy)
29
30
 
30
31
  def _proxy(self, command, *args):
31
32
  cmd = (self._orig, command) + args
32
33
  result = self.tk.call(cmd)
33
34
 
34
- if command in ("insert", "delete", "replace"):
35
- self.event_generate("<<TextModified>>")
35
+ if command in ('insert', 'delete', 'replace'):
36
+ self.event_generate('<<TextModified>>')
36
37
 
37
38
  return result
38
39
 
@@ -349,7 +350,7 @@ class Tooltip:
349
350
 
350
351
  x, y = tip_pos_calculator(widget, label)
351
352
 
352
- self.tw.wm_geometry("+%d+%d" % (x, y+self.vertical_offset))
353
+ self.tw.wm_geometry('+%d+%d' % (x, y+self.vertical_offset))
353
354
  # self.tw.wm_geometry(f'{x}x{y}')
354
355
 
355
356
  def hide(self):
@@ -1 +0,0 @@
1
- __version__ = '0.2.18'
@@ -1,85 +0,0 @@
1
-
2
- from tkinter import ttk
3
- import dateutil # type: ignore
4
- from dateutil.parser import parse # type: ignore
5
-
6
- UNCHECKED = '\u2610'
7
- CHECKED = '\u2612'
8
-
9
-
10
- def sort_treeview(tree: ttk.Treeview, col: int, reverse: bool) -> None:
11
- """Sort the Treeview by column."""
12
- children = [
13
- (tree.set(child, col), child) for child in tree.get_children('')
14
- ]
15
- is_date = True
16
- try:
17
- date_children = []
18
- for child in children:
19
- if len(child[0]) < 8:
20
- is_date = False
21
- break
22
- date = parse(child[0])
23
- date_children.append((date, child[1]))
24
- except dateutil.parser._parser.ParserError:
25
- is_date = False
26
- if is_date:
27
- children = date_children
28
- try:
29
- children.sort(key=lambda t: float(t[0]), reverse=reverse)
30
- except TypeError:
31
- children.sort(key=lambda t: t[0], reverse=reverse)
32
- except ValueError:
33
- children.sort(reverse=reverse)
34
-
35
- for index, (val, child) in enumerate(children):
36
- tree.move(child, '', index)
37
-
38
- tree.heading(col, command=lambda: sort_treeview(tree, col, not reverse))
39
-
40
-
41
- class CheckTreeView(ttk.Treeview):
42
- def __init__(self, master=None, width=200, clicked=None,
43
- unchecked=UNCHECKED, checked=CHECKED, **kwargs):
44
- """
45
- :param width: the width of the check list
46
- :param clicked: the optional function if a checkbox is clicked. Takes a
47
- `iid` parameter.
48
- :param unchecked: the character for an unchecked box (default is
49
- "\u2610")
50
- :param unchecked: the character for a checked box (default is "\u2612")
51
-
52
- Other parameters are passed to the `TreeView`.
53
- """
54
- if "selectmode" not in kwargs:
55
- kwargs["selectmode"] = "none"
56
- if "show" not in kwargs:
57
- kwargs["show"] = "tree"
58
- ttk.Treeview.__init__(self, master, **kwargs)
59
- self.number_selected = 0
60
-
61
- def item_click(self, pos_x: int, pos_y: int) -> int:
62
- element = self.identify("element", pos_x, pos_y)
63
- if element == "text":
64
- iid = self.identify_row(pos_y)
65
- self._toggle(iid)
66
- return self.number_selected
67
-
68
- def _toggle(self, iid):
69
- """
70
- Toggle the checkbox `iid`
71
- """
72
- values = list(self.item(iid).values())[2]
73
- new_value = UNCHECKED
74
- number = -1
75
- if values[0] == UNCHECKED:
76
- new_value = CHECKED
77
- number = 1
78
- values = [new_value] + list(values[1:])
79
- self.item(iid, values=values)
80
- self.number_selected = self.number_selected + number
81
-
82
- def populate(self, items: list[tuple]) -> None:
83
- for item in items:
84
- values = [UNCHECKED] + list(item)
85
- self.insert('', 'end', values=values)
File without changes