Inkscape extension: python doesn't invoke .exe

坚强是说给别人听的谎言 提交于 2019-12-11 17:13:47

问题


I'm developing a plugin for Inkscape. Some versions:

  • Inkscape v0.92.3
  • Windows 10, version 1803 (build 17134.165)
  • Python 3.7 explicitly installed
  • MonoDevelop Version 7.7 Preview (7.7) Extra versions below

Installation Locations:

  • Inkscape: C:\Program Files\Inkscape
  • Extension: C:\Program Files\Inkscape\share\extensions
    • Contains: myplugin.inx, myplugin.py, MyPlugin.exe

I've made a plugin which, for development reasons, works as currently intended.
Most important of all, it runs when I run it either from MonoDevelop, or the built exe itself (both with the generated .dll's etc in the same location, or with only the exe copied to a different location).

I use (a slightly edited version of) SugarPillStudio's python script to run the .exe file. However, when I run that python script by invoking the extension, the .exe is not launched. Inkscape blinks a message that says 'MyPlugin is launching...' and closes that as fast as it opens.

I know that the python script works, because I have it print debugging lines to a .log file on my desktop. I know that the .exe doesn't launch because I have it also writing lines to the same .log file, first thing when the main() is invoked. When I (successfully) run the .exe it does print to the file, when I run the extension it doesn't.

This leads me to believe there's a problem with the python script in invoking the .exe. Any help?

Python Script:

#!/usr/bin/env python
'''
sugarpillstudios.com/wp/?p=142
'''
import os, sys, subprocess, datetime

f=open("C:\Users\Diamundo\Documents\plugin.log", "a+")
f.write("[PYT] %s Python script called at: %s.\n" % (datetime.datetime.now().isoformat(), os.getcwd() ) )

argv = []  
for arg in sys.argv[:]:  
  if arg.startswith("--executable="):  
    executable = arg.split("=")[1]  
  else:  
    argv.append(arg)
argv[0] = executable  
f.write("[PYT] %s %s\n" % ( datetime.datetime.now().isoformat(), executable ) )
process = subprocess.Popen(argv,shell=False,stdout=subprocess.PIPE)
print process.communicate()[0]

Plugin.inx:

<inkscape-extension>
    <name>MyPlugin</name>
    <id>name.space.plugin.main</id>
    <param name="executable" type="string" gui-hidden="true">MyPlugin.exe</param>
    <effect>
        <object-type>all</object-type>
        <effects-menu>
            <submenu _name="MyPlugin"/>
        </effects-menu>
    </effect>
    <script>
        <command reldir="extensions" interpreter="python">myplugin.py</command>
    </script>
</inkscape-extension>

Extra Monodevelop versions:

Runtime:
    Microsoft .NET 4.0.30319.42000
    GTK+ 2.24.26 (Light theme)
    GTK# 2.12.45

NuGet 
Version: 4.3.1.4445

.NET Core
Runtime: C:\Program Files\dotnet\dotnet.exe
Runtime Versions:
    2.0.9
    2.0.5
SDK: C:\Program Files\dotnet\sdk\2.1.202\Sdks
SDK Versions:
    2.1.202
    2.1.4
MSBuild SDKs: Not installed

回答1:


Inkscape uses Python 2.7, which it brings with it, unless you set that differently in the settings file (edit manually).

If you want to write an Inkscape extension, you can learn how to do this by:

  • reading https://inkscape.org/develop/extensions/
  • following the examples in other extensions that work (e.g. for running additional Inkscape instances, you could follow this one: https://gitlab.com/su-v/inx-pathops/blob/master/src/pathops.py)



回答2:


Loosely based on the pathops.py file, linked by Moini in her answer, I've come up with the following file.

About

It uses the inkex.py (source on GitLab) library to declare an Inkscape Effect. The Effect class uses the OptionParser library to parse the default given parameters (e.g. --id=$$ for selected nodes where $$ is the XML node's 'id' tag's value). By adding the custom executable option, we can also parse this.

Parsing arguments

After the OptionParser is done parsing, the values will be visible in self.options, i.e. our executable now lives in self.options.executable (because of the action="store" and dest="executable" parameters).
Furthermore, the temporary SVG-file as created by Inkscape, can be found in self.svg_file.

Saving edits

As previously said, Inkscape makes a temporary file with the contents of the SVG in its then current state. Any edits you(r plugin) make(s) should not be saved back to this file, but returned to Inkscape itself - this is the premise of the Effect class: it edits an SVG and returns the edit to Inkscape. Further reading here.

Instead, in your plugin you should (readonly) open the file, read its contents, and then edit it. When you're done editing, write the entire SVG to your commandline.
Then, the line out, err = process.communicate(None) will grab your plugin's output and error-output. These are used to return information to Inkscape.

Notes

The structure of the cmd array is of no importance, except the fact that the executable should come as the very first element. All other array-elements can be anything in any order, I just added '--id=$$' to every ID because that's the way Inkscape uses, and this way it looks the same as if there's no Python middleware present. The same goes for the self.svg_file which I placed last, Inkscape does the same in its arguments - you could also make '--file='+self.svg_file from it for clarity.

Source

#!/usr/bin/env python

import os
from subprocess import Popen, PIPE
import time

try:
    import inkex_local as inkex
except ImportError:
    import inkex
#import simplestyle

class MyPlugin(inkex.Effect):
    def __init__(self):
        inkex.Effect.__init__(self)
        self.OptionParser.add_option("--executable", action="store", type="string", dest="executable", default="MyPlugin.exe")

    def effect(self):
        out = err = None

        cmd = []
        cmd.append(self.options.executable)
        for id in self.options.ids:
            cmd.append("--id=" + id)
        cmd.append(self.svg_file)
        #inkex.debug(cmd);

        process = Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE)
        out, err = process.communicate(None)

        if process.returncode == 0:
            print out
        elif err is not None:
            inkex.errormsg(err)

if __name__ == '__main__':
    myplugin = MyPlugin()
    myplugin.affect()


来源:https://stackoverflow.com/questions/51650591/inkscape-extension-python-doesnt-invoke-exe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!