Inkscape.org
Beyond the Basics DIY extension not appearing in menu
  1. #1
    Lomax Lomax @Lomax

    I'd like to try my hand at making a simple extension for Inkscape (0.92) but cannot even get it to appear in the menu. I started by copying the Render/Gears extension, renaming the files to render_dial.inx and render_dial.py, and putting them in ~/.config/inkscape/extensions/ (which I have confirmed is my user extensions location). I have only made minimal changes to the files - the inx file (render_dial.inx) looks like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
        <name>Dial</name>
        <id>org.cwo.inkscape.dial</id>
        <param name="teeth"    type="int"   min="6"    max="360"    gui-text="Number of teeth:">24</param>
        <param name="pitch"    type="float" min="0.0"  max="1000.0" gui-text="Circular pitch (tooth size):">20.0</param>
        <param name="angle"    type="float" min="10.0" max="30.0"   gui-text="Pressure angle (degrees):">20.0</param>
        <param name="centerdiameter"    type="float" min="0.0" max="1000.0"   gui-text="Diameter of center hole (0 for none):">20.0</param>
        <param name="unit" gui-text="Units:" type="optiongroup" appearance="combo">
            <option value="px">px</option>
            <option value="in">in</option>
            <option value="mm">mm</option>
        </param>
        <label>Unit of measurement for both circular pitch and center diameter.</label>
        <effect>
            <object-type>all</object-type>
            <effects-menu>
                <submenu name="Render" />
            </effects-menu>                                 
        </effect>
        <script>
            <command location="inx" interpreter="python">render_dial.py</command>
        </script>
    </inkscape-extension>

    And the Python file (render_dial.py)

    #!/usr/bin/env python
    
    """
    Generate dial scale in SVG
    """
    
    from math import acos, cos, pi, radians, sin, sqrt
    
    import inkex
    from inkex import PathElement
    
    def involute_intersect_angle(Rb, R):
        Rb, R = float(Rb), float(R)
        return (sqrt(R ** 2 - Rb ** 2) / Rb) - (acos(Rb / R))
    
    def point_on_circle(radius, angle):
        x = radius * cos(angle)
        y = radius * sin(angle)
        return x, y
    
    def points_to_svgd(p):
        f = p[0]
        p = p[1:]
        svgd = 'M{:.5f},{:.5f}'.format(f[0], f[1])
        for x in p:
            svgd += ' L{:.5f},{:.5f}'.format(x[0], x[1])
        svgd += 'z'
        return svgd
    
    class Dial(inkex.GenerateExtension):
        container_label = 'Rendered Dial'
    
        def add_arguments(self, pars):
            pars.add_argument("--teeth", type=int, default=24, help="Number of teeth")
            pars.add_argument("--pitch", type=float, default=20.0, help="Circular Pitch")
            pars.add_argument("--angle", type=float, default=20.0, help="Pressure Angle")
            pars.add_argument("--centerdiameter", type=float, default=10.0, help="Diameter of hole")
            pars.add_argument("--unit", default="px", help="unit for pitch and center diameter")
    
        def generate(self):
            teeth = self.options.teeth
            pitch = self.svg.unittouu(str(self.options.pitch) + self.options.unit)
            angle = self.options.angle  # Angle of tangent to tooth at circular pitch wrt radial line.
            centerdiameter = self.svg.unittouu(str(self.options.centerdiameter) + self.options.unit)
    
            # print >>sys.stderr, "Teeth: %s\n"        % teeth
    
            two_pi = 2.0 * pi
    
            # Pitch (circular pitch): Length of the arc from one tooth to the next)
            # Pitch diameter: Diameter of pitch circle.
            pitch_diameter = float(teeth) * pitch / pi
            pitch_radius = pitch_diameter / 2.0
    
            # Base Circle
            base_diameter = pitch_diameter * cos(radians(angle))
            base_radius = base_diameter / 2.0
    
            # Diametrial pitch: Number of teeth per unit length.
            pitch_diametrial = float(teeth) / pitch_diameter
    
            # Addendum: Radial distance from pitch circle to outside circle.
            addendum = 1.0 / pitch_diametrial
    
            # Outer Circle
            outer_radius = pitch_radius + addendum
            outer_diameter = outer_radius * 2.0
    
            # Tooth thickness: Tooth width along pitch circle.
            tooth = (pi * pitch_diameter) / (2.0 * float(teeth))
    
            # Undercut?
            undercut = (2.0 / (sin(radians(angle)) ** 2))
            needs_undercut = teeth < undercut
    
            # Clearance: Radial distance between top of tooth on one gear to bottom of gap on another.
            clearance = 0.0
    
            # Dedendum: Radial distance from pitch circle to root diameter.
            dedendum = addendum + clearance
    
            # Root diameter: Diameter of bottom of tooth spaces.
            root_radius = pitch_radius - dedendum
            root_diameter = root_radius * 2.0
    
            half_thick_angle = two_pi / (4.0 * float(teeth))
            pitch_to_base_angle = involute_intersect_angle(base_radius, pitch_radius)
            pitch_to_outer_angle = involute_intersect_angle(base_radius, outer_radius) - pitch_to_base_angle
    
            centers = [(x * two_pi / float(teeth)) for x in range(teeth)]
    
            points = []
    
            for c in centers:
    
                # Angles
                pitch1 = c - half_thick_angle
                base1 = pitch1 - pitch_to_base_angle
                outer1 = pitch1 + pitch_to_outer_angle
    
                pitch2 = c + half_thick_angle
                base2 = pitch2 + pitch_to_base_angle
                outer2 = pitch2 - pitch_to_outer_angle
    
                # Points
                b1 = point_on_circle(base_radius, base1)
                p1 = point_on_circle(pitch_radius, pitch1)
                o1 = point_on_circle(outer_radius, outer1)
    
                b2 = point_on_circle(base_radius, base2)
                p2 = point_on_circle(pitch_radius, pitch2)
                o2 = point_on_circle(outer_radius, outer2)
    
                if root_radius > base_radius:
                    pitch_to_root_angle = pitch_to_base_angle - involute_intersect_angle(base_radius, root_radius)
                    root1 = pitch1 - pitch_to_root_angle
                    root2 = pitch2 + pitch_to_root_angle
                    r1 = point_on_circle(root_radius, root1)
                    r2 = point_on_circle(root_radius, root2)
                    p_tmp = [r1, p1, o1, o2, p2, r2]
                else:
                    r1 = point_on_circle(root_radius, base1)
                    r2 = point_on_circle(root_radius, base2)
                    p_tmp = [r1, b1, p1, o1, o2, p2, b2, r2]
    
                points.extend(p_tmp)
    
            path = points_to_svgd(points)
    
            # Create SVG Path for gear
            style = {'stroke': '#000000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
            gear = PathElement()
            gear.style = style
            gear.path = path
            yield gear
    
            if centerdiameter > 0.0:
                arc = PathElement.arc((0, 0), centerdiameter / 2)
                arc.style = style
                yield arc
    
    
    if __name__ == '__main__':
        Dial().run()

    It does not appear in the Extensions/Render submenu, or anywhere else, nor do I get any error messages when launcing Inkscape from the console. If I change the submenu name to something else, e.g. "blabla" I do get a new submenu on the Extensions menu, but it is empty. If I purposely make an error in the .inx file I get an appropriate error message when launcing Inkscape from the console. So the extension is being found by Inkscape, but no menu entry is created for it. I have tried:

    • Changing the name of the extension and its files
    • Changing the id, e.g. to "org.inkscape.extensions.dial"
    • Putting the files in /usr/share/inkscape/extensions (where the original files are)
    • Changing the python class name
    • Changing the submenu name
    • Changing `<command location="inx" ... ` to `<command reldir="extensions" ... `
    • Etc

    None of this has led to any change in behaviour other than the appearance of a new submenu when one is specified. The actual menu item never appears. Any ideas?  

     

  2. #2
    Lomax Lomax @Lomax

    This stripped down version doesn't work either.

    render_dial.inx

    <?xml version="1.0" encoding="UTF-8"?>
    <inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
        <name>Dial</name>
        <id>org.cwo.inkscape.dial</id>
        <label>Lorem ipsum</label>
        <effect>
            <object-type>all</object-type>
            <effects-menu>
                <submenu name="Render" />
            </effects-menu>                                 
        </effect>
        <script>
            <command location="inx" interpreter="python">render_dial.py</command>
        </script>
    </inkscape-extension>

    render_dial.py

    #!/usr/bin/env python
    
    import inkex
    
    class Dial(inkex.GenerateExtension):
        container_label = 'Rendered Dial'
    
        def generate(self):
            yield True
    
    if __name__ == '__main__':
        Dial().run()
    

    No errors, no menu entry, no nothing.

  3. #3
    Lomax Lomax @Lomax
    *

    Ok, I think I found something... If I change...

        <script>
            <command location="inx" interpreter="python">render_dial.py</command>
        </script>

    ... to...

        <script>
            <command reldir="extensions" interpreter="python">render_dial.py</command>
        </script>

    ... then the menu item appears. Is location="inx" perhaps a new 1.0 attribute?

  4. #4
    Tyler Durden Tyler Durden @TylerDurden

    Maybe answer is in in the wiki:

    https://wiki.inkscape.org/wiki/index.php?title=Updating_your_Extension_for_1.0

  5. #5
    Lomax Lomax @Lomax

    Maybe I didn't think to look at that particular page.

Inkscape Inkscape.org Inkscape Forum Beyond the Basics DIY extension not appearing in menu