You are not logged in.

#1 2010-01-03 05:46:54

NeOnsKuLL
Member
From: Havana, Cuba
Registered: 2005-03-29
Posts: 117

myGtkMenu dinamic menu generator (auto-updated)

I switched from lxpanel to bmpanel2 about a week or 10 days ago. With lxpanel I used the menu it provides, but bmpanel2 doesn't cames with a menu widget. One of the solutions suggested is use myGtkMenu, called from a launcher. I use Openbox with obmenugen, which auto-updates the menu every time I call it (right click on desktop), so, I want the same approach with myGtkMenu. This means that if I install a new application and it puts a .desktop file (which is the most common behavior), when I right-click in the desktop, and the menu is shown, the application is already under the category it belongs.

The first version of obmenugen I wrote, was in python. obmenugen has evolved a lot since that, but the original python script, still having the main code needed to get the information from .desktop files, so, I tweaked the script and quickly have a usable version which generates a dinamic menu using the information present in .desktop files (/usr/share/applications/*.desktop).

By now, the script still not adding icons to the menu, so, you end having a menu that looks similar to the Openbox menu. The layout of the menu is quite fixed (I don't know the term in english to describe "rígido"). To configure it, you must tweak the script itself, but it's easy if you don't need so much.

In the future (I hope soon) I will be writing a modified version of obmenugen, also in the D programming language, with the same flexibility as obmenugen, but generating in myGtkMenu format.

Edit it as needed (CONFIG, MY_GTK_MENU_BINARY, MENUPOS), and simply run it directly (save the code to a file, give it execution permissions, and call it).

This is the script:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

__license__ = """
Copyright 2010 Pável Varela Rodríguez <neonskull@gmail.com>
   
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
"""
__author__ = "Pável Varela Rodríguez <neonskull@gmail.com>"
__version__ = "0.1"


###############################
### EDIT THIS TO YOUR NEEDS ###
###############################

CONFIG = {
"Terminal": "sakura",
"Editor" : "gedit",
"FileManager" : "thunar",
"RunCommand" : "gmrun",
"ExitCommand" : "oblogout"
}

MY_GTK_MENU_BINARY="~/bin/myGtkMenu"
MY_GTK_MENU_FILE="/tmp/myGtkMenu.txt"
ICONSIZE=-1        # Set to -1 to ignore icon
MENUPOS=(1, 30)    # Set both to -1 to ignore position

#############################
### DO NOT EDIT FROM HERE ###
#############################
import os, re

OB_CFG_HOME = os.path.expanduser("~/.config/openbox")

DEFAULT_CFG = {
"Terminal": "xterm",
"Editor" : "gedit",
"FileManager" : "thunar",
"RunCommand" : "gmrun"
}

LANG = {}
LANG["Long"] = os.environ["LANG"].split(".")[0]
LANG["Short"] = LANG["Long"].split("_")[0]

DOT_DESKTOP_LOCATIONS = [
"/usr/share/applications",
"/usr/share/applications/kde4"
]

FILTERS = {
"dotDesktop": re.compile('.+\.desktop'),
"Categories": re.compile('Categories=.+'),
"Name": re.compile('Name(\[(en_US|en)\]|)=.+'),
"Exec": re.compile('Exec=.+'),
"Icon": re.compile('Icon=.+'),
}

CATEGORIES_PRIORITY = ["01System", "02Settings", "03Development", "04Education",
                       "05AudioVideo", "06Audio", "07Video", "08Office",
                       "09Graphics", "10Network", "11Utility", "12Viewer",
                       "13Game"]

MENU_TEMPLATE = """
MENU_TOP
SEPARATOR
MENU_ACCESORIES
MENU_GRAPHICS
MENU_EDUCATION
MENU_AUDIOVIDEO
MENU_OFFICE
MENU_GAMES
MENU_NETWORK
MENU_DEVEL
MENU_SETTINGS
MENU_SYSTEM
SEPARATOR
#submenu = Openbox Settings
#\ticon = NULL
#\tMENU_OPENBOX
EXIT
"""
MENU_TOP = """item=Terminal\ncmd="%s"\nicon=NULL\n
item=Editor\ncmd="%s"\nicon=NULL\n
item=FileManager\ncmd="%s"\nicon=NULL\n
item=Run\ncmd="%s"\nicon=NULL\n"""
MENU_CATEGORY = """submenu = %s\n\ticon = NULL\n%s\n\n"""
ITEM_ACTION = """\titem=%s\n\tcmd="%s"\n\ticon=%s\n\n"""
MENU = ""

LISTS = {
"accesories": {"categories": ["utility"], "label": "Accesories", "files": []},
"graphics": {"categories": ["graphics"], "label": "Graphics", "files": []},
"education": {"categories": ["education"], "label": "Education", "files": []},
"audiovideo": {"categories": ["audiovideo", "audio", "video"], "label": "Audio & Video", "files": []},
"office": {"categories": ["office"], "label": "Office", "files": []},
"games": {"categories": ["game"], "label": "Games", "files": []},
"network": {"categories": ["network"], "label": "Network", "files": []},
"devel": {"categories": ["development"], "label": "Development", "files": []},
"settings": {"categories": ["settings"], "label": "Settings", "files": []},
"system": {"categories": ["system"], "label": "System Tools", "files": []}
}


def getDotDesktopFiles():
    filelist = []
    for directory in DOT_DESKTOP_LOCATIONS:
        utf8_dir = directory.decode('utf8')
        filelist += [os.path.join(utf8_dir, item.decode('utf8'))
                         for item in os.listdir(directory)
                             if FILTERS["dotDesktop"].match(item)]
    return filelist


def __cleanValue(value):
    for to_clean in ["%U", "%u", "%F", "%f", "\n"]:
        value = value.replace(to_clean, "")
    value = value.replace("&", "&")
    value = value.strip()
    return value
    

def getName(content):
    for line in content:
         if FILTERS["Name"].match(line):
            return __cleanValue(line.split("=")[1])
    return None


def getCategory(content):
    for line in content:
        if FILTERS["Categories"].match(line):
            categories = [item.replace("\n", "") for item in line.split("=")[1].split(";")]
            for cat in CATEGORIES_PRIORITY:
                if cat[2:] in categories:
                    return __cleanValue(cat[2:])
    return None


def getExecCmd(content):
    for line in content:
        if FILTERS["Exec"].match(line):
            return __cleanValue(line.split("=")[1])
    return None


def getIcon(content):
    if ICONSIZE > 0:
        for line in content:
             if FILTERS["Icon"].match(line):
                return __cleanValue(line.split("=")[1])
    return "NULL"


def parseDotDesktopFile(filepath):
    content = open(filepath, "r").readlines()
    name = getName(content)
    category = getCategory(content)
    exec_cmd = getExecCmd(content)
    icon = getIcon(content)
    if None in [name, category, exec_cmd]:
        return None
    else:
        return {"Name": name,
                "Category": category,
                "Exec": exec_cmd,
                "Icon": icon}


def fillLists():
    files = getDotDesktopFiles()
    for currentFile in getDotDesktopFiles():
        info = parseDotDesktopFile(currentFile)
        if info:
            for category_list in LISTS.keys():
                if info["Category"].lower() in LISTS[category_list]["categories"]:
                    LISTS[category_list]["files"].append(info)


def __genMenuTop():
    for key in CONFIG.keys():
        if CONFIG[key]: DEFAULT_CFG[key] = CONFIG[key]
    return MENU_TOP % (DEFAULT_CFG["Terminal"],
                       DEFAULT_CFG["Editor"],
                       DEFAULT_CFG["FileManager"],
                       DEFAULT_CFG["RunCommand"])


def __genCategoryMenu(category):
    items = ""
    LISTS[category]["files"].sort()
    for item in LISTS[category]["files"]:
        items += ITEM_ACTION % (item["Name"], item["Exec"], item["Icon"])
    if not items:
        return ""
    menu_label = LISTS[category]["label"]
    return MENU_CATEGORY % (menu_label, items)


def __genMenuOpenbox():
    items = ""
    AUTOSTARTSH = os.path.join(OB_CFG_HOME, "autostart.sh")
    if not os.path.exists(AUTOSTARTSH):
        f = open(AUTOSTARTSH, "w")
        f.close()
        os.chmod(AUTOSTARTSH, 0744)
    items += ITEM_ACTION[8:] % ("Configure Autostarted Applications", "%s %s" % (DEFAULT_CFG["Editor"], AUTOSTARTSH))
    for item in LISTS["settings"]["files"]:
        if item["Exec"] in ["obconf"]:
            items += ITEM_ACTION[:-1] % (item["Name"], item["Exec"], item["Icon"])
    return items


def __genMenu():
    fillLists()
    
    MENU = MENU_TEMPLATE.replace("MENU_TOP", __genMenuTop())
    
    for category in LISTS.keys():
        MENU = MENU.replace("MENU_%s" % category.upper(), __genCategoryMenu(category))
    
    #MENU = MENU.replace("MENU_OPENBOX", __genMenuOpenbox())
    
    MENU = MENU.replace("EXIT", "\nitem=Exit\ncmd=%s\nicon=NULL\n" % CONFIG["ExitCommand"])

    if ICONSIZE > 0:
        MENU = "iconsize = %d\n%s" % (ICONSIZE, MENU)
    
    if MENUPOS[0] > 0 and MENUPOS[1] > 0:
        MENU = "MenuPosition =  %d %d\n%s" % (MENUPOS[0], MENUPOS[1], MENU)

    return MENU



def __writeMenuFile(content):
    filePath = os.path.join("/tmp", MY_GTK_MENU_FILE)
    menuFile = open(filePath, "w")
    menuFile.write(content)
    menuFile.close()


if __name__ == "__main__":
    if len(os.sys.argv) > 2:
        print("Bad arguments length!!")
    elif "--help" in os.sys.argv or "-h" in os.sys.argv:
        print "La ayuda"
    elif len(os.sys.argv) == 1:
        menu = __genMenu() 
        #print(menu)
        __writeMenuFile(menu)
        os.system("%s %s" % (MY_GTK_MENU_BINARY, MY_GTK_MENU_FILE))
    else:
        print("Argument error: %s" % " ".join(os.sys.argv[1:]))

The only things you need to edit are those present in the first section of the script. If you know wath you do, can also change the order of the categories, editing MENU_TEMPLATE.

The most important are settings are:
- MY_GTK_MENU_BINARY: Which you must set up to the myGtkMenu binary (the full path or simply myGtkMenu if it is in $PATH)
- CONFIG: Put there the tools you use
- MENUPOS: If you will use the generated menu the way Openbox show it's own menu (ie. with compiz+emerald standalone), set this to (-1, -1), the menu will appear just under the mouse pointer. I setted it to (1, 30), because I have my bmpanel2 at screen's top, and the launcher is at the begining (the left).

Sorry about the ugly code, but this was written to do the job, quick and dirty.

I hope you enjoy it, and wait for next versions, which will be really flexible and fast, as obmenugen.

See you

Last edited by NeOnsKuLL (2010-01-05 05:32:48)


Intel Core 2 Duo E8400 3.0 GHz | 2x1GB 667MHz | 250+750GB Seageate SATAII | Samsung 19" TFT 1440x900
Openbox + obmenugen + PyTyle | bmpanel2 | oblogout | conky | pyBgSetter (with Esetroot as backend)
Projects: obmenugen, pyBgSetter

Offline

#2 2010-09-18 18:47:02

ans5685
Member
From: Kolkata/India
Registered: 2008-07-02
Posts: 14

Re: myGtkMenu dinamic menu generator (auto-updated)

I used this script and it as the icon names generated by it are the ones in the .desktop files, myGtkMenu was not able to show icons. So i inserted a piece of code from cbpanel to get the absolute path into the mygtkmenu file.

Here is the relevant piece of code:

#copied from the cbpanel code
def find_icon(icon):
    foundiconfile=None

    if icon == '':
        return foundiconfile

    if icon[0] == '/':
        return icon

    iconbase=('','Faenza','elementary','gnome','hicolor','locolor')
    iconpath='/usr/share/icons'
    sizelist =('', 'scalable', '256x256', '128x128', '64x64', '48x48', '32x32', '24x24')
    categorylist=('actions', 'apps','devices', 'categories','filesystems', 'places', 'status', 'stock', '')
    extensionlist = ('png', 'svg', 'xpm')

    iconimagelist=[]

    for extension in extensionlist:
        if (icon.find('.'+extension) != -1):
            icon = icon.replace('.'+extension,'')

    for size in sizelist:
        for extension in extensionlist:
            for category in categorylist:
                for iconbasecat in iconbase:
                    iconfile = iconpath+"/"+iconbasecat+'/'+size+'/'+category+'/'+icon+'.'+extension
                    iconimagelist.append(iconfile)

    for extension in extensionlist:
        iconfile = '/usr/share/pixmaps/'+icon+'.'+extension
        iconimagelist.append(iconfile)

    for extension in extensionlist:
        iconfile = '/usr/share/app-install/icons/'+icon+'.'+extension
        iconimagelist.append(iconfile)

    # Seek if the files in pre-generated list exists.. first match is the best
    # return it
    for iconimage in iconimagelist:
        if os.path.exists(iconimage):
            return iconimage

    return foundiconfile
    #end of copy from cbpanel code

And the relevant modification in NeOnsKuLL's script:

def getIcon(content):
    if ICONSIZE > 0:
        for line in content:
             if FILTERS["Icon"].match(line):
                #return __cleanValue(line.split("=")[1])
                return find_icon(__cleanValue(line.split("=")[1]))
    return "NULL"

This is my first attempt at doing anything with Python, so somebody with  any skills will do much better  than this copy-paste job big_smile

I have no idea why the icons were not displayed in the first place though. hmm

Hope somebody finds it useful as I found NeOnsKull's script.

Offline

Board footer

Powered by FluxBB