Inkscape.org
Creating New Extensions Iterating over all ShapeElements
  1. #1
    Scott Pakin Scott Pakin @pakin

    An EffectExtension can iterate over all selected objects with self.svg.selection.  But how can it iterate over all selectable ShapeElements?  Essentially, I'd like the equivalent of self.svg.selection if it were run after a Select All.

    The closest I've come so far is

    for child in self.svg:
        if isinstance(child, inkex.ShapeElement):
    

    but that seems also to return things like the g element that represents the layer—something that would not be selected by a Select All.  (I do want to select ordinary g elements, though.)

  2. #2
    inklinea inklinea @inklinea⛰️

    Hello Scott, 

    This might be the wrong way to do it ? 

    Could you rejected an item from a list before based on a simple string match ?

     

    my_selection = self.svg.xpath("//svg:rect | //svg:ellipse | //svg:polygon | //svg:path | //svg:line | //svg:g")
    
    inkex.errormsg('----------')
    inkex.errormsg('----------')
    inkex.errormsg(f'{my_selection} Selection Length {len(my_selection)}')
    
    # Two strings to allow for "" or ''
    rejection_string = 'groupmode="layer"'
    rejection_string2 = "groupmode='layer'"
    
    for my_object in my_selection:
        my_element = str(my_object.tostring().decode("utf-8"))
    
        if rejection_string in my_element or rejection_string2 in my_element:
    
            my_selection.remove(my_object)
    
    inkex.errormsg('----------')
    inkex.errormsg('----------')
    inkex.errormsg(f'{my_selection} Selection Length {len(my_selection)}')

     

  3. #3
    Scott Pakin Scott Pakin @pakin

    Thanks for the suggestion.

    I don't think I can make the self.svg.xpath approach work because it will match all shapes, even those nested within groups.  For example, if you grouped a couple of circles, grouped a couple of squares, and then grouped the two of those together, self.svg.xpath would return the two circles, the two squares, and the three groups (plus the layer group).  In contrast, if a user had previously done a Select All, self.svg.selection would return only the top-level group.

    Assuming layers are the only unexpected direct children of self.svg—I need to do more testing to check—I can indeed check and reject groupmode="layer" groups.  String matching is a bit kludgy, though.  I just tried, and 

    if my_object.get('inkscape:groupmode') == 'layer':

    seems to work without relying on a particular string representation.

  4. #4
    Scott Pakin Scott Pakin @pakin

    Here's what I've come up with so far:

        def top_level_shapes(self):
            'Return a list of all top-level ShapeElements.'
            # Find all ShapeElements whose parent is a layer.
            shapes = []
            layers = {g
                      for g in self.svg.xpath('//svg:g')
                      if g.get('inkscape:groupmode') == 'layer'}
            shapes = [obj
                      for l in layers
                      for obj in l
                      if isinstance(obj, inkex.ShapeElement)]
    
            # Find all ShapeElements whose parent is the root.
            root_shapes = [obj
                           for obj in self.svg
                           if isinstance(obj, inkex.ShapeElement) and \
                              obj not in layers]
            shapes.extend(root_shapes)
            return shapes