ui.py

Sat, 20 Jan 2018 19:57:05 +0000

author
Tuomo Valkonen <tuomov@iki.fi>
date
Sat, 20 Jan 2018 19:57:05 +0000
changeset 10
76dbfb06eba0
parent 9
aa121291eb0e
child 11
0bff53095f28
permissions
-rw-r--r--

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())


mercurial