Sat, 20 Jan 2018 19:57:05 +0000
Semi-working menu items.
NOTES:
Python closures suck dog's balls... first and the last program I
write in Python until somebody fixes the brain-damaged scoping
that requires word _b=b hacks etc. to get normal behaviour.
See also http://math.andrej.com/2009/04/09/pythons-lambda-is-broken/
Rumps also sucks, apparently no way to update single menu items, one
always has to rebuild the entire menu. Spent hours trying to get it to
work until giving in.
1
4cdc9c1f6b28
basic scheduler structure draft, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff
changeset
|
1 | # |
4cdc9c1f6b28
basic scheduler structure draft, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff
changeset
|
2 | # MacOS UI |
4cdc9c1f6b28
basic scheduler structure draft, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff
changeset
|
3 | # |
4cdc9c1f6b28
basic scheduler structure draft, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff
changeset
|
4 | |
4cdc9c1f6b28
basic scheduler structure draft, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff
changeset
|
5 | import rumps |
10 | 6 | import time |
7 | import datetime | |
8 | import logging | |
9 | from threading import Lock | |
10 | ||
11 | def make_title(status): | |
12 | if status['type']=='scheduled': | |
13 | # Operation scheduled | |
14 | when=status['when'] | |
15 | now=time.time() | |
16 | if when<now: | |
17 | whenstr='overdue' | |
18 | else: | |
19 | diff=datetime.timedelta(seconds=when-now) | |
20 | if diff.days>0: | |
21 | whenday=datetime.date.fromtimestamp(when) | |
22 | whenstr='on %s' % whenday.isoformat() | |
23 | else: | |
24 | twhen=time.localtime(when) | |
25 | if twhen.tm_sec>30: | |
26 | # Round up minute display to avoid user confusion | |
27 | twhen=time.localtime(when+30) | |
28 | whenstr='at %02d:%02d' % (twhen.tm_hour, twhen.tm_min) | |
29 | detail='' | |
30 | if 'detail' in status and status['detail']: | |
31 | detail=status['detail']+' ' | |
32 | return "%s (%s%s %s)" % (status['name'], detail, status['operation'], whenstr) | |
33 | elif status['type']=='current': | |
34 | # Operation running | |
35 | return "%s (running: %s)" % (status['name'], status['operation']) | |
36 | else: # status['type']=='nothing': | |
37 | # Should be unscheduled, nothing running | |
38 | return status['name'] | |
39 | ||
1
4cdc9c1f6b28
basic scheduler structure draft, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff
changeset
|
40 | |
4cdc9c1f6b28
basic scheduler structure draft, etc.
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff
changeset
|
41 | class BorgendTray(rumps.App): |
9
aa121291eb0e
Rumps/Mac UI stuff is fucked and disables ^C etc.; threading doesn't help
Tuomo Valkonen <tuomov@iki.fi>
parents:
1
diff
changeset
|
42 | def __init__(self, name, backups): |
10 | 43 | self.lock=Lock() |
44 | self.backups=backups | |
45 | self.statuses=[None]*len(backups) | |
46 | ||
47 | with self.lock: | |
48 | # Initialise reporting callbacks and local status copy | |
49 | # (since rumps doesn't seem to be able to update menu items | |
50 | # without rebuilding the whole menu, and we don't want to lock | |
51 | # when calling Backup.status(), especially as we will be called | |
52 | # from Backup reporting its status, we keep a copy to allow | |
53 | # rebuilding the entire menu | |
54 | for index in range(len(backups)): | |
55 | b=backups[index] | |
56 | # Python closures suck dog's balls; hence the _index=index hack | |
57 | # See also http://math.andrej.com/2009/04/09/pythons-lambda-is-broken/ | |
58 | cb=(lambda obj, status, _index=index: | |
59 | self.__status_callback(obj, _index, status)) | |
60 | b.set_status_update_callback(cb) | |
61 | self.statuses[index]=b.status() | |
62 | ||
63 | menu=self.__rebuild_menu() | |
9
aa121291eb0e
Rumps/Mac UI stuff is fucked and disables ^C etc.; threading doesn't help
Tuomo Valkonen <tuomov@iki.fi>
parents:
1
diff
changeset
|
64 | |
10 | 65 | super().__init__(name, menu=menu, quit_button=None) |
66 | ||
67 | def __rebuild_menu(self): | |
68 | menu=[] | |
69 | for index in range(len(self.backups)): | |
70 | b=self.backups[index] | |
71 | title=make_title(self.statuses[index]) | |
72 | logging.info('TITLE: %s' % title) | |
73 | # Python closures suck dog's balls... | |
74 | # first and the last program I write in Python until somebody | |
75 | # fixes this brain damage | |
76 | cbm=lambda sender, _b=b: self.__menu_select_backup(sender, _b) | |
77 | item=rumps.MenuItem(title, callback=cbm) | |
78 | menu.append(item) | |
79 | ||
80 | menu_quit=rumps.MenuItem("Quit...", callback=self.my_quit) | |
81 | menu.append(menu_quit) | |
82 | ||
83 | return menu | |
84 | ||
85 | ||
86 | def my_quit(self, _): | |
9
aa121291eb0e
Rumps/Mac UI stuff is fucked and disables ^C etc.; threading doesn't help
Tuomo Valkonen <tuomov@iki.fi>
parents:
1
diff
changeset
|
87 | rumps.quit_application() |
aa121291eb0e
Rumps/Mac UI stuff is fucked and disables ^C etc.; threading doesn't help
Tuomo Valkonen <tuomov@iki.fi>
parents:
1
diff
changeset
|
88 | |
10 | 89 | def __menu_select_backup(self, sender, b): |
90 | #sender.state=not sender.state | |
91 | logging.debug("Manually backup '%s'", b.name) | |
92 | b.create(None) | |
9
aa121291eb0e
Rumps/Mac UI stuff is fucked and disables ^C etc.; threading doesn't help
Tuomo Valkonen <tuomov@iki.fi>
parents:
1
diff
changeset
|
93 | |
10 | 94 | def __status_callback(self, obj, index, status): |
95 | logging.debug('Status callbackup %s' % str(status)) | |
96 | with self.lock: | |
97 | self.statuses[index]=status | |
98 | logging.debug('Rebuilding menu') | |
99 | self.menu.clear() | |
100 | self.menu.update(self.__rebuild_menu()) | |
101 | ||
102 | ||
103 |