Author Topic: [Solved] Want to Create a Simple Extension to Call Menu Functions  (Read 519 times)

May 01, 2019, 03:57:59 PM
Read 519 times

AntumDeluge

  • Sr. Newbie

  • Offline
  • **

  • 4
  • Gender
    Male

    Male
Note: For solution, see end of message under highlighted "Solved".

Hello, this is my first post in the Inkscape Community Forums.

I have been looking over the PythonEffectTutorial & the Python modules for extensions pages on the wiki. What I want to do is create a Python extension that simply calls the menu functions Path -> Combine | Path -> Union | Path -> Simplify | Path -> Break Apart in that order on the selected objects in the document.

I am familiar with scripting in Python, but I'm pretty lost in creating this extension as this is all I have so far:
Code: [Select]
#!/usr/bin/env python

# This script is licensed under CC0

import inkex

class SimplePathOptimize(inkex.Effect):
def run(self):
svg = self.document.getroot()

SimplePathOptimize().run()

I'm not sure if I'm even on the right trail. If anyone could show me how to access & execute the menu functions from within a Python extension, I would really appreciate it.

I am using Inkscape version 0.92.2 on Windows 10 64-bit.

Edit: Forgot to mention that I have been looking in the pre-installed extensions to try to get an idea as well.

Edit: I've gotten a little closer:

Code: [Select]
class SimplePathOptimize(inkex.Effect):
def __init__(self):
inkex.Effect.__init__(self)

def effect(self):
selected = self.selected
if not selected:
inkex.errormsg('Please select an object')
return

# TODO: call functions on selected objects


if __name__ == '__main__':
SimplePathOptimize().affect()

Edit: I've been told to use verbs for what I want to do. So my script now creates a temporary copy of the currently opened SVG document. But I'm not sure how pass the selected objects to Inkscape to select them in the temporary document:

Code: [Select]
#!/usr/bin/env python

# This work is released under Creative Commons Zero (CC0).
#
# The author hereby waives all copyright and related or
# neighboring rights together with all associated claims
# and causes of action with respect to this work to the
# extent possible under the law.
#
# See: https://creativecommons.org/publicdomain/zero/1.0/legalcode

import inkex, os, subprocess, sys, tempfile, traceback

# in case run outside of Inkscape's extensions directory on Linux/Unix-like systems
sys.path.append('/usr/share/inkscape/extensions')


# helper function for calling inkscape commands
def execute(params=None):
cmd = ['inkscape']
if sys.platform == 'win32':
# support stdout on Windows
cmd[0] = 'inkscape.com'

if not params:
params = cmd
else:
if type(params) == str:
params = params.split(' ')
elif type(params) == tuple:
params = list(params)

params = cmd + params

proc = subprocess.Popen(params, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate()

if err:
# clean up error message
err = err.strip(' \t\n\r')
inkex.errormsg('Error: {}'.format(err))
return

# clean up output
return out.strip(' \t\n\r')


# main class
class SimplePathOptimize(inkex.Effect):
def __init__(self):
inkex.Effect.__init__(self)

def effect(self):
selected = self.selected
if not selected:
inkex.errormsg('Please select object(s)')
return

# create a temp SVG document
temp_file = tempfile.mktemp('temp.svg')
self.document.write(temp_file)

verb_list = (
'SelectionCombine',
'SelectionUnion',
'SelectionSimplify',
'SelectionBreakApart',
'FileSave',
'FileQuit',
)
cmd_list = []
for V in verb_list:
cmd_list.append('--verb={}'.format(V))
cmd_list += ['-f', temp_file]

execute(cmd_list)

# delete temporary file
try:
os.remove(temp_file)
except:
msg = 'Error removing temporary file: {}\n\n'.format(temp_file)
msg += traceback.format_exc()

# clean up message
msg = msg.strip(' \t\n\r')

inkex.errormsg(msg)


if __name__ == '__main__':
SimplePathOptimize().affect()

Solved: Okay, figured out that I needed to use the "--select" parameter. Then I read the information from the saved temp file & update the open document. Here is the full extension script:

simple_path_optimize.py:
Code: [Select]
#!/usr/bin/env python

# This work is released under Creative Commons Zero (CC0).
#
# The author hereby waives all copyright and related or
# neighboring rights together with all associated claims
# and causes of action with respect to this work to the
# extent possible under the law.
#
# See: https://creativecommons.org/publicdomain/zero/1.0/legalcode

import inkex, os, subprocess, sys, tempfile, traceback

# in case run outside of Inkscape's extensions directory on Linux/Unix-like systems
sys.path.append('/usr/share/inkscape/extensions')


# helper function for calling inkscape commands
def execute(params=None):
# verbs require GUI so cannot use '-z' paramater?
#cmd = ['inkscape', '-z',]
cmd = ['inkscape',]
if sys.platform == 'win32':
# support stdout on Windows
cmd[0] = 'inkscape.com'

if not params:
params = cmd
else:
if type(params) == str:
params = params.split(' ')
elif type(params) == tuple:
params = list(params)

params = cmd + params

proc = subprocess.Popen(params, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate()

if err:
# clean up error message
err = err.strip(' \t\n\r')
inkex.errormsg('Error: {}'.format(err))
return

# clean up output
return out.strip(' \t\n\r')


# main class
class SimplePathOptimize(inkex.Effect):
def __init__(self):
inkex.Effect.__init__(self)

def effect(self):
selected = self.selected
if not selected:
inkex.errormsg('Please select object(s) for path optimization')
return

# create a temp SVG document
temp_file = tempfile.mktemp('temp.svg')
self.document.write(temp_file)

# get all selected paths for selection in temp document
cmd_list = []
for S in selected:
cmd_list.append('--select={}'.format(S))

verb_list = (
'SelectionCombine',
'SelectionUnion',
'SelectionSimplify',
'SelectionBreakApart',
'FileSave',
'FileQuit',
)

for V in verb_list:
cmd_list.append('--verb={}'.format(V))

cmd_list += ['-f', temp_file]
execute(cmd_list)

# get results
BUFFER = open(temp_file, 'r')
new_document = inkex.etree.parse(BUFFER)
BUFFER.close()

# delete temporary file
try:
os.remove(temp_file)
except:
msg = 'Error removing temporary file: {}\n\n'.format(temp_file)
msg += traceback.format_exc()

# clean up message
msg = msg.strip(' \t\n\r')

inkex.errormsg(msg)

# update open document
self.document = new_document


if __name__ == '__main__':
SimplePathOptimize().affect()

simple_path_optimize.inx:
Code: [Select]
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
    <_name>Simple Path Optimize</_name>
    <id>org.inkscape.modify_path.simple_path_optimize</id>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu _name="Modify Path"/>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">simple_path_optimize.py</command>
</script>
</inkscape-extension>

Uploaded the extension for anyone that might like to use it: Simple Path Optimize
« Last Edit: May 02, 2019, 07:41:50 PM by AntumDeluge »
  • 0.92.2
  • Windows 10 64-bit

May 02, 2019, 08:34:27 PM
Reply #1

brynn

  • Administrator

  • Offline
  • ******

  • 3,941
  • Gender
    Female

    Female
    • Inkscape Community
Welcome to the forum!

You're welcome to share the extension on the Inkscape website:  https://inkscape.org/gallery/=extension/

I have some questions about it.  When you say pixel art converted to SVG, do you mean using Path menu > Trace Pixel Art?  Or converted some other way?

I've actually never used that feature before - mostly because I don't have any pixel art to test with.  I wonder if it just creates a large series of colored squares?

Is that the kind of file that your extension is meant to be used on?

Ok now you've made me curious enough to search for a pixel art image to play with!

Edit
Hhm, interesting.  It does not make a series of squares.  I wonder why whoever created it used B-Spline curves for the output, instead of just regular bezier curves?

I do see why you need the Simplify.  But the Combine step (and Union) must make everything the same color.  I guess color isn't needed for your purpose?
  • Inkscape version 0.92.3
  • Windows 7 Pro, 64-bit
Inkscape Tutorials (and manuals)                      Inkscape Community Gallery                        Inkscape for Cutting Design                     



"Be ashamed to die until you have won some victory for humanity" - Horace Mann                       

May 03, 2019, 03:21:05 PM
Reply #2

AntumDeluge

  • Sr. Newbie

  • Offline
  • **

  • 4
  • Gender
    Male

    Male
So, first of all, as far as the extension that I created goes, it's actually not as efficient as just pressing the key combinations: Ctrl+K, Ctrl++, Ctrl+L, Ctrl+Shift+K. A decent macro program for setting virtual keystrokes would probably be a better alternative.

I have used Inkscape's "Trace Bitmap" & "Trace Pixel Art" tools. But in the case for which I wanted this extension, I had used a version of pixel2svg, which creates an SVG image from a raster image & does just create a large series of squares. By selecting all squares of the same fill color, the extension "simplifies" paths by essentially joining any neighboring squares of the same color into one object. For this it needs the "Combine" step. And in order for "Simplify" to work correctly, "Union" must be called on the combined objects. It shouldn't be used on objects of different colors as it will, as you said, merge them all into one color.

There are some caveats to this automation though, any objects completely surrounded by squares of a single color will potentially be covered up after the "Break Apart" function. I may remove this function as I it seems that if I keep all objects of the same color as one, it makes the file slightly smaller. Not 100% sure about it though.
  • 0.92.2
  • Windows 10 64-bit

May 04, 2019, 07:16:21 AM
Reply #3

brynn

  • Administrator

  • Offline
  • ******

  • 3,941
  • Gender
    Female

    Female
    • Inkscape Community
Oh I see.  I thought you were applying it to the whole traced image, all at once.  But you're selecting certain parts of it at a time.

So it could still be used on a result of Trace Pixel Art, or even Trace Bitmap, after ungrouping the result, and selecting unique color objects.

Thanks for explaining :)
  • Inkscape version 0.92.3
  • Windows 7 Pro, 64-bit
Inkscape Tutorials (and manuals)                      Inkscape Community Gallery                        Inkscape for Cutting Design                     



"Be ashamed to die until you have won some victory for humanity" - Horace Mann                       

May 04, 2019, 04:44:33 PM
Reply #4

AntumDeluge

  • Sr. Newbie

  • Offline
  • **

  • 4
  • Gender
    Male

    Male
It would be much more efficient if it could just call the functions on the open document. But, I've been told that I have to use "verbs", which are called from the command line. So, the extension has to open a separate Inkscape instance to make the changes on a new document, save it to the filesystem, then the saved document information is imported into the current document. So, kind of pointless since it is faster to just use a combination of hotkeys.
  • 0.92.2
  • Windows 10 64-bit

May 04, 2019, 06:42:00 PM
Reply #5

brynn

  • Administrator

  • Offline
  • ******

  • 3,941
  • Gender
    Female

    Female
    • Inkscape Community
Wow, really?  Do all extensions work like that?  Or just the way it works with verbs?  Interesting.

Did I hear that someone is working something like a macro system for Inkscape?  Or did I just hear a wish for that?
  • Inkscape version 0.92.3
  • Windows 7 Pro, 64-bit
Inkscape Tutorials (and manuals)                      Inkscape Community Gallery                        Inkscape for Cutting Design                     



"Be ashamed to die until you have won some victory for humanity" - Horace Mann                       

May 04, 2019, 11:36:07 PM
Reply #6

AntumDeluge

  • Sr. Newbie

  • Offline
  • **

  • 4
  • Gender
    Male

    Male
I think that is just how it works with verbs. I hope someone is working on a macro system or a way to call more functions from extensions.
  • 0.92.2
  • Windows 10 64-bit