'What is wrong with this inkscape extension?

I am not a programmer. I used to use an inkscape extension that was designed to change the path in inkscape to Gcode. Recently this extension stopped working giving the following error massage. The extension has 5 python files (unicorn.py, init.py, context.py, entities.py, svg_parser.py). The error massage I keep getting is:

Traceback (most recent call last): File "unicorn.py", line 23, in from unicorn.svg_parser import SvgParser File "C:\Program Files\Inkscape\share\inkscape\extensions\unicorn\svg_parser.py", line 4, in import entities ModuleNotFoundError: No module named 'entities'

Based on the error massage I think the problem is with python.py or svg_parder.py I post the unicorn.py and svg_parser.py here. Can anybody help me with this?

unicorn.py text

#!/usr/bin/env python
'''

''
import sys,os
import inkex
from math import *
import getopt
from unicorn.context import GCodeContext
from unicorn.svg_parser import SvgParser

class MyEffect(inkex.Effect):
  def __init__(self):
    inkex.Effect.__init__(self)
    self.OptionParser.add_option("--extrude_multiple",
                      action="store", type="float",
                      dest="extrude_multiple", default="1.0",
                      help="Extrude_multiple")
                      
    self.OptionParser.add_option("--e_r_pattern",
                      action="store", type="float",
                      dest="e_r_pattern", default="1.0",
                      help="E_r_pattern")

    self.OptionParser.add_option("--e_r_speed",
                      action="store", type="float",
                      dest="e_r_speed", default="900.0",
                      help="E_r_speed")

    self.OptionParser.add_option("--z_hop_enabled",
                      action="store", type="inkbool",
                      dest="z_hop_enabled", default="true",
                      help="Z_hop_enabled")
                      
    self.OptionParser.add_option("--z_hop_height",
                      action="store", type="float",
                      dest="z_hop_height", default="10.0",
                      help="Z_hop_height")

    self.OptionParser.add_option("--z_hop_speed",
                      action="store", type="float",
                      dest="z_hop_speed", default="900.0",
                      help="Z_hop_speed")
                      
    self.OptionParser.add_option("--x_move",
                      action="store", type="float",
                      dest="x_move", default="0.1",
                      help="X move")

    self.OptionParser.add_option("--y_move",
                      action="store", type="float",
                      dest="y_move", default="0.1",
                      help="Y move")

    self.OptionParser.add_option("--layer_delay",
                      action="store", type="int",
                      dest="layer_delay", default="10000",
                      help="Layer_delay")

    self.OptionParser.add_option("--retraction",
                      action="store", type="float",
                      dest="retraction", default="0.1",
                      help="Retraction")

    self.OptionParser.add_option("--z_up",
                      action="store", type="float",
                      dest="z_up", default="0.1",
                      help="Z up height")

    self.OptionParser.add_option("--retract_before",
                      action="store", type="float",
                      dest="retract_before", default="0.1",
                      help="Retract/extrude before print")

    self.OptionParser.add_option("--retract_after",
                      action="store", type="float",
                      dest="retract_after", default="0.1",
                      help="Retract/extrude after print")

    self.OptionParser.add_option("--pen-up-angle",
                      action="store", type="float",
                      dest="pen_up_angle", default="50.0",
                      help="Pen Up Angle")
    self.OptionParser.add_option("--pen-down-angle",
                      action="store", type="float",
                      dest="pen_down_angle", default="30.0",
                      help="Pen Down Angle")
    self.OptionParser.add_option("--start-delay",
                      action="store", type="float",
                      dest="start_delay", default="150.0",
                      help="Delay after pen down command before movement in milliseconds")
    self.OptionParser.add_option("--stop-delay",
                      action="store", type="float",
                      dest="stop_delay", default="150.0",
                      help="Delay after pen up command before movement in milliseconds")
    self.OptionParser.add_option("--xy-feedrate",
                      action="store", type="float",
                      dest="xy_feedrate", default="300.0",
                      help="XY axes feedrate in mm/min")
    self.OptionParser.add_option("--z-feedrate",
                      action="store", type="float",
                      dest="z_feedrate", default="0.1",
                      help="Z axis feedrate in mm/min")
    self.OptionParser.add_option("--z-height",
                      action="store", type="float",
                      dest="z_height", default="0.0",
                      help="Z axis print height in mm")
    self.OptionParser.add_option("--finished-height",
                      action="store", type="float",
                      dest="finished_height", default="0.0",
                      help="Z axis height after printing in mm")
    self.OptionParser.add_option("--register-pen",
                      action="store", type="string",
                      dest="register_pen", default="true",
                      help="Add pen registration check(s)")
    self.OptionParser.add_option("--x-home",
                      action="store", type="float",
                      dest="x_home", default="0.0",
                      help="Starting X position")
    self.OptionParser.add_option("--y-home",
                      action="store", type="float",
                      dest="y_home", default="0.0",
                      help="Starting Y position")
    self.OptionParser.add_option("--num-copies",
                      action="store", type="int",
                      dest="num_copies", default="1")
    self.OptionParser.add_option("--continuous",
                      action="store", type="string",
                      dest="continuous", default="false",
                      help="Plot continuously until stopped.")
    self.OptionParser.add_option("--pause-on-layer-change",
                      action="store", type="string",
                      dest="pause_on_layer_change", default="false",
                      help="Pause on layer changes.")
    self.OptionParser.add_option("--tab",
                      action="store", type="string",
                      dest="tab")

  def output(self):
    self.context.generate()

  def effect(self):
    self.context = GCodeContext(self.options.extrude_multiple , self.options.e_r_pattern, self.options.e_r_speed, self.options.z_hop_enabled, self.options.z_hop_height,self.options.z_hop_speed, self.options.x_move, self.options.y_move, self.options.layer_delay, self.options.retraction,
                           self.options.z_up, self.options.retract_before, self.options.retract_after,
                           self.options.xy_feedrate, self.options.z_feedrate,
                           self.options.start_delay, self.options.stop_delay,
                           self.options.pen_up_angle, self.options.pen_down_angle,
                           self.options.z_height, self.options.finished_height,
                           self.options.x_home, self.options.y_home,
                           self.options.register_pen,
                           self.options.num_copies,
                           self.options.continuous,
                           self.svg_file)
    parser = SvgParser(self.document.getroot(), self.options.pause_on_layer_change)
    parser.parse()
    for entity in parser.entities:
      entity.get_gcode(self.context)

if __name__ == '__main__':   #pragma: no cover
  e = MyEffect()
  e.affect()

Svg_parser.py text

import inkex
import cubicsuperpath
import simplepath
import simplestyle
import cspsubdiv
from simpletransform import *
from bezmisc import *
import entities
from math import radians
import sys
import pprint

def parseLengthWithUnits(str):
    '''
    Parse an SVG value which may or may not have units attached
    This version is greatly simplified in that it only allows: no units,
    units of px, and units of %.  Everything else, it returns None for.
    There is a more general routine to consider in scour.py if more
    generality is ever needed.
    '''
    u = 'px'
    s = str.strip()
    if s[-2:] == 'px':
        s = s[:-2]
    elif s[-1:] == '%':
        u = '%'
        s = s[:-1]
    try:
        v = float(s)
    except:
        return None, None
    return v, u

def subdivideCubicPath(sp, flat, i=1):
    """
    Break up a bezier curve into smaller curves, each of which
    is approximately a straight line within a given tolerance
    (the "smoothness" defined by [flat]).

    This is a modified version of cspsubdiv.cspsubdiv(). I rewrote the recursive
    call because it caused recursion-depth errors on complicated line segments.
    """

    while True:
        while True:
            if i >= len(sp):
                return

            p0 = sp[i - 1][1]
            p1 = sp[i - 1][2]
            p2 = sp[i][0]
            p3 = sp[i][1]

            b = (p0, p1, p2, p3)

            if cspsubdiv.maxdist(b) > flat:
                break

            i += 1

        one, two = beziersplitatt(b, 0.5)
        sp[i - 1][2] = one[1]
        sp[i][0] = two[2]
        p = [one[2], one[3], two[1]]
        sp[i:1] = [p]

class SvgIgnoredEntity:
    def load(self, node, mat):
        self.tag = node.tag

    def __str__(self):
        return "Ignored '%s' tag" % self.tag

    def get_gcode(self, context):
        #context.codes.append("(" + str(self) + ")")
        # context.codes.append("")
        return

class SvgPath(entities.PolyLine):
    def load(self, node, mat):
        d = node.get('d')
        if len(simplepath.parsePath(d)) == 0:
            return
        p = cubicsuperpath.parsePath(d)
        applyTransformToPath(mat, p)

        # p is now a list of lists of cubic beziers [ctrl p1, ctrl p2, endpoint]
        # where the start-point is the last point in the previous segment
        self.segments = []
        for sp in p:
            points = []
            subdivideCubicPath(sp, 0.2)  # TODO: smoothness preference
            for csp in sp:
                points.append((csp[1][0], csp[1][1]))
            self.segments.append(points)

    def new_path_from_node(self, node):
        newpath = inkex.etree.Element(inkex.addNS('path', 'svg'))
        s = node.get('style')
        if s:
            newpath.set('style', s)
        t = node.get('transform')
        if t:
            newpath.set('transform', t)
        return newpath

class SvgRect(SvgPath):
    def load(self, node, mat):
        newpath = self.new_path_from_node(node)
        x = float(node.get('x'))
        y = float(node.get('y'))
        w = float(node.get('width'))
        h = float(node.get('height'))
        a = []
        a.append(['M ', [x, y]])
        a.append([' l ', [w, 0]])
        a.append([' l ', [0, h]])
        a.append([' l ', [-w, 0]])
        a.append([' Z', []])
        newpath.set('d', simplepath.formatPath(a))
        SvgPath.load(self, newpath, mat)

class SvgLine(SvgPath):
    def load(self, node, mat):
        newpath = self.new_path_from_node(node)
        x1 = float(node.get('x1'))
        y1 = float(node.get('y1'))
        x2 = float(node.get('x2'))
        y2 = float(node.get('y2'))
        a = []
        a.append(['M ', [x1, y1]])
        a.append([' L ', [x2, y2]])
        newpath.set('d', simplepath.formatPath(a))
        SvgPath.load(self, newpath, mat)

class SvgPolyLine(SvgPath):
    def load(self, node, mat):
        newpath = self.new_path_from_node(node)
        pl = node.get('points', '').strip()
        if pl == '':
            return
        pa = pl.split()
        if not len(pa):
            return

        d = "M " + pa[0]
        for i in range(1, len(pa)):
            d += " L " + pa[i]
        newpath.set('d', d)
        SvgPath.load(self, newpath, mat)

class SvgEllipse(SvgPath):
    def load(self, node, mat):
        rx = float(node.get('rx', '0'))
        ry = float(node.get('ry', '0'))
        SvgPath.load(self, self.make_ellipse_path(rx, ry, node), mat)

    def make_ellipse_path(rx, ry, node):
        if rx == 0 or ry == 0:
            return None
        cx = float(node.get('cx', '0'))
        cy = float(node.get('cy', '0'))
        x1 = cx - rx
        x2 = cx + rx
        d = 'M %f,%f ' % (x1, cy) + \
            'A %f,%f ' % (rx, ry) + \
            '0 1 0 %f, %f ' % (x2, cy) + \
            'A %f,%f ' % (rx, ry) + \
            '0 1 0 %f,%f' % (x1, cy)
        newpath = self.new_path_from_node(node)
        newpath.set('d', d)
        return newpath

class SvgCircle(SvgEllipse):
    def load(self, node, mat):
        rx = float(node.get('r', '0'))
        SvgPath.load(self, self.make_ellipse_path(rx, rx, node), mat)

class SvgText(SvgIgnoredEntity):
    def load(self, node, mat):
        inkex.errormsg(
            'Warning: unable to draw text. please convert it to a path first.')
        SvgIgnoredEntity.load(self, node, mat)

class SvgLayerChange():
    def __init__(self, layer_name):
        self.layer_name = layer_name

    def get_gcode(self, context):
        context.codes.append("M01 (Plotting layer '%s')" % self.layer_name)

class SvgParser:

    entity_map = {
        'path': SvgPath,
        'rect': SvgRect,
        'line': SvgLine,
        'polyline': SvgPolyLine,
        'polygon': SvgPolyLine,
        'circle': SvgCircle,
        'ellipse': SvgEllipse,
        'pattern': SvgIgnoredEntity,
        'metadata': SvgIgnoredEntity,
        'defs': SvgIgnoredEntity,
        'eggbot': SvgIgnoredEntity,
        ('namedview', 'sodipodi'): SvgIgnoredEntity,
        'text': SvgText
    }
    def __init__(self, svg, pause_on_layer_change='false'):
        self.svg = svg
        self.pause_on_layer_change = pause_on_layer_change
        self.entities = []

    def getLength(self, name, default):
        '''
        Get the <svg> attribute with name "name" and default value "default"
        Parse the attribute into a value and associated units.  Then, accept
        no units (''), units of pixels ('px'), and units of percentage ('%').
        '''
        str = self.svg.get(name)
        if str:
            v, u = parseLengthWithUnits(str)
            if not v:
                # Couldn't parse the value
                return None
            elif (u == '') or (u == 'px'):
                return v
            elif u == '%':
                return float(default) * v / 100.0
            else:
                # Unsupported units
                return None
        else:
            # No width specified; assume the default value
            return float(default)

    def parse(self):
        # 0.28222 scale determined by comparing pixels-per-mm in a default Inkscape file.
        self.svgWidth = self.getLength('width', 354) * 0.28222
        self.svgHeight = self.getLength('height', 354) * 0.28222
        self.recursivelyTraverseSvg(self.svg, [
                                    [0.28222, 0.0, -(self.svgWidth/2.0)], [0.0, -0.28222, (self.svgHeight/2.0)]])

    # TODO: center this thing
    def recursivelyTraverseSvg(self, nodeList,
                                matCurrent=[[1.0, 0.0, 0.0],
                                            [0.0, -1.0, 0.0]],
                                parent_visibility='visible'):
        """
        Recursively traverse the svg file to plot out all of the
        paths.  The function keeps track of the composite transformation
        that should be applied to each path.

        This function handles path, group, line, rect, polyline, polygon,
        circle, ellipse and use (clone) elements. Notable elements not
        handled include text.  Unhandled elements should be converted to
        paths in Inkscape.

        TODO: There's a lot of inlined code in the eggbot version of this
        that would benefit from the Entities method of dealing with things.
        """
        for node in nodeList:
            # Ignore invisible nodes
            v = node.get('visibility', parent_visibility)
            if v == 'inherit':
                v = parent_visibility
            if v == 'hidden' or v == 'collapse':
                pass

            # first apply the current matrix transform to this node's transform
            matNew = composeTransform(
                matCurrent, parseTransform(node.get("transform")))

            if node.tag == inkex.addNS('g', 'svg') or node.tag == 'g':
                if (node.get(inkex.addNS('groupmode', 'inkscape')) == 'layer'):
                    layer_name = node.get(inkex.addNS('label', 'inkscape'))
                    if(self.pause_on_layer_change == 'true'):
                        self.entities.append(SvgLayerChange(layer_name))
                self.recursivelyTraverseSvg(
                    node, matNew, parent_visibility=v)
            elif node.tag == inkex.addNS('use', 'svg') or node.tag == 'use':
                refid = node.get(inkex.addNS('href', 'xlink'))
                if refid:
                    # [1:] to ignore leading '#' in reference
                    path = '//*[@id="%s"]' % refid[1:]
                    refnode = node.xpath(path)
                    if refnode:
                        x = float(node.get('x', '0'))
                        y = float(node.get('y', '0'))
                        # Note: the transform has already been applied
                        if (x != 0) or (y != 0):
                            matNew2 = composeTransform(
                                matNew, parseTransform('translate(%f,%f)' % (x, y)))
                        else:
                            matNew2 = matNew
                        v = node.get('visibility', v)
                        self.recursivelyTraverseSvg(
                            refnode, matNew2, parent_visibility=v)
                    else:
                        pass
                else:
                    pass
            elif not isinstance(node.tag, basestring):
                pass
            else:
                entity = self.make_entity(node, matNew)
                if entity == None:
                    inkex.errormsg(
                        'Warning: unable to draw object, please convert it to a path first.')

    def make_entity(self, node, mat):
        for nodetype in SvgParser.entity_map.keys():
            tag = nodetype
            ns = 'svg'
            if(type(tag) is tuple):
                tag = nodetype[0]
                ns = nodetype[1]
            if node.tag == inkex.addNS(tag, ns) or node.tag == tag:
                constructor = SvgParser.entity_map[nodetype]
                entity = constructor()
                entity.load(node, mat)
                self.entities.append(entity)
                return entity
        return None

# the python


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source