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?

2 Answers2

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.

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
  • 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
  • JacobVlijm was correct in what I am trying to do. @Serg Indeed this is longer. Not interested in right clicking at all. Would love to execute the script via keyboard, based on selected file in Nautilus, to Open the picture/file in GIMP. I did see a reference to Ubuntu Keyboard Shortcut: Open a Selected File in Sublime Text 2 However I am a novice in how to implement it for GIMP. Would appreciate any ideas on how to do so. – Seek Truth Feb 24 '17 at 21:26
  • @SeekTruth reference to what ? Also, there is currently no way to use shortcuts with nautilus and custom scripts – Sergiy Kolodyazhnyy Feb 24 '17 at 21:29
  • @serg had some "return" key edit issues when posting previous comment. I have fixed it to include the missing information. – Seek Truth Feb 24 '17 at 21:31
  • @SeekTruth hmmm, come back tomorrow. I have an idea, and will try to implement it – Sergiy Kolodyazhnyy Feb 24 '17 at 21:32
  • @SeekTruth Answer updated , I've implemented alternative solution, which does use shortcuts, although it has to be two of them actually. See the answer. Let me know if you have any questions – Sergiy Kolodyazhnyy Feb 24 '17 at 22:20
  • The thought is excellent, but I think you need to add an xdotool Ctrl+C command to add the current selection to clipboard, and even if I manually insert the right arguments after unquoted_args =, execlp('gimp',*unquoted_args) is not doing its job, while a simple subprocess.Popen() does. Currently it does not work on my system, with these changes it does. +1 for the ingenious concept though. – Jacob Vlijm Feb 24 '17 at 23:02
  • @JacobVlijm I could add xdotool but that will require user to install it, and I usually prefer minimum dependencies approach for the user. Also, I see no reason why execlp() wouldn't work on your system, since it comes from os module, which is standard. Can you explain what exactly doesn't work ? – Sergiy Kolodyazhnyy Feb 24 '17 at 23:11
  • It simply does nothing as it is. If I replace it with subprocess.Popen() it works instantly, that is if I press Ctrl-C before running the script. Try opening two different files, it simply won't work without first copying the file to clipboard. – Jacob Vlijm Feb 24 '17 at 23:15
  • @SeekTruth HI there ! I've updated my answer again. There's two solutions, each has its own advantages and disadvantages. Please read the instructions carefully on how to use either. Let me know how this works for you and whether you have any other questions – Sergiy Kolodyazhnyy Feb 25 '17 at 05:09
  • 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 00:44
  • @Serg gimp error screenshot. Had to be patient with the system for this to function. However GIMP gave me the above error messages when opening - screenshot. Home/username/Unknown Character "No Such File or Directory"- I'm Using Ubuntu 14.04 LTS – Seek Truth Feb 26 '17 at 01:15
  • @SeekTruth interesting. Do filenames have non-english characters ? I'm using 16.04, so the fact that you're using 14.04 might also play role in it. I'll look into the 14.04 possibility, I have virtual machine with that somewhere – Sergiy Kolodyazhnyy Feb 26 '17 at 01:17
  • @serg Very standard file names. all lowercase numbers and letters .jpg Just tried again and got "Opening '/home/username/@' failed: No such file or directory" – Seek Truth Feb 26 '17 at 01:19
  • @SeekTruth OK, in that case it's likely that it's version compatibility issue. I'll see what can be done about that. – Sergiy Kolodyazhnyy Feb 26 '17 at 01:21
  • @SeekTruth By the way, I've left a diagnostic print statement in the code before posting it. Try running it from command line like sleep 4 && python3 ~/bin/clipboard_launch.py and then switch immediately to nautilus window and press ctrl+c. It should show you some output, two lists in square brackets. You can post it here: paste.ubuntu.com and provide link – Sergiy Kolodyazhnyy Feb 26 '17 at 01:37
  • @SeekTruth I rewrote the first script, tested it in 14.04 virtual machine. The function that I was using previously indeed behaves differently in 14.04 , so I switched to using a different one, but I've also added other a few other improvements. Please try it out, let me know how it works for you – Sergiy Kolodyazhnyy Feb 26 '17 at 05:29
  • 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 02:49
  • @SeekTruth Perfect :) Glad I could help. Let me know if you have any further questions. – Sergiy Kolodyazhnyy Feb 27 '17 at 02:51
  • @SeekTruth As far as marking answers as accepted, the rules of the site such that only one answer can be marked as accepted. It is certainly up to you, as the owner of the question, to mark accepted answers. I'll just mention that I don't mind whether or not mine is marked as accepted, since I'm well satisfied with providing a working solution for the users. Alternatively, if you ever get above 50 points of reputation on the site, you can award Jacob's answer a bounty of 50 points or more. – Sergiy Kolodyazhnyy Feb 27 '17 at 02:59
  • In Ubuntu 19.04, where the behaviour of Ctrl+c has changed, this approach is broken. – vanadium Apr 25 '19 at 11:38
  • @vanadium Can you provide link that explains what's changed specifically ? – Sergiy Kolodyazhnyy Apr 25 '19 at 11:58
  • Here is a bug report related to the change, which was done to accommodate the Desktop Icons Gnome Shell extension: https://gitlab.gnome.org/GNOME/nautilus/issues/634 – vanadium Apr 25 '19 at 13:50
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.

Jacob Vlijm
  • 83,767
  • 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 02: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 06: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 07: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 02: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 03:03
  • @SeekTruth I just tried on 14.04, and indeed to my big surprise, pyperclip is only available in the way you mentioned, unlike 16.04. I will edit it in. – Jacob Vlijm Feb 27 '17 at 06:37
  • @JacobVlijm you might want to add xclip to list of packages in 14.04 apt command, since it is a dependency – Sergiy Kolodyazhnyy Feb 27 '17 at 09:11
  • From where is the system linking "gimp" to the actual gimp application? I want to adapt this to open with intellij-idea, how do I know how to name it correctly? is it the app name in apt, or the .Desktop entry? – Char Jan 15 '20 at 09:11