"""This module contains the rows of instructions of knitting patterns.
The :class:`rows <Row>` are part of :class:`knitting patterns
<knittingpattern.KnittingPattern.KnittingPattern>`.
They contain :class:`instructions
<knittingpattern.Instruction.InstructionInRow>` and can be connected to other
rows.
"""
from .Prototype import Prototype
from itertools import chain
from ObservableList import ObservableList
from .utils import unique
COLOR = "color" #: the color of the row
#: an error message
CONISTENCY_MESSAGE = "The data structure must be consistent."
[docs]class Row(Prototype):
"""This class contains the functionality for rows.
This class is used by :class:`knitting patterns
<knittingpattern.KnittingPattern.KnittingPattern>`.
"""
[docs] def __init__(self, row_id, values, parser):
"""Create a new row.
:param row_id: an identifier for the row
:param values: the values from the specification
:param list inheriting_from: a list of specifications to inherit values
from, see :class:`knittingpattern.Prototype.Prototype`
.. note:: Seldomly, you need to create this row on your own. You can
load it with the :mod:`knittingpattern` or the
:class:`knittingpattern.Parser.Parser`.
"""
super().__init__(values)
self._id = row_id
self._instructions = ObservableList()
self._instructions.register_observer(self._instructions_changed)
self._parser = parser
def _instructions_changed(self, change):
"""Call when there is a change in the instructions."""
if change.adds():
for index, instruction in change.items():
if isinstance(instruction, dict):
in_row = self._parser.instruction_in_row(self, instruction)
self.instructions[index] = in_row
else:
instruction.transfer_to_row(self)
@property
def id(self):
"""The id of the row.
:return: the id of the row
"""
return self._id
@property
def instructions(self):
"""The instructions in this row.
:return: a collection of :class:`instructions inside the row
<knittingpattern.Instruction.InstructionInRow>`
:rtype: ObservableList.ObservableList
"""
return self._instructions
@property
def number_of_produced_meshes(self):
"""The number of meshes that this row produces.
:return: the number of meshes that this row produces
:rtype: int
.. seealso::
:meth:`Instruction.number_of_produced_meshes()
<knittingpattern.Instruction.Instruction.number_of_produced_meshes>`,
:meth:`number_of_consumed_meshes`
"""
return sum(instruction.number_of_produced_meshes
for instruction in self.instructions)
@property
def number_of_consumed_meshes(self):
"""The number of meshes that this row consumes.
:return: the number of meshes that this row consumes
:rtype: int
.. seealso::
:meth:`Instruction.number_of_consumed_meshes()
<knittingpattern.Instruction.Instruction.number_of_consumed_meshes>`,
:meth:`number_of_produced_meshes`
"""
return sum(instruction.number_of_consumed_meshes
for instruction in self.instructions)
@property
def produced_meshes(self):
"""The meshes that this row produces with its instructions.
:return: a collection of :class:`meshes <knittingpattern.Mesh.Mesh>`
that this instruction produces
"""
return list(chain(*(instruction.produced_meshes
for instruction in self.instructions)))
@property
def consumed_meshes(self):
"""Same as :attr:`produced_meshes` but for consumed meshes."""
return list(chain(*(instruction.consumed_meshes
for instruction in self.instructions)))
[docs] def __repr__(self):
"""The string representation of this row.
:return: a string representation of this row
:rtype: str
"""
return "<{} {}>".format(self.__class__.__qualname__, self.id)
@property
def color(self):
"""The color of the row.
:return: the color of the row as specified or :obj:`None`
"""
return self.get(COLOR)
@property
def instruction_colors(self):
"""The colors of the instructions in the row in the order tehy appear.
:return: a list of colors of the knitting pattern in the order that
they appear in
:rtype: list
"""
return unique(instruction.colors for instruction in self.instructions)
@property
def last_produced_mesh(self):
"""The last produced mesh.
:return: the last produced mesh
:rtype: knittingpattern.Mesh.Mesh
:raises IndexError: if no mesh is produced
.. seealso:: :attr:`number_of_produced_meshes`
"""
for instruction in reversed(self.instructions):
if instruction.produces_meshes():
return instruction.last_produced_mesh
raise IndexError("{} produces no meshes".format(self))
@property
def last_consumed_mesh(self):
"""The last consumed mesh.
:return: the last consumed mesh
:rtype: knittingpattern.Mesh.Mesh
:raises IndexError: if no mesh is consumed
.. seealso:: :attr:`number_of_consumed_meshes`
"""
for instruction in reversed(self.instructions):
if instruction.consumes_meshes():
return instruction.last_consumed_mesh
raise IndexError("{} consumes no meshes".format(self))
@property
def first_produced_mesh(self):
"""The first produced mesh.
:return: the first produced mesh
:rtype: knittingpattern.Mesh.Mesh
:raises IndexError: if no mesh is produced
.. seealso:: :attr:`number_of_produced_meshes`
"""
for instruction in self.instructions:
if instruction.produces_meshes():
return instruction.first_produced_mesh
raise IndexError("{} produces no meshes".format(self))
@property
def first_consumed_mesh(self):
"""The first consumed mesh.
:return: the first consumed mesh
:rtype: knittingpattern.Mesh.Mesh
:raises IndexError: if no mesh is consumed
.. seealso:: :attr:`number_of_consumed_meshes`
"""
for instruction in self.instructions:
if instruction.consumes_meshes():
return instruction.first_consumed_mesh
raise IndexError("{} consumes no meshes".format(self))
@property
def rows_before(self):
"""The rows that produce meshes for this row.
:rtype: list
:return: a list of rows that produce meshes for this row. Each row
occurs only once. They are sorted by the first occurrence in the
instructions.
"""
rows_before = []
for mesh in self.consumed_meshes:
if mesh.is_produced():
row = mesh.producing_row
if rows_before not in rows_before:
rows_before.append(row)
return rows_before
@property
def rows_after(self):
"""The rows that consume meshes from this row.
:rtype: list
:return: a list of rows that consume meshes from this row. Each row
occurs only once. They are sorted by the first occurrence in the
instructions.
"""
rows_after = []
for mesh in self.produced_meshes:
if mesh.is_consumed():
row = mesh.consuming_row
if rows_after not in rows_after:
rows_after.append(row)
return rows_after
@property
def first_instruction(self):
"""The first instruction of the rows instructions.
:rtype: knittingpattern.Instruction.InstructionInRow
:return: the first instruction in this row's :attr:`instructions`
"""
return self.instructions[0]
@property
def last_instruction(self):
"""The last instruction of the rows instructions.
:rtype: knittingpattern.Instruction.InstructionInRow
:return: the last instruction in this row's :attr:`instructions`
"""
return self.instructions[-1]
__all__ = ["Row", "COLOR"]