Ubuntu – How to set a shortcut key to open the currently selected file in nautilus

gimpnautilusnautilus-scriptscripts

Tired of right clicking Open With to open a file in a specific application, I would like to set a shortcut to open a file in e.g. Gimp. I don't want to change default open action though. (Ubuntu 14.04)

What I tried:

I have set a keyboard shortcut to run the script below to open the selected Nautilus file, in specified application (gimp).

!/bin/sh
for file in $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS 
do
/usr/bin/gimp "$file"
done

and…

!/bin/sh
/usr/bin/gimp "$1"

It never picks up the selected file in the nautilus window properly however.

Is there a way to do this?

Best Answer

NOTE: in this answer there are two solutions, each with its own benefits. Users are encouraged to find out which one works best for their specific case.

Introduction

Nautilus by itself doesn't offer a way to define custom keyboard shortcuts and their actions. This is the main reason why the scripts you tried have failed. $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS can only be used via right click submenu. But we can exploit something else to achieve the desired result - shortcuts defined in the settings and system clipboard.

Nautilus is built in something known as Gtk toolkit. It is an extensive library for creating graphic applications, and among other things it has utilities for interfacing with system clipboard. Nautilus, being a file manager, is special in the fact that it outputs a list of URIs ( in the form file:///home/user/Pictures/encoded%image%name.png ) to clipboard (which isn't a well known fact to most users, and I've learned quite recently as well). Apparently this is the way GUI file-managers copy files.

We can exploit that fact by copying the URI of the file (or to be exact, the list of URI's; even if there's just one, it defaults to a list). Then we can pass that list to gimp. The two solutions presented below operate exactly on that idea.

Reasoning for 2 solutions:

I personally consider solution #1 as preferred one. It relies on manually pressing copy shortcut first, and then script shortcut - that's two keyboard shortcuts - but it has advantage in having less dependencies. It's a more manual approach , but fairly decent. It uses os.execlp call, which will replace script's process with Gimp, thus acting as merely a springboard for Gimp. It's a frequent practice in scripting and system programming to use exec family of functions

The second solution was written because Jacob Vlijm mentioned in the comments that for him the execlp function didn't work, for whatever reason. I find this very strange because the execlp belong to standard os module for python , which is one of the modules installed default. In addition, subprocess.Popen() defaults to exec() family of functions; from subprocess documentation:

On POSIX, with shell=False (default): In this case, the Popen class uses os.execvp() to execute the child program. args should normally be a sequence. A string will be treated as a sequence with the string as the only item (the program to execute).

( Note "On POSIX" means "POSIX compliant OS"; Ubuntu is POSIX-compliant)

Thus, it doesn't seem like an issue with a function itself, but with user's system. Nevertheless, I wrote second script. That one uses subprocess module and relies on xdotool, which will basically automate pressing Ctrl+C shortcut for you, and then launch Gimp. I personally don't like this one as much, since it requires additional item to be installed, but it has advantage of needing just one keyboard shortcut.

The idea, however, is the same. We still use Gtk tools to query clipboard contents and in each case, scripts must be bound to a shortcut.

Solution #1, two shortcuts, minimum dependencies

Usage: select file and press Ctrl+C to copy file first, then press the shortcut you've assigned to this script. execlp function will replace the script's process with gimp.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from gi.repository import Gtk, Gdk
from os import execlp
from time import sleep
from urllib.parse import unquote
import signal
import threading
import subprocess

uris = None

def autoquit():
    global uris
    #sleep(0.5)
    while not uris:
       # allow sufficient time to obtain uris
       sleep(2)
       pass
    Gtk.main_quit()

def get_clipboard_contents():
    global uris
    clip = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
    tr = threading.Thread(target=autoquit)
    tr.daemon = True
    tr.start()
    uris = clip.wait_for_uris()
    Gtk.main()
    return [unquote(i).replace('file://','')
           for i in uris]
def main():
    signal.signal(signal.SIGINT,signal.SIG_DFL)
    files = get_clipboard_contents()
    print(files)
    args = ['gimp'] + files
    execlp('gimp',*args)

if __name__ == '__main__': main()

Solution #2: single shortcut, xdotool dependency

Usage for this script is simpler: select file(s) in Nautilus and press keyboard shortcut. NOTE: you must have xdotool installed for this to work, use sudo apt-get install xdotool.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from gi.repository import Gtk, Gdk
from subprocess import Popen,STDOUT
from time import sleep
from urllib.parse import unquote
import sys

def unquote_uri(*args):
    uris = args[-2]
    unquoted_args = [ str(unquote(i).replace('file://',''))
                      for i in uris]
    with open('/dev/null','w') as dev_null:
        proc = Popen(['gimp',*unquoted_args],stdout=dev_null,stderr=STDOUT)
    Gtk.main_quit()


def main():
    # NOTE: xdotool is REQUIRED for this to work
    Popen(['xdotool','key','Ctrl+c'])
    sleep(0.5)
    clip = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
    clip.request_uris(unquote_uri, None)
    Gtk.main()

if __name__ == '__main__': main()

Setting up the shortcut

In both cases, you need to have script linked to a shortcut. Open System Settings -> Keyboard -> Shortcuts -> Custom. Click the + button. Give full (!) path to file as command. For example, /home/User_Name/bin/clipboard_launch.py

enter image description here

Assign the shortcut to be something sensible. For example, since we're calling Gimp, I've assigned my script to Ctrl+Super+G.

Related Question