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.
# # MacOS UI # import rumps import time import datetime import logging from threading import Lock def make_title(status): if status['type']=='scheduled': # Operation scheduled when=status['when'] now=time.time() if when<now: whenstr='overdue' else: diff=datetime.timedelta(seconds=when-now) if diff.days>0: whenday=datetime.date.fromtimestamp(when) whenstr='on %s' % whenday.isoformat() else: twhen=time.localtime(when) if twhen.tm_sec>30: # Round up minute display to avoid user confusion twhen=time.localtime(when+30) whenstr='at %02d:%02d' % (twhen.tm_hour, twhen.tm_min) detail='' if 'detail' in status and status['detail']: detail=status['detail']+' ' return "%s (%s%s %s)" % (status['name'], detail, status['operation'], whenstr) elif status['type']=='current': # Operation running return "%s (running: %s)" % (status['name'], status['operation']) else: # status['type']=='nothing': # Should be unscheduled, nothing running return status['name'] class BorgendTray(rumps.App): def __init__(self, name, backups): self.lock=Lock() self.backups=backups self.statuses=[None]*len(backups) with self.lock: # Initialise reporting callbacks and local status copy # (since rumps doesn't seem to be able to update menu items # without rebuilding the whole menu, and we don't want to lock # when calling Backup.status(), especially as we will be called # from Backup reporting its status, we keep a copy to allow # rebuilding the entire menu for index in range(len(backups)): b=backups[index] # Python closures suck dog's balls; hence the _index=index hack # See also http://math.andrej.com/2009/04/09/pythons-lambda-is-broken/ cb=(lambda obj, status, _index=index: self.__status_callback(obj, _index, status)) b.set_status_update_callback(cb) self.statuses[index]=b.status() menu=self.__rebuild_menu() super().__init__(name, menu=menu, quit_button=None) def __rebuild_menu(self): menu=[] for index in range(len(self.backups)): b=self.backups[index] title=make_title(self.statuses[index]) logging.info('TITLE: %s' % title) # Python closures suck dog's balls... # first and the last program I write in Python until somebody # fixes this brain damage cbm=lambda sender, _b=b: self.__menu_select_backup(sender, _b) item=rumps.MenuItem(title, callback=cbm) menu.append(item) menu_quit=rumps.MenuItem("Quit...", callback=self.my_quit) menu.append(menu_quit) return menu def my_quit(self, _): rumps.quit_application() def __menu_select_backup(self, sender, b): #sender.state=not sender.state logging.debug("Manually backup '%s'", b.name) b.create(None) def __status_callback(self, obj, index, status): logging.debug('Status callbackup %s' % str(status)) with self.lock: self.statuses[index]=status logging.debug('Rebuilding menu') self.menu.clear() self.menu.update(self.__rebuild_menu())