timy-cli 2.0.0__tar.gz → 2.1.0__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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: timy-cli
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Summary: Analog Clock and Timer
5
5
  Author-email: espehon <espehon@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/espehon/timy-cli
@@ -14,6 +14,11 @@ Classifier: Topic :: Utilities
14
14
  Requires-Python: >=3.8
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
+ Requires-Dist: colorama>=0.4.6
18
+ Requires-Dist: questionary>=2.1.0
19
+ Requires-Dist: tzdata>=2024.1
20
+ Requires-Dist: backports.zoneinfo; python_version < "3.9"
21
+ Dynamic: license-file
17
22
 
18
23
  # timy
19
24
  Simple console clock/timer
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" # Boilerplate
4
4
 
5
5
  [project]
6
6
  name = "timy-cli" # Your pip install <name> (must be unique)
7
- version = "2.0.0" # Must be manually updated here with every change
7
+ version = "2.1.0" # Must be manually updated here with every change
8
8
  authors = [
9
9
  { name="espehon", email="espehon@gmail.com" }, # Email is optional (as array?)
10
10
  ]
@@ -12,7 +12,10 @@ description = "Analog Clock and Timer" # Header on PyPI (above rea
12
12
  readme = "README.md" # Boilerplate and will be added to the PyPI page
13
13
  requires-python = ">=3.8" # Python version
14
14
  dependencies = [ # Any site-package dependencies (as a list)
15
- "colorama >= 0.4.6" # NOTE: if any given, the pip test will be different
15
+ "colorama >= 0.4.6",
16
+ "questionary >= 2.1.0",
17
+ "tzdata >= 2024.1",
18
+ "backports.zoneinfo; python_version < \"3.9\""
16
19
  ]
17
20
  classifiers = [ # Kinda boilerplate. Used as hashtags really
18
21
  "Programming Language :: Python :: 3", # Tweak this to match your project
@@ -0,0 +1,2 @@
1
+ # MIT License
2
+ # Copyright (c) 2022 espehon
@@ -0,0 +1,9 @@
1
+ # MIT License
2
+ # Copyright (c) 2022 espehon
3
+
4
+ import sys
5
+
6
+ from timy_cli.timy import cli
7
+
8
+ if __name__ == "__main__":
9
+ sys.exit(cli())
@@ -0,0 +1,37 @@
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+
5
+ CONFIG_FILE = Path.home() / ".local/share/timy/settings.json"
6
+
7
+ DEFAULT_SETTINGS = {
8
+ "MainZone": "local",
9
+ "TimeZone1": "local",
10
+ "TimeZone2": "UTC",
11
+ "TimeZone3": "America/New_York",
12
+ "TimeZone4": "Europe/London",
13
+ "stretch_x": True
14
+ }
15
+
16
+
17
+
18
+ def load_or_create_settings():
19
+ if not os.path.exists(CONFIG_FILE):
20
+ CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
21
+ with open(CONFIG_FILE, 'w') as f:
22
+ json.dump(DEFAULT_SETTINGS, f, indent=4) # Create file with defaults
23
+ return DEFAULT_SETTINGS
24
+ else:
25
+ with open(CONFIG_FILE, 'r') as f:
26
+ user_settings = json.load(f) # Load user settings
27
+
28
+ # Merge user settings with defaults (user settings take precedence)
29
+ settings = DEFAULT_SETTINGS.copy()
30
+ settings.update(user_settings)
31
+ return settings
32
+
33
+
34
+ def save_settings(settings):
35
+ CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
36
+ with open(CONFIG_FILE, 'w') as f:
37
+ json.dump(settings, f, indent=4)
@@ -0,0 +1,427 @@
1
+
2
+ import math
3
+ import time
4
+ import sys
5
+ import argparse
6
+ import importlib.metadata
7
+ from datetime import datetime
8
+ from os import name, get_terminal_size
9
+ from subprocess import run
10
+
11
+ try:
12
+ from zoneinfo import ZoneInfo
13
+ except ImportError:
14
+ from backports.zoneinfo import ZoneInfo
15
+
16
+ from colorama import Fore, init
17
+ from questionary import Choice
18
+ import questionary
19
+ from timy_cli.settings import load_or_create_settings
20
+
21
+ init(autoreset=True)
22
+
23
+
24
+ # Set __version__
25
+ try:
26
+ __version__ = f"timy {importlib.metadata.version('timy-cli')}"
27
+ except importlib.metadata.PackageNotFoundError:
28
+ __version__ = "Package not installed..."
29
+
30
+
31
+ parser = argparse.ArgumentParser(description='Print an analog clock to the consol!', add_help=False)
32
+
33
+ parser.add_argument('-?', '--help', action='help', help='Show this help message and exit.')
34
+
35
+ parser.add_argument('-v', '--version', action='version', version='%(prog)s {version}'.format(version=__version__))
36
+
37
+ parser.add_argument('-l', '--live', dest='_refresh', action='store_true', help='Refresh every minute until stopped. (live clock)')
38
+
39
+ parser.add_argument('-s', '--stopwatch', action='store_true', help='Interactive Stopwatch timer')
40
+
41
+ parser.add_argument('-c', '--countdown', metavar='M', action='append', type=int, nargs='?', const=60, help='Countdown timer for [M] minutes (default 60)')
42
+
43
+ parser.add_argument('-m', '--multiple', action='store_true', help='Show multiple timezones (defined in settings.json)')
44
+
45
+ parser.add_argument('-z', '--zone', action='store_true', help='Configure timezone settings interactively')
46
+
47
+ args = parser.parse_args() #Execute parse_args()
48
+
49
+
50
+
51
+
52
+ def clear():
53
+ if name == 'nt':
54
+ _ = run('cls', shell=True)
55
+ else:
56
+ _ = run('clear')
57
+
58
+
59
+ def load_zone(zone_name):
60
+ if zone_name is None:
61
+ return None
62
+ if isinstance(zone_name, str) and zone_name.lower() == 'local':
63
+ return None
64
+ try:
65
+ return ZoneInfo(zone_name)
66
+ except Exception:
67
+ normalized = zone_name.replace(' ', '_') if isinstance(zone_name, str) else zone_name
68
+ try:
69
+ return ZoneInfo(normalized)
70
+ except Exception:
71
+ return None
72
+
73
+
74
+ def zone_label(zone_name):
75
+ if zone_name is None:
76
+ return 'Unknown'
77
+ if isinstance(zone_name, str) and zone_name.lower() == 'local':
78
+ return 'Local'
79
+ return zone_name
80
+
81
+
82
+ POPULAR_ZONES = [
83
+ ('UTC', 'UTC'),
84
+ ('US Eastern (New York)', 'America/New_York'),
85
+ ('US Central (Chicago)', 'America/Chicago'),
86
+ ('US Mountain (Denver)', 'America/Denver'),
87
+ ('US Pacific (Los Angeles)', 'America/Los_Angeles'),
88
+ ('Arizona (no DST)', 'America/Phoenix'),
89
+ ('Hawaii (no DST)', 'Pacific/Honolulu'),
90
+ ('Alaska', 'America/Anchorage'),
91
+ ('London', 'Europe/London'),
92
+ ('Central Europe (Berlin)', 'Europe/Berlin'),
93
+ ('Eastern Europe (Athens)', 'Europe/Athens'),
94
+ ('Moscow', 'Europe/Moscow'),
95
+ ('India (Kolkata)', 'Asia/Kolkata'),
96
+ ('China (Shanghai)', 'Asia/Shanghai'),
97
+ ('Japan (Tokyo)', 'Asia/Tokyo'),
98
+ ('Singapore', 'Asia/Singapore'),
99
+ ('Australia Eastern (Sydney)', 'Australia/Sydney'),
100
+ ('Australia Central (Darwin, no DST)', 'Australia/Darwin'),
101
+ ('Australia Western (Perth, no DST)', 'Australia/Perth'),
102
+ ('New Zealand (Auckland)', 'Pacific/Auckland'),
103
+ ]
104
+
105
+ ZONE_CANCELLED = '__cancelled__'
106
+
107
+
108
+ def zone_choices(include_disabled=False):
109
+ choices = [Choice('Local', 'local')]
110
+ if include_disabled:
111
+ choices.append(Choice('Disabled', '__disabled__'))
112
+ choices.extend(Choice(label, value) for label, value in POPULAR_ZONES)
113
+ choices.append(Choice('More', '__more__'))
114
+ return choices
115
+
116
+
117
+ def choose_zone(current_zone, include_disabled=False):
118
+ selected = questionary.select(
119
+ 'Select a timezone:',
120
+ choices=zone_choices(include_disabled),
121
+ default=current_zone,
122
+ ).ask()
123
+ if selected is None:
124
+ return ZONE_CANCELLED
125
+ if selected == '__disabled__':
126
+ return None
127
+ if selected != '__more__':
128
+ return selected
129
+
130
+ try:
131
+ from zoneinfo import available_timezones
132
+ except ImportError:
133
+ from backports.zoneinfo import available_timezones
134
+
135
+ all_zones = sorted(available_timezones())
136
+
137
+ return questionary.select(
138
+ 'Select a timezone from the full list:',
139
+ choices=[Choice('Local', 'local')] + [Choice(zone, zone) for zone in all_zones],
140
+ default=current_zone if current_zone in all_zones else None,
141
+ ).ask() or ZONE_CANCELLED
142
+
143
+
144
+ def configure_zones():
145
+ from timy_cli.settings import save_settings
146
+
147
+ settings = load_or_create_settings()
148
+ target = questionary.select(
149
+ 'Which timezone setting do you want to change?',
150
+ choices=[
151
+ Choice('Main clock', 'MainZone'),
152
+ Choice('Multi clock 1', 'TimeZone1'),
153
+ Choice('Multi clock 2', 'TimeZone2'),
154
+ Choice('Multi clock 3', 'TimeZone3'),
155
+ Choice('Multi clock 4', 'TimeZone4'),
156
+ Choice('Done', '__done__'),
157
+ ],
158
+ ).ask()
159
+
160
+ while target not in (None, '__done__'):
161
+ selected_zone = choose_zone(settings.get(target), target != 'MainZone')
162
+ if selected_zone == ZONE_CANCELLED:
163
+ return
164
+ settings[target] = selected_zone
165
+ save_settings(settings)
166
+ print(f"{target} set to {settings[target] or 'disabled'}")
167
+ target = questionary.select(
168
+ 'Choose another setting:',
169
+ choices=[
170
+ Choice('Main clock', 'MainZone'),
171
+ Choice('Multi clock 1', 'TimeZone1'),
172
+ Choice('Multi clock 2', 'TimeZone2'),
173
+ Choice('Multi clock 3', 'TimeZone3'),
174
+ Choice('Multi clock 4', 'TimeZone4'),
175
+ Choice('Done', '__done__'),
176
+ ],
177
+ ).ask()
178
+
179
+
180
+ def countdownTimer(Minutes):
181
+ clear()
182
+ paddingWithGlass = get_terminal_size()[1] - 32 # 32 is length of the following outputs
183
+ if paddingWithGlass > 0:
184
+ print("\n" * paddingWithGlass)
185
+ print('''
186
+ _.-"""-._
187
+ _.-"" ""-._
188
+ :"-. .-":
189
+ '"-_"-._ _.-".-"'
190
+ ||T+._"-._.-"_.-"|
191
+ ||: "-.|.-" : ||
192
+ || . ' || . ||
193
+ || . '|| . ||
194
+ || ';.:||' ||
195
+ || '::|| ||
196
+ || :|| ||
197
+ || ':|| ||
198
+ || .' :||. ||
199
+ || ' . :||.' ||
200
+ ||.'- .:|| -'._||
201
+ .-'": .::::||:. : "'-.
202
+ :"-.'::::::||::' .-":
203
+ "-."-._"--:" .-".-"
204
+ "-._"-._.-".-"
205
+ "-.|.-"
206
+ ''')
207
+ try:
208
+ for m in progressbar(range(Minutes), prefix="Timer: " +str(Minutes) + " Min ", suffix="(pass ← wait)"):
209
+ time.sleep(60)
210
+ if m == Minutes - 1:
211
+ clear()
212
+ print("\n" * get_terminal_size()[1])
213
+ print(f'''{Fore.LIGHTGREEN_EX}
214
+ +====+
215
+ |( )|
216
+ | )( |
217
+ |(::)|
218
+ +====+
219
+ Timer has ended!''')
220
+ print("\a")
221
+ except:
222
+ print(f"\n\n{Fore.YELLOW}[Timer interrupted]\n\n")
223
+
224
+
225
+ def progressbar(it, prefix="", suffix=""): #progressbar --> prefix: [############################.............................] i/it
226
+ size = abs(get_terminal_size()[0] - len(prefix) - len(suffix) - 16)
227
+ count = len(it)
228
+ def show(j):
229
+ x = int(size*j/count)
230
+ sys.stdout.write("%s[%s%s] %i ← %i %s \r" % (prefix, "#"*x, "."*(size-x), j, (count-j), suffix))
231
+ sys.stdout.flush()
232
+ show(0) #This prints the progressbar at 0 progress. Then next for loop renders the rest (stating at 1)
233
+ for i, item in enumerate(it): #This is the 'i' in the comment on the 'def' line
234
+ yield item
235
+ show(i+1)
236
+ sys.stdout.write("\n")
237
+ sys.stdout.flush()
238
+
239
+
240
+ class AnalogClock:
241
+ def __init__(self, width=None, height=25, stretch_x=True, zone_name=None):
242
+ self.height = height
243
+ self.stretch_x = stretch_x
244
+ self.zone_name = zone_name
245
+ self.timezone = load_zone(zone_name)
246
+ self.width = width if width is not None else (height * 2 if stretch_x else height)
247
+ self.canvas = [[' '] * self.width for _ in range(self.height)]
248
+ self.center_x = self.width // 2
249
+ self.center_y = self.height // 2
250
+ self.x_radius = self.width // 2 - 1
251
+ self.y_radius = self.height // 2 - 1
252
+ self.x_scale = self.x_radius / 24.0
253
+ self.y_scale = self.y_radius / 24.0
254
+
255
+ def reset_canvas(self):
256
+ self.canvas = [[' '] * self.width for _ in range(self.height)]
257
+
258
+ def plot(self, t, r, sym='*'):
259
+ row = int(self.center_y - r * self.y_scale * math.cos(t))
260
+ col = int(self.center_x + r * self.x_scale * math.sin(t))
261
+
262
+ if 0 <= row < self.height and 0 <= col < self.width:
263
+ self.canvas[row][col] = sym
264
+
265
+ def current_time(self):
266
+ if self.timezone is None:
267
+ return datetime.now().astimezone()
268
+ return datetime.now(self.timezone)
269
+
270
+ def draw(self):
271
+ self.reset_canvas()
272
+ now = self.current_time()
273
+ h = now.hour * 6.283 + now.minute / 9.549
274
+ min_size = 0.02
275
+ hr_size = 0.01
276
+ hr_fmt = 12
277
+
278
+ for i in range(999):
279
+ self.plot(i / 158.0, 24)
280
+ self.plot(h, i * min_size, "▓")
281
+ self.plot(h / hr_fmt, i * hr_size, "█")
282
+ for q in range(12):
283
+ self.plot(q / 1.91, 24 - i * 0.005, '•')
284
+
285
+ rendered = '\n'.join(''.join(row) for row in self.canvas)
286
+ time_str = now.strftime("%H:%M")
287
+ return rendered, time_str
288
+
289
+ def render(self, refresh=False):
290
+ try:
291
+ while True:
292
+ print('\n' * 4)
293
+ rendered, time_str = self.draw()
294
+ print(zone_label(self.zone_name).center(self.width))
295
+ print(rendered)
296
+ print(" " * int(((self.width / 2) - 2)) + time_str)
297
+ if refresh:
298
+ print("\n[ctrl + c] to terminate", end='')
299
+ time.sleep(60)
300
+ clear()
301
+ else:
302
+ break
303
+ except KeyboardInterrupt:
304
+ return
305
+
306
+
307
+ class SmallClock:
308
+ def __init__(self, zone_name=None, stretch_x=False):
309
+ self.zone_name = zone_name
310
+ self.timezone = load_zone(zone_name)
311
+ self.stretch_x = stretch_x
312
+ self.base_width = 13
313
+ self.height = 11
314
+ self.width = self.base_width * 2 if stretch_x else self.base_width
315
+ self.center_x = self.width // 2
316
+ self.center_y = self.height // 2
317
+ self.radius = self.center_y - 1
318
+ self.x_scale = 2 if stretch_x else 1
319
+
320
+ def current_time(self):
321
+ if self.timezone is None:
322
+ return datetime.now().astimezone()
323
+ return datetime.now(self.timezone)
324
+
325
+ def hand_position(self, step, radius):
326
+ angle = step * (math.pi / 6)
327
+ row = int(round(self.center_y - math.cos(angle) * radius))
328
+ col = int(round(self.center_x + math.sin(angle) * radius * self.x_scale))
329
+ return row, col
330
+
331
+ def draw_line(self, canvas, step, radius, symbol):
332
+ end_row, end_col = self.hand_position(step, radius)
333
+ for distance in range(1, radius + 1):
334
+ fraction = distance / radius
335
+ row = int(round(self.center_y + (end_row - self.center_y) * fraction))
336
+ col = int(round(self.center_x + (end_col - self.center_x) * fraction))
337
+ if 0 <= row < self.height and 0 <= col < self.width:
338
+ canvas[row][col] = symbol
339
+
340
+ def draw(self):
341
+ now = self.current_time()
342
+ canvas = [[' '] * self.width for _ in range(self.height)]
343
+
344
+ for tick in range(12):
345
+ row, col = self.hand_position(tick, self.radius)
346
+ if 0 <= row < self.height and 0 <= col < self.width:
347
+ canvas[row][col] = 'o'
348
+
349
+ for label, step, offset in [('12', 0, -1), ('3', 3, 0), ('6', 6, 0), ('9', 9, 0)]:
350
+ row, col = self.hand_position(step, self.radius)
351
+ col += offset
352
+ for index, character in enumerate(label):
353
+ if 0 <= row < self.height and 0 <= col + index < self.width:
354
+ canvas[row][col + index] = character
355
+
356
+ minute_step = round(now.minute / 5) % 12
357
+ hour_step = round(((now.hour % 12) * 60 + now.minute) / 60) % 12
358
+ self.draw_line(canvas, hour_step, self.radius - 2, 'H')
359
+ self.draw_line(canvas, minute_step, self.radius - 1, 'M')
360
+
361
+ if 0 <= self.center_y < self.height and 0 <= self.center_x < self.width:
362
+ canvas[self.center_y][self.center_x] = '+'
363
+
364
+ rendered = '\n'.join(''.join(row) for row in canvas)
365
+ time_str = now.strftime("%H:%M")
366
+ return rendered, time_str
367
+
368
+
369
+ class MultipleClockRenderer:
370
+ def __init__(self, zone_names, stretch_x=False, padding=4):
371
+ self.zone_names = [zone for zone in zone_names if zone is not None][:4]
372
+ if not self.zone_names:
373
+ self.zone_names = ['local']
374
+ self.padding_string = ' ' * padding
375
+ self.clocks = [SmallClock(zone_name=zone, stretch_x=stretch_x) for zone in self.zone_names]
376
+
377
+ def render_once(self):
378
+ rendered_clocks = [clock.draw() for clock in self.clocks]
379
+ clock_lines = [rendered.splitlines() for rendered, _ in rendered_clocks]
380
+ widths = [len(lines[0]) for lines in clock_lines]
381
+
382
+ top_labels = [zone_label(zone)[:width].center(width) for zone, width in zip(self.zone_names, widths)]
383
+ bottom_labels = [time_str.center(width) for (_, time_str), width in zip(rendered_clocks, widths)]
384
+
385
+ composed = [self.padding_string.join(top_labels)]
386
+ for row in range(len(clock_lines[0])):
387
+ composed.append(self.padding_string.join(lines[row] for lines in clock_lines))
388
+ composed.append(self.padding_string.join(bottom_labels))
389
+ return '\n'.join(composed)
390
+
391
+ def render(self, refresh=False):
392
+ try:
393
+ while True:
394
+ print('\n' * 4)
395
+ print(self.render_once())
396
+ if refresh:
397
+ print("\n[ctrl + c] to terminate", end='')
398
+ time.sleep(60)
399
+ clear()
400
+ else:
401
+ break
402
+ except KeyboardInterrupt:
403
+ return
404
+
405
+
406
+ def cli():
407
+ if args.zone:
408
+ configure_zones()
409
+ elif args.countdown is not None:
410
+ countdownTimer(args.countdown[0])
411
+ elif args.multiple:
412
+ settings = load_or_create_settings()
413
+ zones = [settings.get(key) for key in [
414
+ 'TimeZone1',
415
+ 'TimeZone2',
416
+ 'TimeZone3',
417
+ 'TimeZone4',
418
+ ] if settings.get(key) is not None][:4]
419
+ stretch_x = settings.get('stretch_x', True)
420
+ renderer = MultipleClockRenderer(zones, stretch_x=stretch_x)
421
+ renderer.render(args._refresh)
422
+ else:
423
+ settings = load_or_create_settings()
424
+ stretch_x = settings.get('stretch_x', True)
425
+ main_zone = settings.get('MainZone', 'local')
426
+ clock = AnalogClock(stretch_x=stretch_x, zone_name=main_zone)
427
+ clock.render(args._refresh)
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: timy-cli
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Summary: Analog Clock and Timer
5
5
  Author-email: espehon <espehon@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/espehon/timy-cli
@@ -14,6 +14,11 @@ Classifier: Topic :: Utilities
14
14
  Requires-Python: >=3.8
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
+ Requires-Dist: colorama>=0.4.6
18
+ Requires-Dist: questionary>=2.1.0
19
+ Requires-Dist: tzdata>=2024.1
20
+ Requires-Dist: backports.zoneinfo; python_version < "3.9"
21
+ Dynamic: license-file
17
22
 
18
23
  # timy
19
24
  Simple console clock/timer
@@ -3,6 +3,7 @@ README.md
3
3
  pyproject.toml
4
4
  src/timy_cli/__init__.py
5
5
  src/timy_cli/__main__.py
6
+ src/timy_cli/settings.py
6
7
  src/timy_cli/timy.py
7
8
  src/timy_cli.egg-info/PKG-INFO
8
9
  src/timy_cli.egg-info/SOURCES.txt
@@ -0,0 +1,6 @@
1
+ colorama>=0.4.6
2
+ questionary>=2.1.0
3
+ tzdata>=2024.1
4
+
5
+ [:python_version < "3.9"]
6
+ backports.zoneinfo
File without changes
@@ -1,7 +0,0 @@
1
-
2
- import sys
3
-
4
- from timy import cli
5
-
6
- if __name__ == "__main__":
7
- sys.exit(cli())
@@ -1,189 +0,0 @@
1
-
2
- import math
3
- import time
4
- import sys
5
- import argparse
6
- import importlib.metadata
7
- from os import system, name, get_terminal_size
8
-
9
- from colorama import Fore, init
10
- init(autoreset=True)
11
-
12
-
13
- # Set __version__
14
- try:
15
- __version__ = f"timy {importlib.metadata.version('timy-cli')}"
16
- except importlib.metadata.PackageNotFoundError:
17
- __version__ = "Package not installed..."
18
-
19
-
20
- parser = argparse.ArgumentParser(description='Print an analog clock to the consol!', add_help=False)
21
-
22
- parser.add_argument('-?', '--help', action='help', help='Show this help message and exit.')
23
-
24
- parser.add_argument('-v', '--version', action='version', version='%(prog)s {version}'.format(version=__version__))
25
-
26
- parser.add_argument('-r', '--refresh', dest='_refresh', action='store_true', help='Refresh every minute until stopped')
27
-
28
- parser.add_argument('-c', '--continuous', dest='_refresh', action='store_true', help='Alias for --refresh')
29
-
30
- parser.add_argument('-t', '--timer', metavar='M', action='append', type=int, nargs='?', const=60, help='Countdown timer for [M] minutes (default 60)')
31
-
32
- args = parser.parse_args() #Execute parse_args()
33
-
34
-
35
-
36
-
37
- def clear():
38
- # for windows
39
- if name == 'nt':
40
- _ = system('cls')
41
- # for mac and linux(here, os.name is 'posix')
42
- else:
43
- _ = system('clear')
44
-
45
- def countdownTimer(Minutes):
46
- clear()
47
- paddingWithGlass = get_terminal_size()[1] - 32 # 32 is length of the following outputs
48
- if paddingWithGlass > 0:
49
- print("\n" * paddingWithGlass)
50
- print('''
51
- _.-"""-._
52
- _.-"" ""-._
53
- :"-. .-":
54
- '"-_"-._ _.-".-"'
55
- ||T+._"-._.-"_.-"|
56
- ||: "-.|.-" : ||
57
- || . ' || . ||
58
- || . '|| . ||
59
- || ';.:||' ||
60
- || '::|| ||
61
- || :|| ||
62
- || ':|| ||
63
- || .' :||. ||
64
- || ' . :||.' ||
65
- ||.'- .:|| -'._||
66
- .-'": .::::||:. : "'-.
67
- :"-.'::::::||::' .-":
68
- "-."-._"--:" .-".-"
69
- "-._"-._.-".-"
70
- "-.|.-"
71
- ''')
72
- try:
73
- for m in progressbar(range(Minutes), prefix="Timer: " +str(Minutes) + " Min ", sufix="(pass ← wait)"):
74
- time.sleep(60)
75
- if m == Minutes - 1:
76
- clear()
77
- print("\n" * get_terminal_size()[1])
78
- print(f'''{Fore.LIGHTGREEN_EX}
79
- +====+
80
- |( )|
81
- | )( |
82
- |(::)|
83
- +====+
84
- Timer has ended!''')
85
- print("\a")
86
- except:
87
- print(f"\n\n{Fore.YELLOW}[Timer interrupted]\n\n")
88
-
89
-
90
- def progressbar(it, prefix="", sufix=""): #progressbar --> prefix: [############################.............................] i/it
91
- size = abs(get_terminal_size()[0] - len(prefix) - len(sufix) - 16)
92
- count = len(it)
93
- def show(j):
94
- x = int(size*j/count)
95
- sys.stdout.write("%s[%s%s] %i ← %i %s \r" % (prefix, "#"*x, "."*(size-x), j, (count-j), sufix))
96
- sys.stdout.flush()
97
- show(0) #This prints the progressbar at 0 progress. Then next for loop renders the rest (stating at 1)
98
- for i, item in enumerate(it): #This is the 'i' in the comment on the 'def' line
99
- yield item
100
- show(i+1)
101
- sys.stdout.write("\n")
102
- sys.stdout.flush()
103
-
104
- def p(t,r,sym='*'):
105
- global c
106
- if stretch_x == True:
107
- c[int((clock_hight-r*math.cos(t))/2)][int(clock_hight+r*math.sin(t))]=sym
108
- else:
109
- c[int(clock_hight-r*math.cos(t))][int(clock_hight+r*math.sin(t))]=sym
110
-
111
- def analog_clock(_refresh):
112
- global c
113
- global stretch_x
114
- global clock_hight
115
-
116
- hr_fmt = 12
117
- stretch_x = True #--> if clock_width is twice that of clock_hight
118
- min_size = 0.02
119
- hr_size = 0.01
120
- clock_width = 50
121
- clock_hight = 25
122
-
123
- try:
124
- while True:
125
- print('\n' * 4)
126
- c = [[' '] * clock_width for i in range(clock_width)]
127
- t = time.localtime()
128
- h = t.tm_hour * 6.283 + t.tm_min / 9.549
129
- for i in range(999):
130
- p(i/158.0,24)
131
- p(h,i*min_size,"▓")
132
- p(h/hr_fmt,i*hr_size,"█")
133
- for q in range(12):
134
- p(q/1.91,24-i*.005,'•')
135
- for y in range(clock_hight):
136
- print(''.join(c[y]))
137
- print((" "*int(((clock_width/2)-2))) + str(time.localtime().tm_hour).zfill(2) + ":" + str(time.localtime().tm_min).zfill(2))
138
- if _refresh == True:
139
- print("\n[ctrl + c] to terminate", end='')
140
- time.sleep(60)
141
- clear()
142
- else:
143
- break
144
- except:
145
- #exit without error message
146
- return
147
-
148
- def mini_clocks(_refresh):
149
- global c
150
- global stretch_x
151
- global clock_hight
152
-
153
- hr_fmt = 12
154
- stretch_x = True #--> if clock_width is twice that of clock_hight
155
- min_size = 0.02
156
- hr_size = 0.01
157
- clock_width = 26
158
- clock_hight = 13
159
-
160
- try:
161
- while True:
162
- print('\n' * 4)
163
- c = [[' '] * clock_width for i in range(clock_width)]
164
- t = time.localtime()
165
- h = t.tm_hour * 6.283 + t.tm_min / 9.549
166
- for i in range(999):
167
- p(i/158.0,24)
168
- p(h,i*min_size,"▓")
169
- p(h/hr_fmt,i*hr_size,"█")
170
- for q in range(12):
171
- p(q/1.91,24-i*.005,'•')
172
- for y in range(clock_hight):
173
- print(''.join(c[y]))
174
- print((" "*int(((clock_width/2)-2))) + str(time.localtime().tm_hour).zfill(2) + ":" + str(time.localtime().tm_min).zfill(2))
175
- if _refresh == True:
176
- print("\n[ctrl + c] to terminate", end='')
177
- time.sleep(60)
178
- clear()
179
- else:
180
- break
181
- except:
182
- #exit without error message
183
- return
184
-
185
- def cli():
186
- if args.timer != None:
187
- countdownTimer(args.timer[0])
188
- else:
189
- analog_clock(args._refresh)
@@ -1 +0,0 @@
1
- colorama>=0.4.6
File without changes
File without changes
File without changes