S60 – Un menù per i tuoi script in Python

PyMenu3Questa è la mia prima applicazione sviluppata utilizzando le classi in Python.
La sua funzione è molto semplice: una volta avviata, mostra un menù personalizzabile che permette l’avvio di uno script Python con la semplice pressione di un tasto.

È possibile personalizzare uno script per ogni tasto della tastiera, da 1 a #. Risulta molto utile se utilizzate spesso degli script in Python, dato che attualmente, per avviare uno script, bisogna utilizzare la shell di Python e cercare ogni volta lo script all’interno del file browser.
Con PyMenu basterà la semplice pressione di un tasto per avviare i vostri script.

Ne ho creato una versione .sis in modo da poterla mettere sul desktop o associare ad un tasto di funzione rapida.

– Download PyMenu.sis (3rd) –

L’id dell’applicazione è:
0xE263CB53

Segue il codice sorgente del programma.

import os
import e32
import e32dbm
import appuifw
import key_codes as key

class PyMenu:
    def __init__(self):
        # System directory - Config file
        self.SYS_DIR = u'C:\\data\\PyMenu\\'
        self.CONFIG_DIR = self.SYS_DIR + u'key_setting'
        if not os.path.isdir(self.SYS_DIR):
            os.mkdir(self.SYS_DIR)
        self.cfg = e32dbm.open(self.CONFIG_DIR, 'c')
        #
        # Keyboard
        self.set_keyboard = False # If true, key_handler set the key
        self.keyN = list(u'123456789*0#')
        self.keyV = [key.EKey1, key.EKey2, key.EKey3, key.EKey4,
                     key.EKey5, key.EKey6, key.EKey7, key.EKey8,
                     key.EKey9, key.EKeyStar, key.EKey0, key.EScancodeHash]
        #
        # Application body
        self.c = appuifw.Canvas(event_callback=self.key_handler,
                                redraw_callback=self.draw)
        self.lock = e32.Ao_lock()

    def draw(self, r=None):
        """Draw icons and text"""
        # Graphic size
        W, H = self.c.size
        WI = W / 3 # Icons width
        HI = H / 4 # Icons height
        ML = (W - 3 * WI) / 3 # Margin left
        MT = (H - 4 * HI) / 4 + 1 # Margin top

        # Graphic color - font
        BORDER = (220, 220, 220)
        ICONS = (157, 190, 217)
        NUMBERS = (200, 0, 0)
        FONT_ICONS = ('normal', 12)
        FONT_NUM = ('normal', 18)

        # Draw keyboard
        self.c.clear()
        index = 0 # Index of keyboard
        for row in range(4):
            for col in range(3):
                # Db requires unicode key value
                key = unicode(self.keyV[index])
                if key in self.cfg.keys():
                    script = self.cfg[key]
                    # Title is just filename without ext
                    title = os.path.basename(script)
                    title = os.path.splitext(title)[0].decode('utf-8')
                else:
                    # No title
                    title = u'-'
                # Icon rectangle
                icon = (col*WI, row*HI, col*WI + WI, row*HI + HI)
                self.c.rectangle(icon, fill=ICONS, outline=BORDER)
                # Text alignment
                length, height = self.c.measure_text(title, font=FONT_ICONS)[-2:]
                description = (icon[0] + WI/2 - length/2, icon[1] + HI/2 - 5)
                key_number = (icon[0] + WI/2 - 3, icon[1] + HI/2 + 20)
                self.c.text(description, title, font=FONT_ICONS)
                self.c.text(key_number,
                            self.keyN[index],
                            font=FONT_NUM,
                            fill=NUMBERS)
                index += 1

    def list_dir(self, path):
        """List a directory and return the selected file"""
        # Previous directory
        if path and path.endswith(r'\..'):
            # Show memories
            if path in [r'C:\\..', r'E:\\..', r'C:\..', r'E:\..']:
                files = ['C:', 'E:']
                path = ''
            # Show previous directory
            else:
                path = os.path.split(os.path.split(path)[0])[0]
                # List the dir
                files = os.listdir(path)
                files.sort()
                files[0:0] = [r'..']
        # List the dir
        elif path:
            files = os.listdir(path)
            files.sort()
            files[0:0] = [r'..']
        ufiles = [i.decode('utf-8') for i in files] # appuifw require unicode
        try:
            selected = files[appuifw.selection_list(ufiles)]
        except TypeError:
            # No file selected
            return None
        # If you select E: it mustn't return \E:!
        if path:
            return '\\'.join([path, selected])
        else:
            return selected

    def select_file(self, def_dir):
        """Select a script"""
        path = def_dir # Default directory
        temp = self.list_dir(path)
        while True:
            if temp and os.path.isfile(temp):
                return temp
            elif temp and os.path.isdir(temp):
                temp = self.list_dir(temp)
            else:
                return None

    def bind_key(self, n):
        """Bind a key to a script"""
        appuifw.app.screen = 'normal'
        script = self.select_file(r'C:\python')
        if script:
            self.cfg[unicode(n)] = script.decode('utf-8')
            appuifw.note(u'Script bound to "%s" key.' %
                self.keyN[self.keyV.index(n)], 'conf')
        else:
            del self.cfg[unicode(n)]
            appuifw.note(u'The "%s" key was deleted.' %
                self.keyN[self.keyV.index(n)], 'conf')
        appuifw.app.screen = 'large'

    def key_set(self):
        """Set global set_keyboard to set keyboard"""
        self.set_keyboard = True
        appuifw.note(u'Select a key', 'info')

    def key_handler(self, event):
        """Handle the keyboard"""
        press = event['scancode']
        if press in self.keyV:
            if self.set_keyboard:
                self.bind_key(int(press))
                self.set_keyboard = False
            else:
                if unicode(press) in self.cfg.keys():
                    namespace = {'__builtins__': __builtins__, '__name__': '__main__'}
                    try:
                        execfile(self.cfg[unicode(press)], namespace)
                    except Exception, error:
                        appuifw.note(u'Error occurred!', 'error')
                        appuifw.note(unicode(error), 'error')
                else:
                    n = appuifw.query(u'The "%s" key is not bound, bind now?' %
                        self.keyN[self.keyV.index(press)], 'query')
                    if n:
                        self.bind_key(int(press))

    def quit(self):
        try:
            self.cfg.close()
        except RuntimeError:
            pass
        self.lock.signal()
        appuifw.app.set_exit()

    def run(self):
        appuifw.app.screen = 'large'
        appuifw.app.orientation = 'portrait'
        appuifw.app.menu = [(u'Bind key', self.key_set)]
        appuifw.app.body = self.c
        appuifw.app.exit_key_handler = self.quit
        self.lock.wait()

app = PyMenu()
app.run()