When I'm using Inkscape, there are some tasks which I frequently do. These are the tasks that I frequently do:
Set the opacity of the selected path to 10%
Export selection to PNG in 600 DPI and insert it to my clipboard
I wish I could map a key to do those repetitive tasks. That is, I want to do (1) or (2) by pressing a single key in my keyboard.
I have been searching for a while and I found out that some people have created Inkscape extensions using Python. I have never created an Inkscape extension, but I have used Python in the past and it seems that Inkscape can be widely controlled using Python so I'm planning to learn how to write my custom extensions, so that I could find a way to map a key to a Python function that do (1) and (2).
I'm creating this post so that others can share their experiences mapping a key to a custom function which you created in Python. Any information on this topic is appreciated.
I managed to create an extension to create an extension for setting the opacity and I was able to map it to a key by going to "Preferences" > "Keyboard". Below is the code of the extension.
Now, I need to learn how to export the selection to a PNG file and insert it to my clipboard.
import inkex
from inkex import TextElement
class Hello(inkex.EffectExtension):
def effect(self):
for elem in self.svg.selection:
elem.style['opacity'] = 0.1
if __name__ == '__main__':
Hello().run()
I managed to create an extension that exports the selection to a PNG file using the code below. I would appreciate some feedback from more experienced users on the code that I wrote. Any suggestion/improvement to my code is appreciated.
import inkex
import subprocess
import copy
class MyExportSelectionToPng(inkex.EffectExtension):
def effect(self):
# Create copy of the SVG file
document = copy.deepcopy(self.document)
# Get the ID of elements in the selection
selected_elements_ids = []
for elem in self.svg.selection:
selected_elements_ids.append(elem.eid)
# Remove elements that are not in the selection
g = document.xpath('/svg:svg/svg:g', namespaces=inkex.NSS)[0]
for element in g.getchildren():
if element.eid not in selected_elements_ids:
g.remove(element)
# Write document to a temporary file
document.write('/tmp/a.svg')
# Export PNG file to SVG file
command = [
'inkscape',
'/tmp/a.svg',
'--export-area-drawing',
'--export-dpi=600',
'--export-type', 'png',
'--export-filename', '/tmp/a.png',
]
try:
subprocess.check_call(command)
except Exception as e:
raise Exception(
f'Failed to convert.')
# TODO: Insert PNG file to clipboard
#
# Note: Calling xclip through subprocess.run makes Inkscape
# get stuck. For the time being, I'll write a separate shell
# scripts that inserts the image into the clipboard.
#
# subprocess.run(['xclip', '-selection', 'clipboard', '-t', 'image/png', '/tmp/a.png'])
if __name__ == '__main__':script
MyExportSelectionToPng().run()
Sidenote: In Preferences, I then mapped "x" to call "Set opacity to 10%" and "c" to call "Export selection to PNG" so that I can quickly call those functions with my left hand. While I'm editing in Inkscape, I mainly use my right hand to do actions with the mouse and my left hand remains in the keyboard.
Heres an example that works for me on Ubuntu 22 and Inkscape 1.4.
Pasting into Inkscape and Gimp works. I haven't tested it with Windows.
It creates a dummy Gtk3 window object, with a Gtk3 pixbuf image element in it.
It then uses the Gtk3 clipboard facility to get the pixbuf into the clipboard.
Works for me but is a bit hacky,it uses Threads to exit Gtk3 after a given number of seconds.
If you are copying a huge object this could fail if not enough time for the copy operation (say on a slow machine).
For my own purposes I used a pruning function to isolate a single object.
However using python to isolate the object / selection you need is a bit pointless.
select-invert has returned to the Inkscape 1.4 command line.
It would be more efficient to pass your selection to the command line using select-by-id:id1,id2,id3...... etc
Then use fit-canvas-to-selection, then select-invert, then object-set-property:display,none
This leaves just your selection visible, and the canvas the correct size for the selection.
Just pass deepcopy(self.svg) to the command line instead of pruned_svg. ------------------------------------------------------------------------------------------
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GdkPixbuf
import time
from time import sleep
import tempfile
import os, shutil
from copy import deepcopy
import inkex
from inkex import command
def make_temp_folder(self):
self.temp_folder = tempfile.mkdtemp()
return self.temp_folder
def save_temp_svg(self, svg, extension='.svg'):
temp_file_name = str(time.time()).replace('.', '') + extension
if hasattr(self, 'temp_folder'):
temp_folder = self.temp_folder
else:
temp_folder = make_temp_folder(self)
svg_temp_file_path = os.path.join(temp_folder, temp_file_name)
with open(svg_temp_file_path, 'w') as svg_temp_file:
my_svg_string = svg.tostring().decode("utf-8")
svg_temp_file.write(my_svg_string)
svg_temp_file.close()
return svg_temp_file_path
def inkscape_command_call(self, input_file, options_list, action_list):
command.inkscape(input_file, options_list, f'--actions={action_list}')
def export_element_to_png(self, element):
pruned_svg = prune_object_tree(self, deepcopy(self.svg), element)
svg_temp_file_path = save_temp_svg(self, pruned_svg)
png_temp_file_path = svg_temp_file_path.split('.svg')[0] + '.png'
action_list = f'export-type:png;export-dpi:600;export-filename:{png_temp_file_path};export-do;'
inkscape_command_call(self, svg_temp_file_path, 'none', action_list)
return png_temp_file_path
def prune_object_tree(self, svg, element):
# A list of tag we wish to process ( to enable defs / namedview etc to be ignored )
element_tags = ['circle', 'ellipse', 'image', 'line', 'path', 'polygon', 'polyline', 'rect', 'text', 'textPath', 'title', 'tspan', 'use', 'g']
ancestors = svg.getElementById(element.get_id()).ancestors()
# Add element to ancestors elementlist to avoid repeating sibling code.
ancestors.add(element)
# Remove all siblings on each level of tree
for ancestor in ancestors:
if ancestor.getparent() != None:
following_siblings = ancestor.xpath('./following-sibling::*')
preceding_siblings = ancestor.xpath('./preceding-sibling::*')
siblings = following_siblings + preceding_siblings
for item in siblings:
if item.TAG in element_tags:
inkex.errormsg(item.TAG)
svg.getElementById(item.get_id()).delete()
return svg
class MyWindow(Gtk.Window):
def __init__(self):
super().__init__(title="Dummy Gtk for clipboard")
gtk_image = Gtk.Image()
pixbuf = GdkPixbuf.Pixbuf.new_from_file(InkRasterClipboard.png_temp_file_path)
gtk_image.set_from_pixbuf(pixbuf)
gtk_image.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
gtk_image.clipboard.set_image(gtk_image.get_pixbuf())
def run_gtk():
win = MyWindow()
win.connect("destroy", Gtk.main_quit)
Gtk.main()
# Sleep time can be adjusted to correct for slow machines
def exit_gtk():
sleep(2)
Gtk.main_quit()
class InkRasterClipboard(inkex.EffectExtension):
def add_arguments(self, pars):
pass
def effect(self):
selection_list = self.svg.selected
if len(selection_list) < 1:
inkex.errormsg('Please one object')
return
if len(selection_list) > 1:
inkex.errormsg('Please select one object')
# Create a png of the current selected object
InkRasterClipboard.png_temp_file_path = export_element_to_png(self, selection_list[0])
from threading import Thread
Thread(target=exit_gtk).start()
run_gtk()
# Remove temp folder
if hasattr(self, 'temp_folder'):
shutil.rmtree(self.temp_folder)
if __name__ == '__main__':
InkRasterClipboard().run()
When I'm using Inkscape, there are some tasks which I frequently do. These are the tasks that I frequently do:
I wish I could map a key to do those repetitive tasks. That is, I want to do (1) or (2) by pressing a single key in my keyboard.
I have been searching for a while and I found out that some people have created Inkscape extensions using Python. I have never created an Inkscape extension, but I have used Python in the past and it seems that Inkscape can be widely controlled using Python so I'm planning to learn how to write my custom extensions, so that I could find a way to map a key to a Python function that do (1) and (2).
So far, I have been able to execute this extension https://inkscapetutorial.org/hello-extension.html in my computer.
I'm creating this post so that others can share their experiences mapping a key to a custom function which you created in Python. Any information on this topic is appreciated.
I managed to create an extension to create an extension for setting the opacity and I was able to map it to a key by going to "Preferences" > "Keyboard". Below is the code of the extension.
Now, I need to learn how to export the selection to a PNG file and insert it to my clipboard.
~/.config/inkscape/extensions/my_set_opacity_to_10_percent/my_set_opacity_to_10_percent.inx
~/.config/inkscape/extensions/my_set_opacity_to_10_percent/my_set_opacity_to_10_percent.py
It's not possible to replicate the gui export to .png function directly with pure python (inkex)
It requires a command call to the main Inkscape program to export to a file in the system tempfolder
You can then open the .png file with PIL (pillow) which is bundled with Inkscape on Windows.
(Please note there was a slight issue with one of the 1.4.x releases and PIL which has been reported)
Then you would need to go from the PIL Image to the Windows clipboard.
I requires a pypi library to be installed, there are a couple of examples if you search
"python 3 PIL image to clipboard"
I think some also work with Linux.
I managed to create an extension that exports the selection to a PNG file using the code below. I would appreciate some feedback from more experienced users on the code that I wrote. Any suggestion/improvement to my code is appreciated.
~/.config/inkscape/extensions/my_export_selection_to_png/my_export_selection_to_png.inx
~/.config/inkscape/extensions/my_export_selection_to_png/my_export_selection_to_png.py
Sidenote: In Preferences, I then mapped "x" to call "Set opacity to 10%" and "c" to call "Export selection to PNG" so that I can quickly call those functions with my left hand. While I'm editing in Inkscape, I mainly use my right hand to do actions with the mouse and my left hand remains in the keyboard.
Heres an example that works for me on Ubuntu 22 and Inkscape 1.4.
Pasting into Inkscape and Gimp works. I haven't tested it with Windows.
It creates a dummy Gtk3 window object, with a Gtk3 pixbuf image element in it.
It then uses the Gtk3 clipboard facility to get the pixbuf into the clipboard.
Works for me but is a bit hacky,it uses Threads to exit Gtk3 after a given number of seconds.
If you are copying a huge object this could fail if not enough time for the copy operation (say on a slow machine).
For my own purposes I used a pruning function to isolate a single object.
However using python to isolate the object / selection you need is a bit pointless.
select-invert
has returned to the Inkscape 1.4 command line.It would be more efficient to pass your selection to the command line using
select-by-id:id1,id2,id3......
etcThen use
fit-canvas-to-selection
, thenselect-invert
, thenobject-set-property:display,none
This leaves just your selection visible, and the canvas the correct size for the selection.
Just pass
deepcopy(self.svg)
to the command line instead ofpruned_svg
.------------------------------------------------------------------------------------------