4

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?

5

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.

| improve this answer | |
  • But then, what is the advantage over "open with" -right click? This is even longer. – Jacob Vlijm Feb 23 '17 at 21:03
  • 1
    @JacobVlijm advantage is that OP doesn't have to change the default program for opening. Nautilus in particular doesn't support binding shortcuts - something OP tried to do. But if you want to offer some sort of black-magic alternative, feel free to post ;) – Sergiy Kolodyazhnyy Feb 23 '17 at 21:16
  • Unfortunately I don't, since indeed nautilus scripts do not work from a shortcut. I don't see why OP would have to change his shortcuts though, gimp should be in the options as it is (and I think OP works with that). I don't have an answer either though. – Jacob Vlijm Feb 23 '17 at 21:21
  • 1
    @Serg going to attempt implementation tomorrow with manual Ctrl + C. I can live with that. Thank you for this excellent explanation and also Jacob for his ideas and contributions. – Seek Truth Feb 26 '17 at 0:44
  • 1
    @Serg The new script worked without a hitch. I would like to mark both of them as correct answers. Many thanks to both of you! Jacob – Seek Truth Feb 27 '17 at 2:49
3

Opening selected file in a specific application from a shortcut

Inspired by the concept of @Serg (+1), a different approach on how to implement it:

The script

#!/usr/bin/env python3
import subprocess
import pyperclip

subprocess.call(["xdotool", "key", "Control_L+c"])
subprocess.Popen(["gimp", pyperclip.paste()])

How to use

  1. The script needs both pyperclip and xdotool (for xdotool- less usage, see notes):

    • for 16.04+:

      sudo apt-get install python3-pyperclip xdotool
      
    • for 14.04:

      sudo apt-get install python3-pip xdotool
      sudo pip3 install pyperclip
      
  2. Copy the script into an empty file, save it as open_ingimp.py

  3. Add it to a shortut key: Choose: System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts". Click the "+" and add the command:

    python3 /path/to/open_ingimp.py
    

That's it. Now select a valid document and press the shortcut key. The file will open in Gimp.

Explanation

  • When pressing Ctrl+C, while a file is selected, the path to the file is copied to the clipboard. We are simulating that with:

    subprocess.call(["xdotool", "key", "Control_L+c"])
    
  • It turns out python's pyperclip module simply produces the path, stripped from file:// when using pyperclip.paste() (this will not literally paste, but make the path available inside the script).
  • Then all we need to make the script do is run the command to open the file with gimp.

    subprocess.Popen(["gimp", pyperclip.paste()])
    

Notes

  • If pressing Ctrl+C before pressing the shortcut to open the file in Gimp is acceptable, we could even shorten the script to almost a one-liner:

    #!/usr/bin/env python3
    import subprocess
    import pyperclip
    
    subprocess.Popen(["gimp", pyperclip.paste()])
    

    This would make the installation of xdotool unnecessary.

  • We could dress up/refine the script a bit of course to only run on defined extensions, or make the application subject to an argument. Just mention.

  • We could even set multiple options: open file type A with application 1, file type B with application2, without messing with the default applications to open the file, or messing with the right- click options.

| improve this answer | |
  • this worked after installing the following on 14.04 LTS. 'sudo apt-get install xclip' 'sudo apt-get -y install python3-pip' 'sudo python3.4 -m pip install pyperclip' Going to wait on Serg's response. Using something that's available by default would be good. – Seek Truth Feb 26 '17 at 2:13
  • @SeekTruth The script does not use xclip at all, nor pyperclip (2), only python3-pyperclip, The instruction in the answer is sudo apt install python3-pyperclip xdotool, which suffices. Are you running it with python (2)? Instructions mention python3 /path/to/open_ingimp.py – Jacob Vlijm Feb 26 '17 at 6:56
  • @SeekTruth I changed apt into apt-get for backward compatibility with 14.04 though. For 14.04, sudo apt-get install python3-pyperclip xdotool really should suffice. – Jacob Vlijm Feb 26 '17 at 7:15
  • I attempted sudo apt-get install python3-pyperclip to no avail. I read elsewhere that pyperclip has xclip as a dependency. I also didn't install xdotool because I used the manual ctrl +c. – Seek Truth Feb 27 '17 at 2:31
  • Indeed that is so: Doing apt-cache show python3-pyperclip | grep 'Depends' returns the following output: Depends: xclip, python3-pyqt4. Effectively , using pyperclip is no different than installing xclip as package and calling it from scrip as external program. Advantage of xclip is that you can use it from shell scrips or simply command-line. It's a good program, I've used it a lot myself in the past. – Sergiy Kolodyazhnyy Feb 27 '17 at 3:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.