psiutils 0.2.18__py3-none-any.whl → 0.2.19__py3-none-any.whl

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.
@@ -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
+ )
psiutils/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.2.18'
1
+ __version__ = '0.2.19'
psiutils/buttons.py CHANGED
@@ -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'),
Binary file
psiutils/text.py CHANGED
@@ -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',
psiutils/widgets.py CHANGED
@@ -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,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: psiutils
3
- Version: 0.2.18
3
+ Version: 0.2.19
4
4
  Summary: Various TKinter utilities.
5
5
  Author: Jeff
6
6
  Author-email: Jeff <<jeffwatkins2000@gmail.com>>
@@ -1,9 +1,10 @@
1
1
  psiutils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  psiutils/_about_frame.py,sha256=Ei9RMja36x2Xc9SecCXnqXd9EzDe1LIHq4ZDDbKtc_I,7656
3
+ psiutils/_date_picker.py,sha256=Db6r-NCbNHQCXiXlu4RFB-C4T_1Cn597FQvKWHjGFjU,6228
3
4
  psiutils/_logger.py,sha256=9nXxKWUyu4xqIILjTSUKRAMOG4cFy_EGJTTo0FtUAGU,2859
4
5
  psiutils/_notify.py,sha256=rQfYxPxsSwAEJBNXaAZSNlRmZLWB1sah1E1Oja9y-mw,437
5
- psiutils/_version.py,sha256=Y5H04AnBcQJBdVrEZQ-udq9B-QAZZpa6im3A5-sArdI,22
6
- psiutils/buttons.py,sha256=vj-RCKKl4_kYisrMqPi8w56eXfRkbx1zze8oqgS4GjM,8807
6
+ psiutils/_version.py,sha256=03OfPLthYtC55XCorTutkAl4vnGLWEzy16PIUJHl7C8,22
7
+ psiutils/buttons.py,sha256=KOHBSH5_PgArG5T0hMU8weM30C97Hp3Wqco7emiBn0Y,8851
7
8
  psiutils/constants.py,sha256=AJOw73GL9Rx9BEmfduTy32S3dM3Rp6iIOYALY0J7JSw,1541
8
9
  psiutils/drag_manager.py,sha256=L04GzKIinWXzGCawvTnn18F1AEt8PUPS1U-HqCVrh48,3470
9
10
  psiutils/errors.py,sha256=tAypFkpzpbeOppO9zKQimlnRVyr3sMPoiwh0yK3Yc88,562
@@ -20,6 +21,7 @@ psiutils/icons/copy_docs.png,sha256=_VQxZ66Ozh-3NmZh8LzS5r91WqjaTHW3VrbHnYcWg4o,
20
21
  psiutils/icons/delete.png,sha256=0vtqjA-UeMT9t7zaEiw2XIKVI9ojmDsIrDJKUsMy48w,1292
21
22
  psiutils/icons/diff.png,sha256=YIwBq-BlBSFvrbcM_E_F02qkszINNwmvxNrPgCNNNsI,408
22
23
  psiutils/icons/done.png,sha256=WYdJW9-DDCDPiRFMnzCRKU5nDJQdlqP5cpLkko-jYf0,847
24
+ psiutils/icons/download.png,sha256=yiR711xKD8fiKxcj2uyeidFLuLvgzRULhHUpvlmgv5I,492
23
25
  psiutils/icons/edit.png,sha256=0G4AW240sMUcnzxxeC9r89605Z3TKtUTIrD-Z7dmjCI,749
24
26
  psiutils/icons/gear.png,sha256=iAN9l2WmectSjSJXQHR-j9VaJI9-LXDqVSjNYiBLuSw,1489
25
27
  psiutils/icons/new.png,sha256=mn-MoVzEijJnPVqkFlNHu1mnxejvhFPf2iCI2QpsVpQ,544
@@ -52,10 +54,10 @@ psiutils/images/icon-query.png,sha256=e18hqkew4eOxABvECKn7BGO2VTHTE-XLMWPSQfSW9d
52
54
  psiutils/known_paths.py,sha256=Ydhk-Ie_q827ti35Hru8YwUx5awdO2FEG845k1N0hPo,9543
53
55
  psiutils/menus.py,sha256=4pUHb3fEYzLnsftxdKB_NjlPryj_pu7WN5Iy5Quy9-M,1746
54
56
  psiutils/messagebox.py,sha256=ODFodaDahAm31A8M4MVp9FXdhI0zhW8PZdUbBqbVQEY,4973
55
- psiutils/text.py,sha256=Jf8GsGKbJf0IyK1kpSGdOK5jjRBM-m-AQOWY3vvzILs,3098
57
+ psiutils/text.py,sha256=zp46Pfyhyqwwo8MeZMMBeLGCT3meRjmdcbTZqF0IxqE,3206
56
58
  psiutils/treeview.py,sha256=jtSzLWrnFIBDWV5YhWNRoZwgW-Kj8_7XVqZyusr-riQ,2826
57
59
  psiutils/utilities.py,sha256=hBntrigp7WiuzRoGGhS96QVE-7pF2OdYT882jwMTrlQ,3355
58
- psiutils/widgets.py,sha256=TOJKDEdYJetPZxFZohQGJsVX_k3E26-ChgSXqDeJ0Y4,11363
59
- psiutils-0.2.18.dist-info/WHEEL,sha256=YUH1mBqsx8Dh2cQG2rlcuRYUhJddG9iClegy4IgnHik,79
60
- psiutils-0.2.18.dist-info/METADATA,sha256=FJ9niUh37BU4WSlJUW_uyhbVqV6Vkv0x9TRhGRMl1PM,2109
61
- psiutils-0.2.18.dist-info/RECORD,,
60
+ psiutils/widgets.py,sha256=haHVcsm4Y9gYQinr0VlrfHg2H83Pnlj_jxjb4aXd37A,11420
61
+ psiutils-0.2.19.dist-info/WHEEL,sha256=e_m4S054HL0hyR3CpOk-b7Q7fDX6BuFkgL5OjAExXas,80
62
+ psiutils-0.2.19.dist-info/METADATA,sha256=YRPX8Vcs3MqE2STtI_q3ARFuFcgraRMzvN1PwdGLDm4,2109
63
+ psiutils-0.2.19.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.11
2
+ Generator: uv 0.9.27
3
3
  Root-Is-Purelib: true
4
- Tag: py3-none-any
4
+ Tag: py3-none-any