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>
<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>
<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>
<separator/>
<label appearance="header">Custom Element Types</label>
<hbox>
<vbox>
<label appearance="header">Geometry</label>
<param name="circle" type="bool" gui-text="circle">true</param> <param name="circle" type="bool" gui-text="circle">true</param>
<param name="clipPath" type="bool" gui-text="clipPath">true</param>
<param name="defs" type="bool" gui-text="defs">true</param>
<param name="ellipse" type="bool" gui-text="ellipse">true</param> <param name="ellipse" type="bool" gui-text="ellipse">true</param>
<param name="image" type="bool" gui-text="image">true</param>
<param name="line" type="bool" gui-text="line">true</param> <param name="line" type="bool" gui-text="line">true</param>
<param name="path" type="bool" gui-text="path">true</param> <param name="path" type="bool" gui-text="path">true</param>
<param name="polyline" type="bool" gui-text="polyline">true</param> <param name="polyline" type="bool" gui-text="polyline">true</param>
<param name="polygon" type="bool" gui-text="polygon">true</param> <param name="polygon" type="bool" gui-text="polygon">true</param>
<param name="rect" type="bool" gui-text="rect">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="svg" type="bool" gui-text="svg">true</param>
<param name="text" type="bool" gui-text="text">true</param> <param name="text" type="bool" gui-text="text">true</param>
<param name="tspan" type="bool" gui-text="tspan">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,31 +13,44 @@ 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 ""
@ -49,58 +62,86 @@ class MigrateGroups(inkex.Effect):
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}sodipodi") if self.options.sodipodi else "" #do not do this. it will crash InkScape
#namespace.append("{http://www.w3.org/2000/svg}svg") if self.options.svg else "" #namespace.append("{http://www.w3.org/2000/svg}svg") if self.options.svg else ""
namespace.append("{http://www.w3.org/2000/svg}text") if self.options.text 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 "" namespace.append("{http://www.w3.org/2000/svg}tspan") if self.options.tspan else ""
namespace.append("{http://www.w3.org/2000/svg}lineargradient") if self.options.lineargradient else ""
namespace.append("{http://www.w3.org/2000/svg}radialgradient") if self.options.radialgradient else ""
namespace.append("{http://www.w3.org/2000/svg}meshgradient") if self.options.meshgradient else ""
namespace.append("{http://www.w3.org/2000/svg}meshrow") if self.options.meshrow else ""
namespace.append("{http://www.w3.org/2000/svg}meshpatch") if self.options.meshpatch else ""
namespace.append("{http://www.w3.org/2000/svg}script") if self.options.script else ""
namespace.append("{http://www.w3.org/2000/svg}stop") if self.options.stop else ""
namespace.append("{http://www.w3.org/2000/svg}use") if self.options.use 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 #check if we have selected elements or if we should parse the whole document instead
def parseNodes(self, node): selected = [] #list of elements to parse
if node.tag in namespace: if len(self.svg.selected) == 0:
if node not in self.allPaths: for element in self.document.getroot().iter(tag=etree.Element):
self.allPaths.append(node) if element != self.document.getroot():
selected.append(element)
else: else:
if node.tag != inkex.addNS('g','svg'): selected = self.svg.selected
self.allNonMigrates.append(node)
if node.tag == inkex.addNS('g','svg') or node.tag == inkex.addNS('svg','svg'): def parseNodes(self, element):
if node not in self.allGroups:
self.allGroups.append(node) if self.options.allitems:
groups = node.getchildren() 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: if len(self.allElements) > 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) #copy all element into the new group
for oldElement in self.allElements:
#oldElementId = oldElement.get('id')
if oldElement.getparent() is not None:
oldElement.getparent().remove(oldElement)
#newElement.set('id', oldElementId)
if self.options.droponly == False:
#make a new group at root level
newGroup = self.document.getroot().add(inkex.Group()) newGroup = self.document.getroot().add(inkex.Group())
newElement = oldElement.copy()
newGroup.add(newElement)
#copy all paths into the new group if self.options.droponly == False:
for path in self.allPaths:
newGroup.add(path.copy())
#then remove all the old stuff
path.getparent().remove(path)
#now remove all the obsolete groups #now remove all the obsolete groups
if len(self.allGroups) > 0: if len(self.allGroups) > 0:
for group in self.allGroups: for group in self.allGroups:
if group.getparent() is not None: if group.getparent() is not None:
group.getparent().remove(group) group.getparent().remove(group)
#remove the selected, now empty group (if it's the case) #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 len(self.svg.selected) > 0 and len(self.allPaths) > 0: if len(self.svg.selected) > 0 and len(self.allElements) > 0:
if self.svg.selected[0].tag == inkex.addNS('g','svg') or self.svg.selected[0].tag == inkex.addNS('svg','svg'): if self.svg.selected[0].tag == inkex.addNS('g','svg') or self.svg.selected[0].tag == inkex.addNS('svg','svg'):
if self.svg.selected[0].getparent() is not None: if self.svg.selected[0].getparent() is not None:
self.svg.selected[0].getparent().remove(self.svg.selected[0]) self.svg.selected[0].getparent().remove(self.svg.selected[0])
if len(self.allNonMigrates) > 0: if self.options.showdroplist and len(self.allNonMigrates) > 0:
self.msg("You are going to remove " + str(len(self.allNonMigrates)) + " nodes while migrating:") 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()