问题
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
- Contains:
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