diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 11:50:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 11:50:49 +0000 |
commit | c853ffb5b2f75f5a889ed2e3ef89b818a736e87a (patch) | |
tree | 7d13a0883bb7936b84d6ecdd7bc332b41ed04bee /share/extensions/docs/tutorial/simple-path-extension.rst | |
parent | Initial commit. (diff) | |
download | inkscape-c853ffb5b2f75f5a889ed2e3ef89b818a736e87a.tar.xz inkscape-c853ffb5b2f75f5a889ed2e3ef89b818a736e87a.zip |
Adding upstream version 1.3+ds.upstream/1.3+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'share/extensions/docs/tutorial/simple-path-extension.rst')
-rw-r--r-- | share/extensions/docs/tutorial/simple-path-extension.rst | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/share/extensions/docs/tutorial/simple-path-extension.rst b/share/extensions/docs/tutorial/simple-path-extension.rst new file mode 100644 index 0000000..143b6b1 --- /dev/null +++ b/share/extensions/docs/tutorial/simple-path-extension.rst @@ -0,0 +1,162 @@ +A more complex example: reading path data and changing the style +================================================================ + +.. note:: + This tutorial builds on the files created in :ref:`first-effect-extension`. + +Resources +--------- + +- :download:`Example solution <resources/stroke_parity_extension.zip>` + +Introduction +------------------ + +We are going to write an effect extension that will change any path with +an even number of nodes to a user-defined stroke and any path with an odd +number of nodes to a different user-defined stroke color. + +From :ref:`first-effect-extension`, we already know how to create the extension boilerplate +(without parameters), how to iterate over the selection and how to modify the style. + +This extension adds two more things: Paths and Parameters. + +Setting up the inx file +----------------------- + +Duplicate the files from the `make_red_extension`, and change all the filenames, class names +and extension ID to `stroke_parity_extension`. + +Now for the parameters for the colors. We're also going to add a checkbox to remove the fill of the +selected paths. The params are defined after ``<id>`` and before ``<effect>`` in the inx file: + +.. code:: xml + + <param name="remove_fill" type="bool" gui-text="Remove fill">false</param> + <label appearance="header">Stroke color for path based on number of nodes</label> + <param name="tab" type="notebook"> + <page name="even" gui-text="Even"> + <param name="even_color" type="color" gui-text="Even stroke color:">4278190335</param> + </page> + <page name="odd" gui-text="Odd"> + <param name="odd_color" type="color" gui-text="Odd stroke color:">65535</param> + </page> + </param> + +The default colors are defined as content of the ``<param>`` element, and they are passed as +integers in RGBA format, so for a "red" color (i.e. ``#ff000ff``), one has to write +``256^3*255 + 255 = 4278190335``. + +Color widgets are quite large, so the two widgets are added in a notebook (i.e. two tabs). + +You can also change the submenu the extension will be listed in to ``Modify Path``. + +Restart Inkscape. If you click the extension in the menu, it should look something like this: + +.. figure:: resources/stroke-parity-widget.png + +Adding the parameters to the python file +---------------------------------------- + +When Inkscape calls the extension in the current state, an error message is shown:: + + usage: stroke_parity_extension.py [-h] [--output OUTPUT] [--id IDS] + [--selected-nodes SELECTED_NODES] + [INPUT_FILE] + stroke_parity_extension.py: error: unrecognized arguments: --remove_fill=false --tab=even --even_color=4278190335 --odd_color=65535 + +We also have to tell the ``MakeRedExtension`` class how to parse the parameters. This is done in the +:func:`~inkex.base.InkscapeExtension.add_arguments` method. We have four parameters: the two colors, +the checkbox and the currently selected tab. + +.. code:: python + + def add_arguments(self, pars): + pars.add_argument("--even_color", type=inkex.Color, default=inkex.Color("red")) + pars.add_argument("--odd_color", type=inkex.Color, default=inkex.Color("blue")) + pars.add_argument("--remove_fill", type=inkex.Boolean, default=False) + pars.add_argument("--tab", type=str, default="stroke") + +In ``effect``, these parameters are available as e.g. ``self.options.even_color``. + +.. hint:: + + :func:`inkex.Boolean <inkex.utils.Boolean>` and :class:`inkex.Color <inkex.colors.Color>` are + special types that preprocess the parsed parameter, so that ``self.options.even_color`` is an + :class:`inkex.colors.Color` object and not the string ``"4278190335"``. + +Processing the paths +-------------------- + +Modify the ``effect`` method as follows: + +.. code-block:: python + :linenos: + + def effect(self): + for elem in self.svg.selection.filter(inkex.PathElement): + elem.set('inkscape:modified_by_tutorial', 'Yes') + elem.style['stroke-width'] = 2.0 + if len(elem.path) % 2: # odd number + elem.style.set_color(self.options.odd_color, 'stroke') + else: + elem.style.set_color(self.options.even_color, 'stroke') + + if self.options.remove_fill: + elem.style["fill"] = None + +Code Explanation +~~~~~~~~~~~~~~~~ + +Firstly, we need to loop through each of the selected paths. We already now how to do this +from the first tutorial, but we now filter the selection to only contain +:class:`~inkex.elements._polygons.PathElement` objects - since we want to count the number of nodes. +If other objects, such as a text object or rectangle, are selected, they are ignored. So, for each +iteration of the loop, ``elem`` will contain one of the selected path +objects. + +The second line sets an attribute ``inkscape:modified_by_tutorial`` on +the xml element ``elem``. The attribute API will handle the ``inkscape`` +namespace for us, so we can use a simple colon to indicate the +namespace. This is how all non-special attributes are set and gotten. +But on top of this simple API we don’t have to worry about parsing the +path, transform or the style attributes. Instead the inkex API does all +the parsing for us and provides us with a way to change styles, modify +paths and even do transformations without manual parsing. + +Then we set a stroke-width of 2, using the standard style API which is +assigning into a type of ordered dictionary. + +Next we use the Path API to get the path data of the element, using +:attr:`~inkex.elements._base.ShapeElement.path`. This is an :class:`inkex.paths.Path` object, +which is a list of all path commands (such as Moveto, Lineto, Curveto...). We can use the length +of the list to determine the number of segments, which is (for the simple case of open paths) equal +to the number of nodes. + +We then use the Color API to assign the correct stroke color. The parameters are passed in RGBA format, +and we can use the :func:`~inkex.styles.Style.set_color` function to set opacity and stroke at the same time. +Stroke and stroke opacity are two different style properties, so we can’t set them with a simple +assignment. + +Last we set the fill to ``None`` (``"none"`` would work as well) - if the checkbox for this is +checked. + +Testing the extension +--------------------- + +As we've already learned, there’s no need to set, save or do anything else as we’ve +modified the style in place. + +Save your python script, and re-launch Inkscape. If inkscape was already +open, close it first. You should find your new extension available in +the ``Modify Path`` menu. + +Draw some shapes with the pen tool, select some of the shapes and use the extension. +You should see the stroke color change for each of the objects selected. + +.. note:: + For closed paths, the extension gives incorrect results, because the closing "Z" / "z" + command does not add a new node, but is counted by ``len(path)``. + + This can be avoided by manually counting segments except that are not ZoneClose (zoneClose) commands. + |