Renamed "Migrate Groups" to "Ungrouper And Element Migrator/Filter"

This commit is contained in:
Mario Voigt 2020-08-23 14:08:11 +02:00
parent b9fb5042e4
commit 433cbbacfd
2 changed files with 162 additions and 89 deletions

View File

@ -1,24 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> <inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Migrate Groups</name> <name>Ungrouper And Element Migrator/Filter</name>
<id>fablabchemnitz.de.migrategroups</id> <id>fablabchemnitz.de.migrategroups</id>
<label>What elements to migrate?</label>
<label>Unchecked item types will be deleted!</label>
<!-- other stuff: sodipodi, lineargradient, radialgradient, meshgradient, meshrow, meshpath, stop --> <label appearance="header">Ungrouper / Element Migrator</label>
<param name="circle" type="bool" gui-text="circle">true</param> <label>Running this extension works for current selection or if nothing is selected for whole document. It allows to flatten elements into one group and to drop elements while processing. Some element IDs might change while doing.</label>
<param name="clipPath" type="bool" gui-text="clipPath">true</param> <label>Enable the checkbox "Ignore custom element types" to ignore custom selections, or disable it and select items you want to drop while ungrouping. Unchecked elements will be dropped.</label>
<param name="defs" type="bool" gui-text="defs">true</param> <separator/>
<param name="ellipse" type="bool" gui-text="ellipse">true</param> <label appearance="header">Custom Element Types</label>
<param name="image" type="bool" gui-text="image">true</param> <hbox>
<param name="line" type="bool" gui-text="line">true</param> <vbox>
<param name="path" type="bool" gui-text="path">true</param> <label appearance="header">Geometry</label>
<param name="polyline" type="bool" gui-text="polyline">true</param> <param name="circle" type="bool" gui-text="circle">true</param>
<param name="polygon" type="bool" gui-text="polygon">true</param> <param name="ellipse" type="bool" gui-text="ellipse">true</param>
<param name="rect" type="bool" gui-text="rect">true</param> <param name="line" type="bool" gui-text="line">true</param>
<param name="svg" type="bool" gui-text="svg">true</param> <param name="path" type="bool" gui-text="path">true</param>
<param name="text" type="bool" gui-text="text">true</param> <param name="polyline" type="bool" gui-text="polyline">true</param>
<param name="tspan" type="bool" gui-text="tspan">true</param> <param name="polygon" type="bool" gui-text="polygon">true</param>
<param name="rect" type="bool" gui-text="rect">true</param>
</vbox>
<separator/>
<vbox>
<label appearance="header">Objects / Misc</label>
<param name="clipPath" type="bool" gui-text="clipPath">true</param>
<param name="defs" type="bool" gui-text="defs">true</param>
<param name="image" type="bool" gui-text="image">true</param>
<!--<param name="sodipodi" type="bool" gui-text="sodipodi">true</param> -->
<param name="svg" type="bool" gui-text="svg">true</param>
<param name="text" type="bool" gui-text="text">true</param>
<param name="tspan" type="bool" gui-text="tspan">true</param>
</vbox>
<separator/>
<vbox>
<label appearance="header">Gradients</label>
<param name="lineargradient" type="bool" gui-text="lineargradient">true</param>
<param name="radialgradient" type="bool" gui-text="radialgradient">true</param>
<param name="meshgradient" type="bool" gui-text="meshgradient">true</param>
<param name="meshrow" type="bool" gui-text="meshrow">true</param>
<param name="meshpatch" type="bool" gui-text="meshpatch">true</param>
<param name="script" type="bool" gui-text="script">true</param>
<param name="stop" type="bool" gui-text="stop">true</param>
<param name="use" type="bool" gui-text="use">true</param>
</vbox>
</hbox>
<separator/>
<param name="allitems" type="bool" gui-text="Ignore custom element types">true</param>
<param name="droponly" type="bool" gui-text="Do not ungroup (drop only)">false</param>
<param name="showdroplist" type="bool" gui-text="Show list of dropped items">false</param>
<separator/>
<label>Written by Mario Voigt / Stadtfabrikanten e.V. (2020)</label>
<label appearance="url">https://fablabchemnitz.de</label>
<label>License: GNU GPL v3</label>
<effect needs-live-preview="true"> <effect needs-live-preview="true">
<object-type>path</object-type> <object-type>path</object-type>
<effects-menu> <effects-menu>

View File

@ -3,7 +3,7 @@
""" """
Extension for InkScape 1.0 Extension for InkScape 1.0
This extension parses the selection and will put all paths into one single group. If you have a cluster with lots of groups and paths you will clean up this way (one top level group, all paths below it). If you select a single path or a set of paths you just wrap it like using CTRL + G (like making a usual group) This extension parses the selection and will put all elements into one single group. If you have a cluster with lots of groups and elements you will clean up this way (one top level group, all elements below it). If you select a single element or a set of elements you just wrap it like using CTRL + G (like making a usual group). You can also use this extension to filter out unwanted SVG elements at all.
Author: Mario Voigt / FabLab Chemnitz Author: Mario Voigt / FabLab Chemnitz
Mail: mario.voigt@stadtfabrikanten.org Mail: mario.voigt@stadtfabrikanten.org
@ -13,94 +13,135 @@ License: GNU GPL v3
""" """
import inkex import inkex
from lxml import etree
class MigrateGroups(inkex.Effect): class MigrateGroups(inkex.Effect):
allPaths = [] allElements = []
allGroups = [] allGroups = []
allNonMigrates = [] allNonMigrates = []
def __init__(self): def __init__(self):
inkex.Effect.__init__(self) inkex.Effect.__init__(self)
self.arg_parser.add_argument("--circle", type=inkex.Boolean, default=True, help="circle") self.arg_parser.add_argument("--allitems", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--clipPath", type=inkex.Boolean, default=True, help="clipPath") self.arg_parser.add_argument("--droponly", type=inkex.Boolean, default=False)
self.arg_parser.add_argument("--defs", type=inkex.Boolean, default=True, help="defs") self.arg_parser.add_argument("--showdroplist", type=inkex.Boolean, default=False)
self.arg_parser.add_argument("--ellipse", type=inkex.Boolean, default=True, help="ellipse")
self.arg_parser.add_argument("--image", type=inkex.Boolean, default=True, help="image") self.arg_parser.add_argument("--circle", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--line", type=inkex.Boolean, default=True, help="line") self.arg_parser.add_argument("--clipPath", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--path", type=inkex.Boolean, default=True, help="path") self.arg_parser.add_argument("--defs", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--polyline", type=inkex.Boolean, default=True, help="polyline") self.arg_parser.add_argument("--ellipse", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--polygon", type=inkex.Boolean, default=True, help="polygon") self.arg_parser.add_argument("--image", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--rect", type=inkex.Boolean, default=True, help="rect") self.arg_parser.add_argument("--line", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--svg", type=inkex.Boolean, default=True, help="svg") self.arg_parser.add_argument("--path", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--text", type=inkex.Boolean, default=True, help="text") self.arg_parser.add_argument("--polyline", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--tspan", type=inkex.Boolean, default=True, help="tspan") self.arg_parser.add_argument("--polygon", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--rect", type=inkex.Boolean, default=True)
#self.arg_parser.add_argument("--sodipodi", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--svg", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--text", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--tspan", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--lineargradient", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--radialgradient", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--meshgradient", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--meshrow", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--meshpatch", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--script", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--stop", type=inkex.Boolean, default=True)
self.arg_parser.add_argument("--use", type=inkex.Boolean, default=True)
def effect(self): def effect(self):
namespace = [] namespace = []
namespace.append("{http://www.w3.org/2000/svg}circle") if self.options.circle else "" namespace.append("{http://www.w3.org/2000/svg}circle") if self.options.circle else ""
namespace.append("{http://www.w3.org/2000/svg}clipPath") if self.options.clipPath else "" namespace.append("{http://www.w3.org/2000/svg}clipPath") if self.options.clipPath else ""
namespace.append("{http://www.w3.org/2000/svg}defs") if self.options.defs else "" namespace.append("{http://www.w3.org/2000/svg}defs") if self.options.defs else ""
namespace.append("{http://www.w3.org/2000/svg}ellipse") if self.options.ellipse else "" namespace.append("{http://www.w3.org/2000/svg}ellipse") if self.options.ellipse else ""
namespace.append("{http://www.w3.org/2000/svg}image") if self.options.image else "" namespace.append("{http://www.w3.org/2000/svg}image") if self.options.image else ""
namespace.append("{http://www.w3.org/2000/svg}line") if self.options.line else "" namespace.append("{http://www.w3.org/2000/svg}line") if self.options.line else ""
namespace.append("{http://www.w3.org/2000/svg}polygon") if self.options.polygon else "" namespace.append("{http://www.w3.org/2000/svg}polygon") if self.options.polygon else ""
namespace.append("{http://www.w3.org/2000/svg}path") if self.options.path else "" namespace.append("{http://www.w3.org/2000/svg}path") if self.options.path else ""
namespace.append("{http://www.w3.org/2000/svg}polyline") if self.options.polyline else "" namespace.append("{http://www.w3.org/2000/svg}polyline") if self.options.polyline else ""
namespace.append("{http://www.w3.org/2000/svg}rect") if self.options.rect else "" namespace.append("{http://www.w3.org/2000/svg}rect") if self.options.rect else ""
#namespace.append("{http://www.w3.org/2000/svg}svg") if self.options.svg else "" #namespace.append("{http://www.w3.org/2000/svg}sodipodi") if self.options.sodipodi else "" #do not do this. it will crash InkScape
namespace.append("{http://www.w3.org/2000/svg}text") if self.options.text else "" #namespace.append("{http://www.w3.org/2000/svg}svg") if self.options.svg else ""
namespace.append("{http://www.w3.org/2000/svg}tspan") if self.options.tspan else "" namespace.append("{http://www.w3.org/2000/svg}text") if self.options.text else ""
namespace.append("{http://www.w3.org/2000/svg}tspan") if self.options.tspan else ""
#get all items from selection. Remove all groups from the selection and form a new single group of it. We also handle svg:svg because it behaves like a group container too namespace.append("{http://www.w3.org/2000/svg}lineargradient") if self.options.lineargradient else ""
def parseNodes(self, node): namespace.append("{http://www.w3.org/2000/svg}radialgradient") if self.options.radialgradient else ""
if node.tag in namespace: namespace.append("{http://www.w3.org/2000/svg}meshgradient") if self.options.meshgradient else ""
if node not in self.allPaths: namespace.append("{http://www.w3.org/2000/svg}meshrow") if self.options.meshrow else ""
self.allPaths.append(node) namespace.append("{http://www.w3.org/2000/svg}meshpatch") if self.options.meshpatch else ""
else: namespace.append("{http://www.w3.org/2000/svg}script") if self.options.script else ""
if node.tag != inkex.addNS('g','svg'): namespace.append("{http://www.w3.org/2000/svg}stop") if self.options.stop else ""
self.allNonMigrates.append(node) namespace.append("{http://www.w3.org/2000/svg}use") if self.options.use else ""
if node.tag == inkex.addNS('g','svg') or node.tag == inkex.addNS('svg','svg'):
if node not in self.allGroups: #check if we have selected elements or if we should parse the whole document instead
self.allGroups.append(node) selected = [] #list of elements to parse
groups = node.getchildren() if len(self.svg.selected) == 0:
for element in self.document.getroot().iter(tag=etree.Element):
if element != self.document.getroot():
selected.append(element)
else:
selected = self.svg.selected
def parseNodes(self, element):
if self.options.allitems:
if element not in self.allElements:
if element.tag != inkex.addNS('g','svg') and element.tag != inkex.addNS('svg','svg') and element.tag != inkex.addNS('namedview','sodipodi'):
self.allElements.append(element)
else:
if element.tag in namespace:
if element not in self.allElements:
self.allElements.append(element)
else:
if element.tag != inkex.addNS('g','svg') and element.tag != inkex.addNS('svg','svg') and element.tag != inkex.addNS('namedview','sodipodi'):
if element not in self.allNonMigrates:
self.allNonMigrates.append(element)
if element.tag == inkex.addNS('g','svg') or element.tag == inkex.addNS('svg','svg'):
if element not in self.allGroups:
self.allGroups.append(element)
groups = element.getchildren()
if groups is not None: if groups is not None:
for group in groups: for group in groups:
parseNodes(self, group) parseNodes(self, group)
if group not in self.allGroups: if group not in self.allGroups:
self.allGroups.append(group) self.allGroups.append(group)
for id, item in self.svg.selected.items(): #get all elements from the selection. Remove all groups from the selection and form a new single group of it. We also handle svg:svg because it behaves like a group container too
parseNodes(self, item) for element in selected:
parseNodes(self, element)
if len(self.allPaths) > 0:
#make a new group at root level - TODO: respect the position where the first selected object is in XML tree and put it there instead (or make this optional) if len(self.allElements) > 0:
newGroup = self.document.getroot().add(inkex.Group()) #copy all element into the new group
for oldElement in self.allElements:
#copy all paths into the new group #oldElementId = oldElement.get('id')
for path in self.allPaths: if oldElement.getparent() is not None:
newGroup.add(path.copy()) oldElement.getparent().remove(oldElement)
#newElement.set('id', oldElementId)
#then remove all the old stuff if self.options.droponly == False:
path.getparent().remove(path) #make a new group at root level
newGroup = self.document.getroot().add(inkex.Group())
newElement = oldElement.copy()
newGroup.add(newElement)
#now remove all the obsolete groups if self.options.droponly == False:
if len(self.allGroups) > 0: #now remove all the obsolete groups
for group in self.allGroups: if len(self.allGroups) > 0:
if group.getparent() is not None: for group in self.allGroups:
group.getparent().remove(group) if group.getparent() is not None:
group.getparent().remove(group)
#remove the selected, now empty group (if it's the case)
if len(self.svg.selected) > 0 and len(self.allPaths) > 0: #remove the selected, now empty group (if it's the case) - this applies not if there is no user selection at all so some dangling group(s) might be left over
if self.svg.selected[0].tag == inkex.addNS('g','svg') or self.svg.selected[0].tag == inkex.addNS('svg','svg'): if len(self.svg.selected) > 0 and len(self.allElements) > 0:
if self.svg.selected[0].getparent() is not None: if self.svg.selected[0].tag == inkex.addNS('g','svg') or self.svg.selected[0].tag == inkex.addNS('svg','svg'):
self.svg.selected[0].getparent().remove(self.svg.selected[0]) if self.svg.selected[0].getparent() is not None:
self.svg.selected[0].getparent().remove(self.svg.selected[0])
if len(self.allNonMigrates) > 0:
self.msg("You are going to remove " + str(len(self.allNonMigrates)) + " nodes while migrating:") if self.options.showdroplist and len(self.allNonMigrates) > 0:
self.msg(str(len(self.allNonMigrates)) + " elements were removed during nodes while migration:")
for i in self.allNonMigrates: for i in self.allNonMigrates:
self.msg(i.get('id')) self.msg(i.tag.replace("{http://www.w3.org/2000/svg}","svg:") + " id:" + i.get('id'))
#TODO: make newGroup selected now. How ?
MigrateGroups().run() MigrateGroups().run()