You are not logged in.

#1 2009-03-31 20:17:02

u_no_hu
Member
Registered: 2008-06-15
Posts: 453

dmenu supercharged

Hi all ,
This is a  simple dmenu based application launcher in python. It sorts applications based on usage so that your most used applications are always at the start. It also indexes files and folders and launches them in filebrowser/application of your preference based on extension.

Append a ";" to the program to start it in a terminal.

github http://github.com/TheWanderer/dmenu-python/tree/master
git git://github.com/TheWanderer/dmenu-python.git

PS: Please dont forget to suggest a name big_smile . And all your suggestions, ideas and feature requests are welcome.

Last edited by u_no_hu (2009-04-02 17:01:05)


Don't be a HELP VAMPIRE. Please search before you ask.

Subscribe to The Arch Daily News.

Offline

#2 2009-03-31 21:04:46

Inxsible
Forum Fellow
From: Chicago
Registered: 2008-06-09
Posts: 9,133

Re: dmenu supercharged

No offense but when you type in a couple of characters of the command, the dmenu lists only shows the ones which match -- which in my opinion is better than having to use the arrow keys to navigate to the command I want.

so I guess i am saying that I would much rather type the first 2-3 chars than hit the arrow key 2-3 times to highlight the command I want.

Last edited by Inxsible (2009-03-31 21:05:46)


Forum Rules

There's no such thing as a stupid question, but there sure are a lot of inquisitive idiots !

Offline

#3 2009-03-31 23:12:14

jumzi
Member
Registered: 2009-02-20
Posts: 69

Re: dmenu supercharged

Hmm yeah i have to agree with Inxsible... But a learning dmenu would be sweat

Offline

#4 2009-04-01 06:40:01

u_no_hu
Member
Registered: 2008-06-15
Posts: 453

Re: dmenu supercharged

@Inxsible.
I agree with that. It may depend upon what programs you have installed. If you can find a match on 2-3 chars then its great. But in my case i usually have a few programs which are similarly named and usually find a choice of typing the full name or using arrow keys. That is the reason i started searching for such a utility . I found only one(yeganesh ) but it was in haskell, so i wrote one for myself in python. I shared it so that anybody with a similar use case as mine won't have to reinvent the wheel . If anybody finds it useful, great... if not.. no issues as i am already using it for myself and saving a few keystrokes smile


Don't be a HELP VAMPIRE. Please search before you ask.

Subscribe to The Arch Daily News.

Offline

#5 2009-04-01 13:03:07

u_no_hu
Member
Registered: 2008-06-15
Posts: 453

Re: dmenu supercharged

Now it can launch command line programs in a terminal by appending a ";"


Don't be a HELP VAMPIRE. Please search before you ask.

Subscribe to The Arch Daily News.

Offline

#6 2009-04-01 15:18:18

Inxsible
Forum Fellow
From: Chicago
Registered: 2008-06-09
Posts: 9,133

Re: dmenu supercharged

u_no_hu wrote:

@Inxsible.
I agree with that. It may depend upon what programs you have installed. If you can find a match on 2-3 chars then its great. But in my case i usually have a few programs which are similarly named and usually find a choice of typing the full name or using arrow keys. That is the reason i started searching for such a utility . I found only one(yeganesh ) but it was in haskell, so i wrote one for myself in python. I shared it so that anybody with a similar use case as mine won't have to reinvent the wheel . If anybody finds it useful, great... if not.. no issues as i am already using it for myself and saving a few keystrokes smile

Oh of course ! It was just my opinion. I have seen your other work, namely sTiler and I do appreciate it. It was just that this would not be useful to me, so I aired my 2 cents.


u_no_hu wrote:

Now it can launch command line programs in a terminal by appending a ";"

Now that sir, is really useful !!!


Forum Rules

There's no such thing as a stupid question, but there sure are a lot of inquisitive idiots !

Offline

#7 2009-04-01 18:49:28

Zariel
Member
Registered: 2008-10-07
Posts: 446

Re: dmenu supercharged

I was going to fork this and do some work to make it dmenu-less, but I got bored with curses and pythons lack of getchar()

Offline

#8 2009-04-02 17:05:12

u_no_hu
Member
Registered: 2008-06-15
Posts: 453

Re: dmenu supercharged

File/Folder indexing support added. Its better to used dmenu-vertical with this

I hope these fields are self explanatory. Avoid indexing mp3s, jpegs etc. Large number of files will lower the performance.


CACHE_FILE = os.getenv('HOME')+'/.launch'
DMENU = "dmenu"
TERM = "urxvt -hold -e "
FOLDERS = ["/home/username"]
EXT = {
        '.txt':'urxvt -e vim',
        '.sh':'urxvt -e vim',
        '.py':'urxvt -e vim'
        }
FILEBROWSER = "pcmanfm"

Last edited by u_no_hu (2009-04-03 08:25:55)


Don't be a HELP VAMPIRE. Please search before you ask.

Subscribe to The Arch Daily News.

Offline

#9 2009-04-03 13:09:05

u_no_hu
Member
Registered: 2008-06-15
Posts: 453

Re: dmenu supercharged

Now appending ';' to a folder opens that folder in a terminal.

Also you can run custom commands like "vim /etc/rc.conf ;"  or "sudo pacman -Syu ;". These will also be run in terminal and the command will be added to the index.


Don't be a HELP VAMPIRE. Please search before you ask.

Subscribe to The Arch Daily News.

Offline

#10 2009-04-04 19:17:20

brisbin33
Member
From: boston, ma
Registered: 2008-07-24
Posts: 1,796
Website

Re: dmenu supercharged

hey nice script as usual.  just a couple questions.

is this zsh specific? i changed the zsh's to bash's as that's my shell.  is that ok, or am i misreading your python?

also, [DMENU, '-l', '10'] fails for me as my dmenu has no -l option.  i removed/replaced the options and it runs fine

if i could request an easier way to define dmenu options that'd be sweet.  adding a longer DMENU="" option at the beginning gives 'no such file or directory' errors in the dmenu() function.  seems you have to use [DMENU, '-X', 'xxx', '-Y', 'yyy'] from inside the function to get the font/colors.  or again, i'm probably mistaken and you've already got a method for that.  just let me know.

anyways not nitpicking very happy with it as is.  just letting you know my findings.

thanks.

Offline

#11 2009-04-04 21:04:00

gladstone
Member
Registered: 2009-01-03
Posts: 74

Re: dmenu supercharged

I haven't had chance to explore it properly yet, but I get the following errors:

$ python launch.py
usage: dmenu [-i] [-b] [-fn <font>] [-nb <color>] [-nf <color>]
             [-p <prompt>] [-sb <color>] [-sf <color>] [-v]
Traceback (most recent call last):
  File "launch.py", line 108, in <module>
    run()
  File "launch.py", line 81, in run
    out = dmenu(pgm_list)
  File "launch.py", line 73, in dmenu
    out = p.communicate("\n".join(pgm_list))[0]
  File "/usr/lib/python2.6/subprocess.py", line 671, in communicate
    return self._communicate(input)
  File "/usr/lib/python2.6/subprocess.py", line 1177, in _communicate
    bytes_written = os.write(self.stdin.fileno(), chunk)
OSError: [Errno 32] Broken pipe

Offline

#12 2009-04-05 10:51:56

b3n
Member
Registered: 2008-11-12
Posts: 20

Re: dmenu supercharged

I'd just do something like this:

#!/bin/sh

DMENU="dmenu -i"
TERMI="urxvt -e"
CACHE="$HOME/.dmenu_cache_recent"

touch $CACHE
MOST_USED=`sort $CACHE | uniq -c | sort -r | colrm 1 8`
RUN=`(echo "$MOST_USED"; dmenu_path | grep -vxF "$MOST_USED") | $DMENU` &&
(echo $RUN; head -n 99 $CACHE) > $CACHE.$$ &&
mv $CACHE.$$ $CACHE

case $RUN in
    *\;) exec `echo $TERMI $RUN | sed -e 's/;$//'`;;
    *)   exec $RUN;;
esac

Offline

#13 2009-04-05 12:54:24

u_no_hu
Member
Registered: 2008-06-15
Posts: 453

Re: dmenu supercharged

@brisbin
No, nothing specific to zsh in it, you can change it to bash aswell.
The -l 10 option is for dmenu-vertical. If you are using it for indexing files, then it is better to use vertical dmenu.

@b3n
Thanks for the script. Will try it out. I just wrote this as an exercise in learning python. smile


Don't be a HELP VAMPIRE. Please search before you ask.

Subscribe to The Arch Daily News.

Offline

#14 2009-04-05 12:56:06

u_no_hu
Member
Registered: 2008-06-15
Posts: 453

Re: dmenu supercharged

@gladstone ..
Install dmenu-vertical or remove the -l 10 after dmenu


Don't be a HELP VAMPIRE. Please search before you ask.

Subscribe to The Arch Daily News.

Offline

#15 2011-01-22 16:09:11

neon
Member
Registered: 2010-08-30
Posts: 12

Re: dmenu supercharged

Hi, I just wanted to point out that this awesome script is broken with Python versions >= 3.0 . I tried to update it myself but am apparently more incompetent at programming in Python than I thought I was. If somebody does eventually get around to updating it then please share it so I can steal your code :-).

Offline

#16 2011-01-22 16:13:33

joeDeuce
Member
From: Georgia, USA
Registered: 2010-05-26
Posts: 17
Website

Re: dmenu supercharged

neon wrote:

Hi, I just wanted to point out that this awesome script is broken with Python versions >= 3.0 . I tried to update it myself but am apparently more incompetent at programming in Python than I thought I was. If somebody does eventually get around to updating it then please share it so I can steal your code :-).

You can change your shebang in the script to #!/usr/bin/python2 in lieu of updating the code to Python 3. smile


We die young, all too often.

Offline

#17 2011-01-22 16:30:16

neon
Member
Registered: 2010-08-30
Posts: 12

Re: dmenu supercharged

Well, that seems obvious in retrospect :-P. Thanks!

Offline

#18 2011-01-24 02:36:23

Sara
Member
From: USA
Registered: 2009-07-09
Posts: 219
Website

Re: dmenu supercharged

neon wrote:

Well, that seems obvious in retrospect :-P. Thanks!

I just wanted to let you know that I've packaged this script in the AUR as dmenu-python-git and have used sed to fix the shebang line so it works (though other parts of the script will need modification for full functionality, such as the file extensions and user's home directory). Let me know how my PKGBUILD can be improved, if you choose to use it.

Also, this script won't work with spaces in filenames unless you modify the source accordingly. My fix of the source code was rather inelegant, but if any are interested, I'll post it.

Last edited by Sara (2011-01-24 02:38:14)


Registed Linux User 483618

Offline

#19 2011-02-02 14:54:26

alterecco
Member
Registered: 2009-07-13
Posts: 152

Re: dmenu supercharged

Thanks u_no_hu, for this lovely little script.

Here is a version that works with Python 3. It has been changed slightly, and since I personally don't use it to launch folders or launch in terminals, I have left those parts out. The script accepts dmenu arguments on the command line. Also, I named it dspawn, and the cache file is located in XDG_DATA_HOME/dspawn which is ~/.share/local/dspawn on most machines.

If there are any problem using non-ascii commands let me know and I will attempt to fix it

#!/usr/bin/python

# original: https://github.com/TheWanderer/dmenu-python

# TODO
# possibly pickle an ordered dict

import os, sys
import subprocess
import pickle

## pass dmenu args on command line
DMENU = ['dmenu']
DMENU.extend(sys.argv[1:])

UPDATE = "update_dspawn"
DATA_HOME = os.getenv("XDG_DATA_HOME")
if not DATA_HOME:
    print("XDG_DATA_HOME not set")
    exit(1)
CACHE_FILE = os.path.join(DATA_HOME, "dspawn")

def create_cache():
    programs = subprocess.getoutput("dmenu_path").split("\n")
    programs.append(UPDATE)

    ## return a dictionary with program name
    ## as key and count as value
    return dict.fromkeys(programs,0)

def get_cache():
    try:
        f = open(CACHE_FILE, 'rb')
        return pickle.load(f)
    except:
        return new_cache()

def store_cache(cache):
    with open(CACHE_FILE, 'wb') as f:
        pickle.dump(cache, f)

def new_cache():
    c = create_cache()
    store_cache(c)
    return c

def update_cache():
    new = create_cache()
    old = get_cache()
    for k in old:
        if k in new:
            new[k] = old[k]
    store_cache(new)
    return new

def main():
    cache = get_cache()
    ## sort cache according to most frequently used
    sorted_cache = sorted(cache.items(), key=lambda el: el[1], reverse=True)
    programs = '\n'.join([x[0] for x in sorted_cache])

    dmenu = subprocess.Popen(DMENU, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    command = dmenu.communicate(programs.encode('utf-8'))[0]

    if len(command) > 0:
        command = command.decode('ascii')
        if command == UPDATE:
            cache = update_cache()
        else:
            subprocess.Popen([command])

        ## if the command does not exist already
        ## then add it to the cache
        if not cache.get(command):
            cache[command] = 0

        cache[command] += 1
        store_cache(cache)

if __name__ == "__main__":
    main()

Last edited by alterecco (2011-02-02 16:27:57)

Offline

#20 2011-05-30 19:00:39

Cptn_Sandwich
Member
Registered: 2010-11-07
Posts: 13

Re: dmenu supercharged

i left the python2 version, but added mimeopen as a method to open files. also files are not added recursively and programs in path can be left out. much like the way kupfer does things.

#!/usr/bin/python
import pickle
import commands
import sys
import os
import subprocess
from operator import itemgetter
import glob

CACHE_FILE = os.getenv('HOME')+'/.launch'
DMENU = "dmenu"
TERM = "xterm -e "
CD_TERM = "urxvt -e zsh -c 'cd "
FOLDERS = ["/home/patrick/","/home/patrick/Downloads/"]
FILEBROWSER = "thunar"
INC_PROGS = False  #toggles whether programs in your $PATH will be included
RECURSIVE = False  #Toggles whether FOLDERS are added recursively


def create_cache():
    if INC_PROGS:
        prog_list = commands.getoutput("dmenu_path").split("\n")
    else:
        prog_list = []
    dirlist = []

    if RECURSIVE:
        for watchdir in FOLDERS:
             for root, dir , files in os.walk(watchdir):
                 if root.find('/.')  == -1 : 
                     for name in files:
                         if not name.startswith('.'):
                             if os.path.splitext(name)[1] in EXT.keys(): 
                                 dirlist.append(os.path.join(root,name))
                             else:
                                 dirlist.append(os.path.join(root,name))
                     for name in dir: 
                         if not name.startswith('.'):
                             dirlist.append(os.path.join(root,name))

    else:
        for lsdir in FOLDERS:
            print lsdir
            for i in glob.glob( lsdir+'*'):
                if 'Downloads' in lsdir:
                    print i
                dirlist.append(os.path.join(lsdir,i))

    dirlist.append("update_dmen")
    dirlist.sort()

    return dict.fromkeys(prog_list + dirlist,0)
    

def store(object,file):
    with open(file, 'w') as f:
        pickle.dump(object,f)
    f.close()

def create_new(file):
    cache = create_cache()
    store(cache,file)
    return cache

def retrieve(file,ifnotfound):
    try:
        with open(file,'r+') as f:
            obj = pickle.load(f)
        f.close()
        return(obj)
    except:
        return ifnotfound(file)

def update():
    cache_new  = create_cache()
    cache_old  = retrieve(CACHE_FILE,create_new)
    for k in cache_old:
        if k in cache_new:
            cache_new[k] = cache_old[k]

    store(cache_new,CACHE_FILE)

def dmenu(pgm_list):
    p = subprocess.Popen([DMENU,"-l","10"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    out = p.communicate("\n".join(pgm_list))[0]
    return out


def run():
    cache = retrieve(CACHE_FILE,create_new)
    sorted_list = sorted(cache.iteritems(), key=itemgetter(1), reverse=True)
    pgm_list = [ x[0] for x in sorted_list ]
    out = dmenu(pgm_list)
    if len(out) > 0 :
        if out == "update_dmen":
            update()
        elif out.endswith(';'):
            out = out[:-1]
            if os.path.isdir(out):
                os.system(CD_TERM + out + " && zsh '")
            else:
                os.system(TERM + out + " &")
        elif out.find('/') != -1:
            if os.path.isdir(out):
                os.system(FILEBROWSER + " " + out + " &")
            else:
                (main,ext) = os.path.splitext(out)
                if ext == '':
                    os.system(out + " &")
                else:
                    os.system('mimeopen -n' + " " + out + " &")
        else:
            os.system(out + " &")

        if not cache.has_key(out):
            cache[out] = 0
        cache[out] += 1
        store(cache,CACHE_FILE)

run()

Offline

Board footer

Powered by FluxBB