Ubuntu 16.04: How do I add/remove pinned apps to Unity Launcher via terminal?
Contents:
- General theory of Launcher operation
- Possible ways of removing and appending to Unity launcher
- launcherctl.py utility
1. General theory of launcher operation
The Unity launcher essentially is a list of .desktop
files. They are essentially shortcuts that allow launching applications as well as performing custom actions. Typically they are stored in /usr/share/applications
, but can be also located in ~/.local/share/applications
, and anywhere else on the system. For the general case, I recommend storing such files in /usr/share/applications
for all users or ~/.local/share/applications
for each individual user.
The Dconf
database of settings allows storing list of such apps for Unity launcher and can be viewed and altered with gsettings
utility. For instance:
$ gsettings get com.canonical.Unity.Launcher favorites
['application://wps-office-et.desktop', 'application://wps-office-wpp.desktop', 'application://wps-office-wps.desktop', 'unity://running-apps', 'unity://devices']
$ gsettings set com.canonical.Unity.Launcher favorites "['wechat.desktop']"
$ gsettings get com.canonical.Unity.Launcher favorites
['application://wechat.desktop', 'unity://running-apps', 'unity://devices']
As you can see all the .desktop
files have application://
prefix on them, however it is not required when setting the launcher list. The items with unity://
prefix are not alterable and cannot be removed.
The gsettings get com.canonical.Unity.Launcher favorites
and gsettings set com.canonical.Unity.Launcher favorites
commands can be used to create functions in your ~/.bashrc
, for instance:
get_launcher()
{
gsettings get com.canonical.Unity.Launcher favorites
}
set_launcher()
{
# call this as set_launcher "['file1.desktop','file2.desktop']"
gsettings set com.canonical.Unity.Launcher favorites "$1"
}
Example:
$ set_launcher "['firefox.desktop','gnome-terminal.desktop']"
$ get_launcher
['application://firefox.desktop', 'application://gnome-terminal.desktop', 'unity://running-apps', 'unity://devices']
2. Possible ways of removing and appending to Unity Launcher
The simplest example has already been shown - via gsettings
utility. Removing and appending specific item requires parsing the gsettings
output. This can be done via sed
or awk
utilities , and with effort even in bash
. However, I find that python allows the easier approach and "path of least resistance". Thus, the examples provided here use gsettings
jointly with python.
Here's a case of removal:
$ gsettings get com.canonical.Unity.Launcher favorites|
> python -c 'import ast,sys; x =[]; x = [i for l in sys.stdin for i in ast.literal_eval(l)];
> x.pop(x.index("application://"+sys.argv[1])); print x' firefox.desktop
['application://gnome-terminal.desktop', 'unity://running-apps', 'unity://devices']
What is happening here ? We pass output of the gsettings get
via pipe to python. Python then reads standard input stream and using ast
library evaluates the text representation of the list, and converts it to actual list that python can recognize. This greatly simplifies the work - if this was awk or sed we would have to deal with removing and appending individual characters. Finally , we remove ( pop ) the second command-line argument ( indicated by sys.argv[1]
) by finding it's index in the list. We now have new list, which can be passed on further via pipe to gsettings set
The complete command is then this:
$ gsettings get com.canonical.Unity.Launcher favorites|
> python -c 'import ast,sys; x =[]; x = [i for l in sys.stdin for i in ast.literal_eval(l)];
> x.pop(x.index("application://"+sys.argv[1])); print "\""+repr(x)+"\""' firefox.desktop |
> xargs -I {} gsettings set com.canonical.Unity.Launcher favorites {}
Which can be nicely put into a ~/.bashrc
function like so:
remove_launcher_item()
{
gsettings get com.canonical.Unity.Launcher favorites|
python -c 'import ast,sys; x =[]; x = [i for l in sys.stdin for i in ast.literal_eval(l)];\
x.pop(x.index("application://"+sys.argv[1])); print "\""+repr(x)+"\""' "$1" |
xargs -I {} gsettings set com.canonical.Unity.Launcher favorites {}
}
A few things to note here is that we need to print again "string" representation of list enclosed into quotes and pass it via xargs
. The idea with appending is similar, except instead of pop
we use append
function:
append_launcher_item()
{
gsettings get com.canonical.Unity.Launcher favorites|
python -c 'import ast,sys; x =[]; x = [i for l in sys.stdin for i in ast.literal_eval(l)];\
x.append("application://"+sys.argv[1]); print "\""+repr(x)+"\""' "$1" |
xargs -I {} gsettings set com.canonical.Unity.Launcher favorites {}
}
Sample run:
$ get_launcher
['unity://running-apps', 'unity://devices', 'application://firefox.desktop']
$ append_launcher_item gnome-terminal.desktop
$ get_launcher
['unity://running-apps', 'unity://devices', 'application://firefox.desktop', 'application://gnome-terminal.desktop']
$
These functions don't necessarily have to be part of ~/.bashrc
. You can place them into a script as well
3. launcherctl.py utility
Over time, I've researched and build a set of functions in python that can effectively do the same as gsettings
utility. Putting the power of python together with those functions, I've made launcherctl.py
utility.
This is a work in progress and will be expanded to include more functions in future. For this specific question, I will leave the source code as it appears in first version. Further versions and improvements can be found on GitHub.
What are the advantages of this script compared to bash functions ? 1. This is a "centralized" utility with specific purpose. You don't have to have separate script/function for each action. 2. Simple to use, minimalist command-line options 3. When used in conjunction with other utilities, this provides for more readable code.
Usage:
As shown by the -h
command line option:
$ ./launcherctl.py -h
usage: launcherctl.py [-h] [-f FILE] [-a] [-r] [-l] [-c]
Copyright 2016. Sergiy Kolodyazhnyy.
This command line utility allows appending and removing items
from Unity launcher, as well as listing and clearing the
Launcher items.
--file option is required for --append and --remove
optional arguments:
-h, --help show this help message and exit
-f FILE, --file FILE
-a, --append
-r, --remove
-l, --list
-c, --clear
Command line usage is simple.
Appending:
$ ./launcherctl.py -a -f wechat.desktop
Removal:
$ ./launcherctl.py -r -f wechat.desktop
Clearing launcher completely:
$ ./launcherctl.py -c
Listing items on the launcher:
$ ./launcherctl.py -l
chromium-browser.desktop
firefox.desktop
opera.desktop
vivaldi-beta.desktop
As mentioned before, it can be used with other commands. For instance,adding from file:
$ cat new_list.txt
firefox.desktop
wechat.desktop
gnome-terminal.desktop
$ cat new_list.txt | xargs -L 1 launcherctl.py -a -f
Same can be used with removal of items given from a text file
Removing a 3rd item from the dash
button:
$ launcherctl.py -l | awk 'NR==3' | xargs -L 1 launcherctl.py -r -f
Obtaining source code and installation
Manual way:
- Create a directory
~/bin
. - Save source code from below into file
~/bin/launcherctl.py
- If you are
bash
user, you can source~/.profile
, or logout and login. The~/bin
directory will be added to your$PATH
variable automatically. For those who don't usebash
, add~/bin
to your$PATH
variable inside your shell configuration file, like so:PATH="$PATH:$HOME/bin
As I've mentioned latest changes to the code go into the GitHub repository. If you have git
installed, the steps are simpler:
git clone https://github.com/SergKolo/sergrep.git ~/bin/sergrep
echo "PATH=$PATH:$HOME/bin/sergrep" >> ~/.bashrc
-
source ~/.bashrc
. After this step, you can calllauncherctl.py
as any other command. Getting updates is as simple ascd ~/bin/sergrep;git pull
Obtaining code from GitHub without git
:
cd /tmp
wget https://github.com/SergKolo/sergrep/archive/master.zip
unzip master.zip
- If you don't have
~/bin
, make it withmkdir ~/bin
mv sergrep-master/launcherctl.py ~/bin/launcherctl.py
In all cases, the same rules apply - the script must live in a directory that is added to PATH
variable and must have executable permissions set with chmod +x launcherctl.py
Original source code:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Author: Serg Kolo , contact: [email protected]
# Date: Sept 24, 2016
# Purpose: command-line utility for controling the launcher
# settings
# Tested on: Ubuntu 16.04 LTS
#
#
# Licensed under The MIT License (MIT).
# See included LICENSE file or the notice below.
#
# Copyright © 2016 Sergiy Kolodyazhnyy
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import gi
from gi.repository import Gio
import argparse
import sys
def gsettings_get(schema, path, key):
"""Get value of gsettings schema"""
if path is None:
gsettings = Gio.Settings.new(schema)
else:
gsettings = Gio.Settings.new_with_path(schema, path)
return gsettings.get_value(key)
def gsettings_set(schema, path, key, value):
"""Set value of gsettings schema"""
if path is None:
gsettings = Gio.Settings.new(schema)
else:
gsettings = Gio.Settings.new_with_path(schema, path)
if isinstance(value,list ):
return gsettings.set_strv(key, value)
if isinstance(value,int):
return gsettings.set_int(key, value)
def puts_error(string):
sys.stderr.write(string+"\n")
sys.exit(1)
def list_items():
""" lists all applications pinned to launcher """
schema = 'com.canonical.Unity.Launcher'
path = None
key = 'favorites'
items = list(gsettings_get(schema,path,key))
for item in items:
if 'application://' in item:
print(item.replace("application://","").lstrip())
def append_item(item):
""" appends specific item to launcher """
schema = 'com.canonical.Unity.Launcher'
path = None
key = 'favorites'
items = list(gsettings_get(schema,path,key))
if not item.endswith(".desktop"):
puts_error( ">>> Bad file.Must have .desktop extension!!!")
items.append('application://' + item)
gsettings_set(schema,path,key,items)
def remove_item(item):
""" removes specific item from launcher """
schema = 'com.canonical.Unity.Launcher'
path = None
key = 'favorites'
items = list(gsettings_get(schema,path,key))
if not item.endswith(".desktop"):
puts_error(">>> Bad file. Must have .desktop extension!!!")
items.pop(items.index('application://'+item))
gsettings_set(schema,path,key,items)
def clear_all():
""" clears the launcher completely """
schema = 'com.canonical.Unity.Launcher'
path = None
key = 'favorites'
gsettings_set(schema,path,key,[])
def parse_args():
"""parse command line arguments"""
info="""Copyright 2016. Sergiy Kolodyazhnyy.
This command line utility allows appending and removing items
from Unity launcher, as well as listing and clearing the
Launcher items.
--file option is required for --append and --remove
"""
arg_parser = argparse.ArgumentParser(
description=info,
formatter_class=argparse.RawTextHelpFormatter)
arg_parser.add_argument('-f','--file',action='store',
type=str,required=False)
arg_parser.add_argument('-a','--append',
action='store_true',required=False)
arg_parser.add_argument('-r','--remove',
action='store_true',required=False)
arg_parser.add_argument('-l','--list',
action='store_true',required=False)
arg_parser.add_argument('-c','--clear',
action='store_true',required=False)
return arg_parser.parse_args()
def main():
""" Defines program entry point """
args = parse_args()
if args.list:
list_items()
sys.exit(0)
if args.append:
if not args.file:
puts_error(">>>Specify .desktop file with --file option")
append_item(args.file)
sys.exit(0)
if args.remove:
if not args.file:
puts_error(">>>Specify .desktop file with --file option")
remove_item(args.file)
sys.exit(0)
if args.clear:
clear_all()
sys.exit(0)
sys.exit(0)
if __name__ == '__main__':
main()
Additional notes:
- in the past I've created an answer that allows setting the launcher list from file: https://askubuntu.com/a/761021/295286
- I've also created a Unity indicator for switching between multiple lists. See instructions here: http://www.omgubuntu.co.uk/2016/09/launcher-list-indicator-update-ppa-workspaces