I am looking for a tray icon indicator that - upon click on the icon - shows me the list of my recently used files. This would be a great way to quickly access these files.

- 83,767

- 13,317
- 17
- 87
- 161
4 Answers
Have your recently used files in the panel
With the script below, you can have an arbitrary number of recently used items at hand in the panel, e.g. 6 items:
...or 20 items:
...depending on your settings.
How it works
The setup exists of two items:
- an icon, named (exactly)
recent.png
- a script
Both need to be in one and the same folder. after that, simply run the script.
How to set up
Possibly, you need to install python3-gi
:
sudo apt-get install python3-gi
Then:
Copy the script below into an empty file, save it as
recused.py
#!/usr/bin/env python3 import signal import gi gi.require_version('Gtk', '3.0') gi.require_version('AppIndicator3', '0.1') from gi.repository import Gtk, AppIndicator3, GObject import time from threading import Thread import os import subprocess # --- set the number of recently used files to appear below n = 20 # --- home = os.environ["HOME"] recdata = os.path.join(home, ".local/share/recently-used.xbel") currpath = os.path.dirname(os.path.realpath(__file__)) class Indicator(): def __init__(self): self.app = 'show_recent' iconpath = os.path.join(currpath, "recent.png") self.indicator = AppIndicator3.Indicator.new( self.app, iconpath, AppIndicator3.IndicatorCategory.OTHER) self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE) self.indicator.set_menu(self.create_menu()) # the thread: self.update = Thread(target=self.check_recent) # daemonize the thread to make the indicator stopable self.update.setDaemon(True) self.update.start() def get_files(self): # create the list of recently used files used = [l for l in open(recdata) if \ all([ '<bookmark href="file://' in l, not "/tmp" in l, "." in l, ])] relevant = [l.split('="') for l in set(used)] relevant = [[it[1][7:-7], it[-2][:-10]] for it in relevant] relevant.sort(key=lambda x: x[1]) return [item[0].replace("%20", " ") for item in relevant[::-1][:n]] def create_menu(self): # creates the (initial) menu self.menu = Gtk.Menu() # separator menu_sep = Gtk.SeparatorMenuItem() self.menu.append(menu_sep) # item_quit.show() self.menu.show_all() return self.menu def open_file(self, *args): # opens the file with the default application index = self.menu.get_children().index(self.menu.get_active()) selection = self.menu_items2[index] subprocess.Popen(["xdg-open", selection]) def set_new(self): # update the list, appearing in the menu for i in self.menu.get_children(): self.menu.remove(i) for file in self.menu_items2: sub = Gtk.MenuItem(file) self.menu.append(sub) sub.connect('activate', self.open_file) # separator menu_sep = Gtk.SeparatorMenuItem() self.menu.append(menu_sep) # quit item_quit = Gtk.MenuItem('Quit') item_quit.connect('activate', self.stop) self.menu.append(item_quit) self.menu.show_all() def check_recent(self): self.menu_items1 = [] while True: time.sleep(3) self.menu_items2 = self.get_files() if self.menu_items2 != self.menu_items1: GObject.idle_add( self.set_new, priority=GObject.PRIORITY_DEFAULT ) self.menu_items1 = self.menu_items2 def stop(self, source): Gtk.main_quit() Indicator() # this is where we call GObject.threads_init() GObject.threads_init() signal.signal(signal.SIGINT, signal.SIG_DFL) Gtk.main()
In the head section of the script, set the number of items to show:
# --- set the number of recently used files to appear below n = 20 # ---
In one and the same folder save the icon below as (exactly)
recent.png
(right-click on it -> save as)
Test- run the script by the command:
python3 /path/to/recused.py
If all works fine, add to Startup Applications: Dash > Startup Applications > Add. Add the command:
/bin/bash -c "sleep 15 && python3 /path/to/recused.py"
Notes
- This script was based on this previous answer, in case you are interested in an alternative, be it that that one was application-specific, and used a periodically updated
.desktop
file. - The script was tested on 16.04, but should work on earlier versions as well.
- Due to a bug in the
recently-used.xbel
file, sometimes a file is mentioned twice; once with, and once without extension. I solved that by filtering the latter out. The consequence is that files without extension won't appear in the list. If that is an issue, let me know, we can try to find another fix in that case. - If a file does not exist (anymore), the indicator simply (obviously) does not open the file. Most likely, I will polish the script a bit more to filter out those outdated entries from the
recently-used.xbel
file.
Explanation
Many applications, editing files and using a Gtk window, keep track of opened files in the file: ~/.local/share/recently-used.xbel
. "Records" include the date & time, the application and the file that was opened.
The script reads the .xbel
file, sorts the items by date/time, includes the first n- (depending on your settings) files in the menu of the indicator. The menu is updated (only if necessary) every 3 seconds as it is. Subsequently, when an item is selected, the file is opened with the command:
xdg-open <file>
Thus the selected file will be opened with the default application. It is very well possible to make sure the file is opened with the actual application it was last opened. That takes a somewhat more sophisticated parsing however. I will add that as an option to the planned ppa version on Launchpad.
The same goes for an option window to set a few options, like the number of files to show etc.
Note
The indicator is now merged with a few other things in this one.

- 83,767
-
1Nice answer! Why not to turn this script into a full-featured app, adding a preferences option? Or just fork the xfce panel app to make it work in Gnome? Any of this is too advanced for me. – Jul 29 '16 at 16:26
-
@luchonacho thanks! Yeah, I think this one should move to launchpad, get a proper settings menu, a ppa etc. This is the first "holiday" version :) Thanks for the suggestion! – Jacob Vlijm Jul 29 '16 at 16:31
-
Jacob, should I address a bug directly here? Umlaut characters (e.g. ü) are not properly interpreted.
ü
turns into%C3%9C
. – orschiro Jul 31 '16 at 19:16 -
Alright, take your time! Just wanted to make sure I won't forget it. :-) – orschiro Jul 31 '16 at 19:28
-
@orschiro Hi orschiro, I just found out that even gedit and LibreOffice do not handle umlauts well. Even gedit's "recently used" breaks on them, since the files are not mentioned correctly in the xbel- file. Not much I can do about that. – Jacob Vlijm Aug 02 '16 at 10:06
-
Hello Jacob, another one: 1. I do a recording to create a new
.ogg
file. 2. I open the indicator menu but cannot find it there. 3. I play the file with my player. 4. I open the indicator menu again and now it's visible there. Any ideas why? – orschiro Aug 04 '16 at 05:23 -
@orschiro could it be that the recorder is no Gtk application? Only Gtk apps keep track of files in the xbel file. – Jacob Vlijm Aug 04 '16 at 06:19
-
It concerns Audio Recorder, which I believe, is a GTK app. At least it provides a GTK-like interface. – orschiro Aug 04 '16 at 16:47
-
@JacobVlijm I think I found the problem! As shown by the screencast, Audio Recorder doesn't open a GTK window after finishing the recording. Thus it doesn't show up in xbel. I guess nothing you can do here, correct? – orschiro Aug 06 '16 at 04:03
-
@orschiro I am afraid so. The indicator depends on what the xbel file shows. – Jacob Vlijm Aug 06 '16 at 05:52
-
@JacobVlijm thanks for the clarification! I filed a feature request for Audio Recorder there: https://bugs.launchpad.net/audio-recorder/+bug/1610508 – orschiro Aug 06 '16 at 07:30
-
@JacobVlijm another feature request: Can the indicator support drag & drop? For example, I click on an entry and thereby can drag the file onto an upload field in my browser to directly upload it to a website. This would be very handy! :-) – orschiro Aug 10 '16 at 04:31
-
Hi @orschiro, thanks for the suggestion. I think that would be kind of out of the scope of the indicator, and nor t what you'd expect t to do :) – Jacob Vlijm Aug 10 '16 at 10:14
-
Hi @JacobVlijm, I see your point and understand that this might be too much although I would still see it as a very nice gimmick feature if it was there. :-) – orschiro Aug 10 '16 at 15:10
-
@JacobVlijm just an idea—how would the script have to look like to list the latest files found in
~/Downloads
instead ofRecent
? – orschiro Oct 27 '16 at 05:57 -
@RobertOrzanna Do you mean the latest (added) files, or the most recently used in Downloads? both are easy :) – Jacob Vlijm Oct 27 '16 at 06:57
-
-
@JacobVlijm fantastic! Happy to test and provide feedback at any stage. :-) – orschiro Oct 27 '16 at 08:55
Although you do not mention which Ubuntu flavour you are using, there is a nice app for Xubuntu. More specifically, it is part of Xfce, so you might still be able to use it in Ubuntu if you use Xfce. The app is called Places.
To enable, right-click on the panel, and from the menu select Panel -> Add New Items..., and there choose Places. This shows a list of folders and Recent Documents you just opened. See below:
You can configure it nicely too:
Here is the project's page.
If you are not using Xfce, you can install it very easily, as explained in this answer. Simply do sudo apt-get install xfce-panel
, and then run (see link). Notice that Xfce panel might not be compatible with all the other apps in your Unity (or other) panel. In my experience I have had no problem at all with it, so it might still be a good solution.
-
Very nice answer and sorry for forgetting to mention Ubuntu 16.04 Unity. Will I be able to install and run this Xfce places plugin on Unity? – orschiro Jul 28 '16 at 08:36
-
That is something I wonder myself too. The package is here. Why don't you give it a try? See if
sudo apt-get install xfce4-places-plugin
works. I can't because it comes by default. – Jul 28 '16 at 08:54 -
-
Can you find Places from Add New item in the panel menu? It might not integrate easily with Unity then. You might need to switch to the entire
xfce-panel
. Try this answer. Then it should work! – Jul 28 '16 at 09:07 -
Bummer, I really like the Unity panel and don't want to loose it just for one places plugin. :-( – orschiro Jul 28 '16 at 09:09
-
Agreed. Hopefully Jacob can provide an answer that works for you. This leads to a nice question: how do you integrate an Xfce app into a Unity panel? I see no related question. A spin-off from this one maybe? – Jul 28 '16 at 09:19
-
-
Not a tray icon but a quicklist script for the unity launcher (works with Umlauts), and you can pin files as well: Ubuntu-Recentquicklists
[detailed install instructions here]
Quick instructions:
- download .zip
- extract
- make
install.sh
andubuntu-recentquicklists.py
executable - run
install.sh
script
Features:
- automatically adds recent files
- manually add non-recent files / pin recent files
- specify how many items per list you want and
- how old the items may be
- resolve symbolic links
- display the full path (or just the filename, as in the screenshot)

- 181
-
This looks very promising. Thanks for sharing! Can you explain in your answer what the pinning feature does? – orschiro Sep 26 '16 at 09:13
-
1Recent files go away if not accessed for a while, but if you e.g. have an important .xls file you want to stay, you can enter pinning mode (click on filepinning), click on the stuff you want to stay (until you unpin it), and exit pinning mode (click on filepinning once again). This writes the pinned files to the config-file as well, so rebooting etc. doesn't remove them – hirsch Sep 26 '16 at 09:46
Update Feb/24/2017: the indicator now has option to pin web links.
Introduction
Files Indicator presented below is simple indicator for accessing user's files and folders. It allows checking recently used files, bookmarking files and directories.
Update: indicator now also supports launching .desktop files that have been pinned. For instance if you have firefox.desktop pinned, it will launch firefox. Thus, the indicator can be used as quick launcher for programs. The feature is on the way into PPA at the time of writing (Nov 19, 7:53 pm GMT, should take about 24 hours to process), but is already on github as well as here, in updated source code.
Obtaining the indicator
The indicator is available from my personal PPA as well as GitHub. Use the following steps to obtain it:
sudo add-apt-repository ppa:1047481448-2/sergkolo
sudo apt-get update
sudo apt-get install files-indicator
Source code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Author: Serg Kolo , contact: 1047481448@qq.com
# Date: November 19 , 2016
# Purpose: appindicator for accessing files and folders
# 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
gi.require_version('AppIndicator3', '0.1')
gi.require_version('Notify', '0.7')
from gi.repository import GLib as glib
from gi.repository import AppIndicator3 as appindicator
from gi.repository import Gtk as gtk
from gi.repository import Gio
from gi.repository import Notify
from collections import OrderedDict
# from collections import OrderedDict
import urllib.parse
import subprocess
import copy
import shutil
import dbus
import math
import json
import os
class FilesIndicator(object):
def __init__(self):
self.app = appindicator.Indicator.new(
'files-indicator', "document-open-recent",
appindicator.IndicatorCategory.HARDWARE
)
self.user_home = os.path.expanduser('~')
filename = '.pinned_files.json'
self.pinned_list = os.path.join(self.user_home,filename)
self.config = os.path.join(self.user_home,'.files_indicator.json')
self.max_items = 15
self.name_length = 20
self.read_config()
self.app.set_status(appindicator.IndicatorStatus.ACTIVE)
self.cached_files = self.get_recent_files()
self.make_menu()
self.update()
def read_config(self,*args):
config = {}
try:
with open(self.config) as f:
config = json.load(f)
except FileNotFoundError:
print('>>> ',self.config,' not found.Creating one')
f = open(self.config,'w')
config = {'max_items':self.max_items,
'name_length':self.name_length
}
json.dump(config,f,indent=4)
f.close()
except json.JSONDecodeError:
print(">>> Can't read ",self.pinned_list,',may be corrupt')
return None
else:
self.max_items = config['max_items']
self.name_length = config['name_length']
def add_menu_item(self, menu_obj, item_type, image, label, action, args):
""" dynamic function that can add menu items depending on
the item type and other arguments"""
menu_item, icon = None, None
if item_type is gtk.ImageMenuItem and label:
menu_item = gtk.ImageMenuItem.new_with_label(label)
menu_item.set_always_show_image(True)
if '/' in image:
icon = gtk.Image.new_from_file(image)
else:
icon = gtk.Image.new_from_icon_name(image, 48)
menu_item.set_image(icon)
elif item_type is gtk.ImageMenuItem and not label:
menu_item = gtk.ImageMenuItem()
menu_item.set_always_show_image(True)
if '/' in image:
icon = gtk.Image.new_from_file(image)
else:
icon = gtk.Image.new_from_icon_name(image, 16)
menu_item.set_image(icon)
elif item_type is gtk.MenuItem:
menu_item = gtk.MenuItem(label)
elif item_type is gtk.SeparatorMenuItem:
menu_item = gtk.SeparatorMenuItem()
if action:
menu_item.connect('activate', action, *args)
menu_obj.append(menu_item)
menu_item.show()
def get_user_dirs(self,*args):
user_dirs = []
for index,val in glib.UserDirectory.__enum_values__.items():
if index == 8: continue
dir = glib.get_user_special_dir(index)
if dir: user_dirs.append(dir)
return user_dirs
def get_file_icon(self,*args):
if args[-1].endswith('.desktop'):
desk_file = Gio.DesktopAppInfo.new_from_filename(args[-1])
icon = desk_file.get_icon()
if type(icon) == Gio.ThemedIcon:
themed_name = icon.get_names()[0]
theme = gtk.IconTheme.get_default()
name = theme.lookup_icon(themed_name, 48, 0).get_filename()
if type(icon) == Gio.FileIcon:
name = icon.get_file().get_uri()
icon_url= urllib.parse.unquote(name).replace('file://','')
return icon_url
file = Gio.File.new_for_path(args[-1])
file_info = file.query_info("standard::*",0)
icon_string = file_info.get_icon().to_string()
if 'folder-' in icon_string:
return icon_string.split()[-2]
return icon_string.split()[-1]
def get_recent_files(self,*args):
manager = gtk.RecentManager.get_default()
try:
files = OrderedDict()
for index,item in enumerate(manager.get_items(),1):
uri = item.get_uri()
uri_decoded = urllib.parse.unquote(uri)
filepath = uri_decoded.replace('file://','')
if not os.path.exists(filepath): continue
basename = os.path.basename(uri_decoded)
files[basename] = filepath
if index == self.max_items:
break
except Exception as e:
print(e)
return None
finally: return files
def callback(self,*args):
self.update()
def update(self,*args):
current_files = self.get_recent_files()
if current_files != self.cached_files:
self.make_menu()
self.cached_files = current_files
glib.timeout_add_seconds(3,self.callback)
def add_submenu(self,top_menu,label):
menuitem = gtk.MenuItem(label)
submenu = gtk.Menu()
menuitem.set_submenu(submenu)
top_menu.append(menuitem)
menuitem.show()
return submenu
def make_menu(self):
if hasattr(self, 'app_menu'):
for item in self.app_menu.get_children():
self.app_menu.remove(item)
else:
self.app_menu = gtk.Menu()
recent = self.add_submenu(self.app_menu,'Recent Files')
recent_dict = self.get_recent_files()
content = [recent,gtk.ImageMenuItem,'gtk-add',
'Add to Recent Files',self.add_recent,[None]
]
self.add_menu_item(*content)
content = [recent,gtk.ImageMenuItem,'user-trash',
'Clear recent files list',self.clear_recent,[None]
]
self.add_menu_item(*content)
content = [recent,gtk.SeparatorMenuItem,
None,None,
None,[None]
]
self.add_menu_item(*content)
self.add_menu_item(*content)
if not recent_dict:
content = [recent,gtk.MenuItem,None,
'No items',None,None
]
self.add_menu_item(*content)
last = None
for i in recent.get_children():
last = i
last.set_sensitive(False)
else:
for name,data in recent_dict.items():
icon = self.get_file_icon(data)
content = [recent, gtk.ImageMenuItem,
icon, name[:self.name_length],
self.open_item, [data]
]
self.add_menu_item(*content)
# Pinned files
bookmarks = self.add_submenu(self.app_menu,'Pinned Files')
content = [bookmarks,gtk.ImageMenuItem,
'bookmark_add','Pin a file',
self.pin_file,[bookmarks,None]
]
self.add_menu_item(*content)
content = [bookmarks,gtk.ImageMenuItem,
'remove','Remove item',
self.remove_pinned,['files']
]
self.add_menu_item(*content)
content = [bookmarks,gtk.ImageMenuItem,
'user-trash','Remove All',
self.remove_all_pinned,[None]
]
self.add_menu_item(*content)
content = [bookmarks,gtk.SeparatorMenuItem,
None,None,
None,[None]
]
self.add_menu_item(*content)
self.add_menu_item(*content)
pinned_files = self.get_pinned()
if (pinned_files and
'files' in pinned_files.keys() and
pinned_files['files']):
for filepath in pinned_files['files']:
icon = self.get_file_icon(filepath)
content = [bookmarks,gtk.ImageMenuItem,
icon,os.path.basename(filepath),
self.open_item,[filepath]
]
self.add_menu_item(*content)
else:
content = [bookmarks,gtk.MenuItem,None,
'No items',None,None
]
self.add_menu_item(*content)
last = None
for i in bookmarks.get_children():
last = i
last.set_sensitive(False)
places = self.add_submenu(self.app_menu,'Places')
content = [places,gtk.ImageMenuItem,'add',
'Pin Directory',self.pin_dir,[None]
]
self.add_menu_item(*content)
content = [places,gtk.ImageMenuItem,
'remove','Remove Pinned',
self.remove_pinned,['dirs']
]
self.add_menu_item(*content)
content = [places,gtk.SeparatorMenuItem,
None,None,
None,[None]
]
self.add_menu_item(*content)
content = [places,gtk.MenuItem,None,
'Standard Dirs',None,None
]
self.add_menu_item(*content)
last = None
for i in places.get_children():
last = i
last.set_sensitive(False)
for dir in self.get_user_dirs():
icon = self.get_file_icon(dir)
content = [places,gtk.ImageMenuItem,icon,
os.path.basename(dir),self.open_item,[dir]
]
self.add_menu_item(*content)
content = [places,gtk.SeparatorMenuItem,
None,None,
None,[None]
]
self.add_menu_item(*content)
content = [places,gtk.MenuItem,None,
'Pinned Dirs',None,None
]
self.add_menu_item(*content)
last = None
for i in places.get_children():
last = i
last.set_sensitive(False)
if (pinned_files and
'dirs' in pinned_files.keys() and
pinned_files['dirs']):
for dir in pinned_files['dirs']:
icon = self.get_file_icon(dir)
print(icon)
content = [places,gtk.ImageMenuItem,icon,
os.path.basename(dir),self.open_item,[dir]
]
self.add_menu_item(*content)
else:
content = [places,gtk.MenuItem,None,
'No items',None,None
]
self.add_menu_item(*content)
last = None
for i in places.get_children():
last = i
last.set_sensitive(False)
content = [self.app_menu,gtk.SeparatorMenuItem,
None,None,
None,[None]
]
self.add_menu_item(*content)
content = [self.app_menu,gtk.ImageMenuItem,'exit',
'quit',self.quit,[None]
]
self.add_menu_item(*content)
self.app.set_menu(self.app_menu)
def check_directory(self,*args):
current_set = set(os.listdir(args[-1]))
return current_set - self.cached_set
def get_pinned(self,*args):
try:
with open(self.pinned_list) as f:
return json.load(f,object_pairs_hook=OrderedDict)
except FileNotFoundError:
print('>>> ',self.pinned_list,' not found')
return None
except json.JSONDecodeError:
print(">>> Can't read ",self.pinned_list,',may be corrupt')
return None
def pin_dir(self,*args):
# TODO
current_list = self.get_pinned()
if not current_list:
current_list = OrderedDict()
current_list['dirs'] = []
f = open(self.pinned_list,'w')
f.write("")
f.close()
if not args[-1]:
cmd = "zenity --file-selection --directory --separator || --multiple"
dirs = self.run_cmd(cmd.split())
else:
dirs = args[-1]
dir_list = []
if not dirs: return None
dir_list = dirs.decode().strip().split("||")
if not 'dirs' in current_list.keys():
current_list['dirs'] = []
for f in dir_list:
#icon = self.get_file_icon(f)
current_list['dirs'].append(f)
with open(self.pinned_list,'w') as f:
json.dump(current_list,f,indent=4)
self.make_menu()
def pin_file(self,*args):
current_list = self.get_pinned()
if not current_list:
current_list = OrderedDict()
current_list['files'] = []
f = open(self.pinned_list,'w')
f.write("")
f.close()
if not args[-1]:
cmd = "zenity --file-selection --separator || --multiple "
files = self.run_cmd(cmd.split())
else:
files = args[-1]
file_list = []
if not files: return None
file_list = files.decode().strip().split("||")
if not 'files' in current_list.keys():
current_list['files'] = []
for f in file_list:
#icon = self.get_file_icon(f)
current_list['files'].append(f)
with open(self.pinned_list,'w') as f:
json.dump(current_list,f,indent=4)
self.make_menu()
def remove_all_pinned(self,*args):
try:
#os.unlink(self.pinned_list)
with open(self.pinned_list) as f:
pinned = json.load(f)
pinned.pop('files')
with open(self.pinned_list,'w') as f:
json.dump(pinned,f,indent=4)
except:
pass
finally:
self.make_menu()
def remove_pinned(self,*args):
key = args[-1]
pinned = self.get_pinned()
if not pinned: return
cmd_str = "zenity --forms --add-combo Remove --combo-values"
vals = "|".join(pinned[key])
cmd = cmd_str.split() + [vals]
item = self.run_cmd(cmd)
if item:
path = item.decode().strip()
index = pinned[key].index(path)
pinned[key].pop(index)
with open(self.pinned_list,'w') as f:
json.dump(pinned,f,indent=4)
self.make_menu()
def add_recent(self,*args):
cmd = "zenity --file-selection --separator || --multiple "
files = self.run_cmd(cmd.split())
file_list = []
if not files: return
file_list = files.decode().strip().split("||")
items = ['file://' + f for f in file_list]
for f in items: gtk.RecentManager().get_default().add_item(f)
def clear_recent(self,*args):
try:
gtk.RecentManager.get_default().purge_items()
self.make_menu()
except:
pass
def open_item(self,*args):
#self.run_cmd(['xdg-open',args[-1]])
if args[-1].endswith('.desktop'):
desk_file = Gio.DesktopAppInfo.new_from_filename(args[-1])
return desk_file.launch_uris()
return subprocess.Popen(['xdg-open',args[-1]])
def quit(self,*args):
gtk.main_quit()
def run_cmd(self, cmdlist):
""" utility: reusable function for running external commands """
#new_env = dict(os.environ)
#new_env['LC_ALL'] = 'C'
try:
stdout = subprocess.check_output(cmdlist) #env=new_env)
except subprocess.CalledProcessError:
pass
else:
if stdout:
return stdout
def run(self):
""" Launches the indicator """
try:
gtk.main()
except KeyboardInterrupt:
pass
def quit(self, *args):
""" closes indicator """
gtk.main_quit()
def main():
""" defines program entry point """
indicator = FilesIndicator()
indicator.run()
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
gtk.main_quit()
Configurations
The indicator is configured via two json files stored in user's home directory.
~/.files_indicator.json
controls user interface , the length of menu entries and maximum numbers in the Recent Files Menu.
{
"name_length": 30,
"max_items": 10
}
The ~/.pinned_files.json
controls the lists of pinned files and folders. Each item is a list/array.
{
"dirs": [
"/home/xieerqi/\u56fe\u7247/Wallpapers"
],
"files": [
"/home/xieerqi/work_in_progress/videonauth_code.py",
"/home/xieerqi/work_in_progress/spin_button.py"
]
}

- 105,154
- 20
- 279
- 497
ppa:vlijm/placesfiles
(see the linked answer). – Jacob Vlijm Nov 21 '16 at 09:54