Prelude : My blog has been silent for quite some time. I was busy with my Masters thesis for couple of months. I am starting with my PhD , wrapping up things and slowly getting into the groove. I hope to settle into a routine where I have one long blog post per week. Let us see how it goes !
Nautilus is one of the most commonly used file manager for GNOME. One of the reasons for its popularity is its extensible architecture that allows developers to write scripts to customize it. Even if you are a command line person, extending Nautilus will result in dramatic increase in productivity. In this post, I will discuss the multiple ways in which Nautilus can be extended and the relative merits in each approach.
The first step in extending Nautilus is to find the set of actions that are tedious (atleast not straightforward) – Tasks that need additional clicks or switching to terminal to get completed. The next step is to determine if there are command line tools available to automate the task or the task can be completed by additional code – Again since you are extending Nautilus, the task involved has to relate to files or folders. For eg, opening a file as administrator is a "relevant" task but starting a nuclear war from Nautilus is not !
Informally, it is easy to extend Nautilus if your task falls in the following categories : Adding new entries in context menus (or in toolbar) that involve selected files/folders or current folder, add additional custom properties to files and display those details in list view, modify the properties page to display additional tabs with information etc. There are other possibilities but these are the most common ones.
If the above discussion sounds very abstract let us give some examples :
1. Open a terminal in the current folder or open the selected file as root.
2. Selecting a few audio files and adding them to Rhythmbox "Now Playing" queue.
3. Selecting a few files and sending them to thunderbird for attachment
4. Display IMDB details about the selected movie file in the property page etc.
The above examples show a gradient of use cases in the order of complexity. Some of them are so simple that they can automated using simple means. Tasks like (4) are tricky and need powerful tools. Selecting the right tool is important and we will discuss how to select the best approach.
Different Approaches to Customize Nautilus Context Menus
Like everything in Linux, there is always a variety of ways to customize Nautilus ranging from simple to complex. In this post, we will discuss the three most common approaches :
1. Using tools like nautilus-actions
2. Using Nautilus scripts
3. Using extensions written in nautilus-python
As before, all my discussion will be focused on Ubuntu but it should be relatively easy to apply to other Linux distributions.
Customizing Nautilus context menu using nautilus-actions
This is probably the easiest method. All you need to know is the shell command or script to perform the task. Nautilus actions provides an intuitive GUI to decide on the filters and the actions to be performed. This approach works best if the following conditions are met :
a. the task you want to be automated is easily translatable in command line
b. the command line utility accepts the arguments in a relatively simple form (eg space separate arguments etc)
c. The command line utility depends only on information pertaining to the selected file/folder.
To install the package, type the following at terminal (or install this package from Synaptic) :
sudo apt-get install nautilus-actions
Once the package is installed it can be accessed at System -> Preferences -> Nautilus Actions Configuration. I will only give a basic discussion here as there is a decent tutorial on how to create a new action at How To Add Custom Functionality To Nautilus.
Let us take a simple example – If I right click on a folder , I want a new menu which says, "Open Terminal Here" and when it is clicked, a new terminal must be opened and the working directory of the terminal must be the selected folder. The first step is to find if it can be expressible in a "single" command. Find the name of the command to invoke the terminal – it is called gnome-terminal. Read the man page to find that it accepts an argument "–working-dir". When provided, it starts the terminal in specified folder.
Now start the Nautilus Action from System -> Preferences -> Nautilus Actions Configuration . The steps are :
a. Create an action.
b. In "Action" tab, give the action some name and select "Display item in selection context menu". If you want it to be visible in the toolbar, it can done too ! Select "Display item in toolbar" and choose some icon.
c. In the command tab, give "gnome-terminal" as path and parameters as "–working-directory=%d/%f". The %d and %f are special codes that will be expanded when the command is invoked. To see other special codes and what they mean, click on the "Legend" button.
d. In "Conditions" tab, select "Only Folders".
Now open a new Nautilus window , select a folder and right click. Presto ! You will see a "Open Terminal Here" menu. Select it and you will see a new terminal open with the selected folder as its current directory !
Some Notes On Nautilus-actions
You can take a look at Nautilus-actions project page to find lot of actions that automate your common tasks. To include them in your system, download the schema and then import them. To import, Tools -> Import assistant -> select the schema file. There are literally hundreds of nautilus actions that you can use immediately.
Nautilus Actions is a very nifty GUI tool that makes creating context menus dramatically easy. The best part is that specifying the command and filters is very intuitive. I will say that for a vast majority of scenarios, Nautilus actions is all that is needed to automate some task. But the simple nature of the tool is also its biggest enemy – there are some tasks that are slightly more complex and nautilus action is not very useful in them. A simple example will be a task which needs two commands to complete. Consider task (2) above – Adding selected mp3 files to Rhythmbox queue. The problem here is that if the file is not already in the library, Rhythmbox will not add them to the queue. So this needs two commands – one to add them to library if not already present and then add them to queue. Tasks like this are tricky to implement using Nautilus actions.
Another class of tasks that are not solvable using Nautilus actions is the ones that have tricky input formats. If you go the "Command" tab and click on "Legend" button, you can see that the list of special codes available are limited. They satisfy majority of needs but not all. Let us take a simple example , say task (3) – send selected files to thunderbird as attachment. Thunderbird has a command to compose a new mail with attachments but the arguments must be "comma" separated. In this case you are out of luck with Nautilus actions and must look at more powerful tools.
Nautilus has an extensible architecture that allows scripts to be executed. There is usually a misconception that Nautilus scripts has to shell scripts. That is no longer the case. Nautilus allows you to run scripts written in any language as long as they are executable and it knows which program to invoke. This means that you can write scripts in shell , python, perl or any other language as long as the first line of the script says which program Nautilus will have to invoke to execute the script (ie the shebang line).
You can consider that Nautilus actions provides a convenient wrapper over Nautilus scripts .The statement is not exactly true , but it is a good start. Before discussing Nautilus scripts, let us compare the two approaches – Nautilus script allows you to do certain things that are not possible in Nautilus actions. For eg, you can have a huge shell script with multiple lines that gets executed instead of a single command. In my opinion, another important advantage is that it is possible to invoke a Nautilus script without selecting a file – For eg one that runs using just the current folder information. This is not possible in Nautilus actions. On the down side, scripts are always available. It is not possible to show a Nautilus script for only mp3 files or only for local files. Another issue is that Nautilus scripts are most useful for local files and folders. For remote files (eg ftp , smb or webdav) they are essentially useless – but Nautilus action based commands will work fine in this scenario.
How to write Nautilus Scripts
All the Nautilus scripts are in the folder $HOME/.gnome2/nautilus-scripts . You can access them in command line or by File -> Scripts -> Open Scripts folder. Alternatively, you can right click on a file/folder and select Scripts->Open Scripts folder from the context menu. Note that the scripts menu will be available only if you have some scripts in that folder.
To write a new script, create a file in $HOME/.gnome2/nautilus-scripts and change its permissions so that the "user" has executable permissions. Make sure that you have a shebang line which tells Nautilus which program to invoke to run this script. If you want to organize your scripts then create subfolders in the nautilus-scripts folder. All the subfolders become sub menus in the Nautilus context menu.
When your script is invoked by Nautilus , at the most 4 environmental variables will be set by it. They are NAUTILUS_SCRIPT_SELECTED_FILE_PATHS, NAUTILUS_SCRIPT_SELECTED_URIS, NAUTILUS_SCRIPT_CURRENT_URI and NAUTILUS_SCRIPT_WINDOW_GEOMETRY. NAUTILUS_SCRIPT_SELECTED_FILE_PATHS and NAUTILUS_SCRIPT_SELECTED_URIS are newline delimited variables about the selected files. The only difference is that one gives the name of the directory and other gives it as a URI. ie for a local file /home/blah/abc.txt , the first variable will /home/blah/abc.txt and second will give file:///home/blah/abc.txt . NAUTILUS_SCRIPT_CURRENT_URI gives the URI of the current location.
You can immediately see that the information available is pretty limited. You have to write the script using only these variables. The information is available as environment variables in shell script. For other languages, you may have to use additional modules to access them. Once you have written a script, it is time to test it ! The steps are :
a. Open a terminal and type "nautilus -q". This will close all instances of Nautilus.
b. Type "nautilus –no-desktop" . This is very useful when you are developing the script as it open Nautilus without opening your profile or preference data reducing the chance of data corruption.
c. Right click on a file/folder. You will notice that a new "Scripts" menu comes up and it has your script as the submenu. Selecting it will run your script with the options. You can alternatively run the script from File -> Scripts.
d. If you want to run a script without running on any specific file/folder, invoke the script using File -> Scripts. You must note that in this case only NAUTILUS_SCRIPT_CURRENT_URI will have meaningful value.
Debugging Tips for Nautilus Scripts
It is possible that your scripts may not work the first time. There are no easy way to debug the scripts. Here are some ideas that I find useful :
Case 1 : The script does not appear in Scripts menu (or the scripts menu itself does not occur)
1. Have you placed your script in $HOME/.gnome2/nautilus-scripts ?
2. Does it has executable permissions atleast for the user ?
Case 2 : The script appears in the menu but does not seem to work correctly
In this case, the bug is most likely in the logic of the code. Comment your entire script logic except the shebang line. Add few lines to echo the values of the environmental values into some file in say /tmp/blah. Quit nautilus and open it up. Execute the script. This will result in the arguments being dumped into the log file. Now manually set the environmental values with the values in log file and try executing the script from command line. Since we have the values of the environmental variables, we can simulate Nautilus execution from command line. Most likely , this will allow you to find the issue and fix it.
Notes on Nautilus Scripts
A huge list of Nautilus scripts are available at g-scripts page . Download the file
nautilus-scripts.tar.gz . Even though the information available to scripts are limited, many of them use shell commands in innovative ways. For eg, they use gdialog or zenity to show information graphically. Browse through the scripts to get an idea of what is possible using Nautilus scripts.
Nautilus scripts brings certain advantages and disadvantages. On one hand, they allow more complex actions to be performed. They are not limited to have only single command like Nautilus actions. They also can be executed without selecting any files. But the information available to the scripts is very limited – only 4 environmental variables. Also they are not useful when you invoke on remote files. It is also not possible to show the script only to files with selected attributes (eg only audio files). Of course, you can always return prematurely for files not matching your criteria but it is unnecessary work that must be repeated for all files.
Nautilus extensions can be considered as the Swiss army knife of customizing Nautilus. They have a relatively steep learning curve but once you learn them , you can extend Nautilus in ways that are simply not possible using other approaches. Nautilus extensions allows you to do tasks that are made possible by previous approaches and more . Some of the things that are possible are :
a) Adding menus for files/folders with certain attributes.
b) Adding new menus in toolbars.
c) Adding new custom attributes to files and folders and using them do perform special actions. It is also possible to display them in the detailed view. A simple example is to add say length of song for mp3 file and show it as a column in Nautilus.
d) Add new tabs in the property page – eg show additional IMDB information for movie files.
e) Add additional information about current location near the location bar.
The classical way to write extensions is in C. But gnome programming in C is bit tricky. I will not discuss them as there are easier ways available. If you are still interested , you can take a look at the Nautilus extensions API reference provided in the Notes section. You will have to compile your extension code and put the executable in /usr/lib/nautilus/extensions-2.0/ .
A cleaner and much easier alternative is to use Nautilus-Python . Nautilus-Python by itself is a C extension for Nautilus. What it does internally is that it exposes a "nautilus" module which can be used by Python programs to write Nautilus extensions in Python. The extension is quite well designed and provides a pythonic interface for programmers. To install this package,
sudo apt-get install python-nautilus
Nautilus Python Interfaces
1. nautilus.ColumnProvider – This allows your program to give additional columns (attributes) for files in the current location when the user is in List mode. Usually gets the information supplied by nautilus.InfoProvider . An example is to show length of the audio for mp3 files.
2. nautilus.InfoProvider - This file is called on all files that the user is currently viewing. You can add new attributes to the files , add new emblems or perform other tasks. An example is to provide track details about the current audio file.
3. nautilus.LocationWidgetProvider – This allows some widget to appear near the location bar. I cannot think of any practical example for this interface.
4. nautilus.MenuProvider - Allows you to provide custom menus for the selected files or folders. It is also possible to create toolbars or background menus.
5. nautilus.PropertyPageProvider – Allows you to augment existing property page (Right click -> Properties) with additional pages displaying specific information about the selected file. An example is to show MD5 of a file or show IMDB details for a movie file.
Writing Nautilus Python Extensions
You can write Nautilus extensions in Python using the Nautilus-Python extension. The scripts you write has to be placed in $HOME/.nautilus/python-extensions. If you have admin rights, you can also place them at /usr/lib/nautilus/extensions-2.0/python-extensions. Usually I prefer my scripts to be in my home folder.
The development process is very similar Nautilus scripts. The steps are :
1. Create a python file in $HOME/.nautilus/python-extensions .
2. Extend one or more of the interfaces and code the relevant functions in the interfaces.
3. Make the script file as executable.
4. Kill all instances of Nautilus using "nautilus -q" . Note that many web pages asks you to use "killall nautilus" but I prefer my apporach as it allows nautilus to die gracefully :)
5. Restart Nautilus. During development, using "nautilus –no-desktop" as it starts without reading profile preferences.
6. Test your extension. This will be extension specific due to the various capabilities of the nautilus python extensions.
A Closer Look at Nautilus Python Extensions
As discussed above, Nautilus Python exposes 5 interfaces. Based on your needs your class will inherit from one or more of the interfaces. Each interface will have few methods where the logic of your extension has to be included. After installation , you can take a look at /usr/share/doc/python-nautilus/examples/ for working examples. You can even copy them to $HOME/.nautilus/python-extensions and play around with them till you are comfortable. If you want to see all the functions that the interfaces expose, un-gzip the file /usr/share/doc/python-nautilus/examples/documentation.py.gz. This will produce a file documentation.py which provides the signature and basic information about the methods exposed by the interfaces. Let us take a brief look at them :
1. nautilus.ColumnProvider - It exposes a single function get_columns . This function has to return a sequence of nautilus.Column objects. As an example, the extension can return information like runtime or codec details for an mp3 file.
2. nautilus.InfoProvider - Exposes a single function update_file_info with a file as an argument. The function can update the file in various ways – for eg, it can set new custom attributes, change emblem or perform other actions on the file.
3. nautilus.LocationWidgetProvider - Exposes a single function get_widget . The function returns some gtk widget which will be displayed near the location bar.
4. nautilus.MenuProvider - Probably the most used interface. It exposes three functions : get_file_items, get_background_items and get_toolbar_items . The first two functions determine the entries that appear in the context menu. The difference is that get_background_items is usually called for a folder. The last function is used for toolbar items hence you must name the icon parameter of the menuItem.
5. nautilus.PropertyPageProvider - Exposes get_property_pages function where you reture one or more "pages" or tabs. An example is to create a new "IMDB Details" page which shows the movie information.
Debugging Nautilus Python Extensions
Debugging Nautilus Python extensions is pretty tricky. The main reason is that the "nautilus" package that is used extensively in the code is available only when the script is invoked from Nautilus. But all is still not lost. The following are some of the tips I learnt when I built my library of Nautilus extensions :)
Case 1 : You wrote a code for new entries in context menu but it either does not appear or works incorrectly
1. Is your script in $HOME/.nautilus/python-extensions ?
2. Is the script has its executable bit turned on?
3. Did you restart Nautilus ? (using nautilus -q and nautilus –no-desktop )
4. Check if the file is compiled in extensions folder – basically is there a .pyc file ? If not invoke the file with python to find the syntax error.
5. Import the logging module and check if the relevant functions are called.
6. If you think the menu is being shown incorrectly, then make sure you are using the relevant functions on the FileInfo class. The class has multiple utility functions and deciding the correct one is important.
Case 2 : You wrote a new property page and it does not appear
Try steps 1-5 from Case 1.
6. Check if all the widgets are visible (ie show method is called).
7. Check if the GUI that you try to render works normally. This can be done by moving the code that returns a page to another python file and putting the HBox/VBox into a gtk.window.
Notes On Nautilus Python Extensions
A good discussion about developing the extensions is at Nautilus Extensions. The discussion is primarily for C. A good C API reference can be found at Nautilus extensions API reference. For Python, nautilus-python Reference Manual provides a basic reference.
The reference manuals per se are very skimpy in details. The best way to learn how to code nautilus python extensions is looking at existing code. There are lot of excellent python extensions that you can use for learning. The simplest is the examples provided with the python-nautilus package. The files can be accessed at /usr/share/doc/python-nautilus/examples/ for working examples. To test them copy to $HOME/.nautilus/python-extensions , restart
nautilus using commands provided above. Nautilus pyextensions contains few useful scripts. Download the source code that is available as nautilus-pyextension-<version>.tar.gz file and play around with them. One of the most powerful examples for using Nautilus Python is RabbitVCS. You can take a look yourself at the amazing RabbitVCS screenshots. You can download the source using apt-get source command.
Generic Nautilus Debugging Tips
I found some websites that mention an alternate way to debug Nautilus extensions. It did not work for me but I will mention it here for the sake of completeness. Create a file called nautilus-debug-log.conf in your home folder with the following lines :
If Nautilus crashes for some reason, it supposedly writes into the file nautilus-debug-log.txt. If it works for you then great ! You have access to another debugging tool :)
Summary and Tips on Choosing the correct Approach
There are three approaches to extend or customize Nautilus. Using Nautilus actions, scripts or extensions. Each of them have their own pros and cons.
1. There is a high likelihood that some one else had a similar problem before. So before choosing one of the approaches, check if the code is available somewhere. Check the discussion (notes section) above for good resources.
2. Is your task very simple? Can it be completed with a single command line utility? Then use the Nautilus actions.
3. Try to complete your task as much as possible using Nautilus actions even if it involves some thing quick and dirty. Most of the case, you are the only person going to use it. One important thing to note is that the command that you provide in Nautilus action can also be your own script !
3. If your task primarily involves invoking multiple command line utilities , needs only basic information about the files and applicable only for local files then use Nautilus scripts.
4. If the task needs execution of multiple commands but need to be used only for certain files (eg only for txt files) , then try to write a script which invokes the appropriate commands. It should able to massage the inputs $* or $@ appropriately. Then use this script as the command to be invoked in Nautilus actions and use the filters to apply it to certain files.
5. If you need to invoke scripts even without selecting any items and the input needed is simple, then use scripts. If more complex processing is needed using extensions.
6. If you need to show the option in toolbar , then use extensions (and implement get_toolbar_items function)
7. If your task is complex – adding attributes to files, adding additional columns , adding new property pages etc use extensions.
8. Using Nautilus scripts does not mean your app is confined to text only. Use gdialog, zenity in Shell scripts for data entry and basic UI. If you use other languages, use the gtk library to develop complex UI. For eg, I have a script that queries opensubtitles.org site for subtitles and shows the result in a gtk window. Selecting one of the results downloads the subtitle file locally.
9. If you are very comfortable with Python, skip scripts and use extensions.
Sample Nautilus Python Extensions
I have a large library of actions, scripts and extensions. In this post, I want to share two of them.
1. Send file to thunderbird as attachment : The task is to select a set of files and compose a mail in Thunderbird with the selected files as attachment. Almost all the scripts / extensions that I see in the net did not work correctly and hence I wrote it myself. This extension will work for single or multiple files. It will appear only if all the selected items are files. It assumes Thunderbird is located at /usr/bin/thunderbird (View File) .
2. IMDB Details Property Page : I wrote this extension as an exercise to learn property pages. It uses the excellent IMDbPY library to fetch details from IMDB. It works only on video files and strips the common unused characters before querying IMDB. Since this was a test, I blindly select the first result and show the details. This extension also uses other gtk widgets to have a more complex UI. Assumes you have IMDbPy and pygtk installed (View file) .
I hope the article showed how to improve your productivity by customizing Nautilus to perfectly suit your work style. If you have any additional tips or ideas please post them in comments !