#################################################################################################### # # Patro - A Python library to make patterns for fashion design # Copyright (C) 2017 Fabrice Salvaire # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # #################################################################################################### """This module implements the val XML file format. """ #################################################################################################### import logging from pathlib import Path from lxml import etree from Patro.Common.Xml.XmlFile import XmlFileMixin from Patro.Pattern.Pattern import Pattern from .Measurement import VitFile from .VitFormat import ( Point, Line, Spline, ModelingPoint, ModelingSpline, Detail, DetailData, DetailPatternInfo, DetailGrainline, DetailNode, ) #################################################################################################### _module_logger = logging.getLogger(__name__) #################################################################################################### class Dispatcher: """Baseclass to dispatch XML to Python class.""" __TAGS__ = {} ############################################## def from_xml(self, element): tag_class = self.__TAGS__[element.tag] if tag_class is not None: return tag_class(element) else: raise NotImplementedError #################################################################################################### class CalculationDispatcher: """Class to implement a dispatcher for calculations.""" _logger = _module_logger.getChild('CalculationDispatcher') __TAGS__ = { 'arc': None, 'ellipse': None, 'line': Line, 'operation': None, 'point': Point, 'spline': Spline, } ############################################## def __init__(self): # Fixme: could be done in class definition self._mapping = {} # used for Calculation -> XML self._init_mapper() ############################################## def _register_mapping(self, xml_class): calculation_class = xml_class.__calculation__ if calculation_class: self._mapping[xml_class] = calculation_class self._mapping[calculation_class] = xml_class ############################################## def _init_mapper(self): for tag_class in self.__TAGS__.values(): if tag_class is not None: if hasattr(tag_class, '__TYPES__'): for xml_class in tag_class.__TYPES__.values(): if xml_class is not None: self._register_mapping(xml_class) else: self._register_mapping(tag_class) ############################################## def from_xml(self, element): tag_class = self.__TAGS__[element.tag] if hasattr(tag_class, '__TYPES__'): cls = tag_class.__TYPES__[element.attrib['type']] else: cls = tag_class if cls is not None: return cls(element) else: raise NotImplementedError ############################################## def from_calculation(self, calculation): return self._mapping[calculation.__class__].from_calculation(calculation) #################################################################################################### class Modeling: ############################################## def __init__(self): self._items = [] self._id_map = {} ############################################## def __getitem__(self, id): return self._id_map[id] ############################################## def append(self, item): self._items.append(item) self._id_map[item.id] = item #################################################################################################### class ModelingDispatcher(Dispatcher): __TAGS__ = { 'point': ModelingPoint, 'spline': ModelingSpline, } #################################################################################################### class DetailDispatcher(Dispatcher): __TAGS__ = { 'grainline': DetailGrainline, 'patternInfo': DetailPatternInfo, 'data': DetailData, } #################################################################################################### class ValFile(XmlFileMixin): """Class to read/write val file.""" _logger = _module_logger.getChild('ValFile') _calculation_dispatcher = CalculationDispatcher() _modeling_dispatcher = ModelingDispatcher() _detail_dispatcher = DetailDispatcher() ############################################## def __init__(self, path=None): # Fixme: path if path is None: path = '' XmlFileMixin.__init__(self, path) self._vit_file = None self._pattern = None # Fixme: # if path is not None: if path != '': self._read() ############################################## def Write(self, path, vit_file, pattern): # Fixme: write self._vit_file = vit_file self._pattern = pattern self.write(path) ############################################## @property def measurements(self): return self._vit_file.measurements @property def pattern(self): return self._pattern ############################################## def _read(self): # # # # 0.4.0 # cm # # # # # # # # #
# # # tree = self._parse() measurements_path = Path(self._get_xpath_element(tree, 'measurements').text) if not measurements_path.exists(): measurements_path = self._path.parent.joinpath(measurements_path) if not measurements_path.exists(): raise NameError("Cannot find {}".format(measurements_path)) self._vit_file = VitFile(measurements_path) unit = self._get_xpath_element(tree, 'unit').text pattern = Pattern(self._vit_file.measurements, unit) self._pattern = pattern for element in self._get_xpath_element(tree, 'draw/calculation'): try: xml_calculation = self._calculation_dispatcher.from_xml(element) xml_calculation.to_calculation(pattern) except NotImplementedError: self._logger.warning('Not implemented calculation\n' + str(etree.tostring(element))) pattern.eval() modeling = Modeling() for element in self._get_xpath_element(tree, 'draw/modeling'): xml_modeling_item = self._modeling_dispatcher.from_xml(element) modeling.append(xml_modeling_item) # print(xml_modeling_item) details = [] for detail_element in self._get_xpath_element(tree, 'draw/details'): xml_detail = Detail(modeling, detail_element) details.append(xml_detail) print(xml_detail) for element in detail_element: if element.tag == 'nodes': for node in element: xml_node = DetailNode(node) # print(xml_node) xml_detail.append_node(xml_node) else: xml_modeling_item = self._detail_dispatcher.from_xml(element) # Fixme: xml_detail. = xml_modeling_item # print(xml_modeling_item) for node, modeling_item in xml_detail.iter_on_nodes(): # print(node.object_id, '->', modeling_item, '->', modeling_item.object_id) print(node, '->\n', modeling_item, '->\n', pattern.get_calculation(modeling_item.object_id)) ############################################## def write(self, path=None): root = etree.Element('pattern') root.append(etree.Comment('Pattern created with Patro (https://github.com/FabriceSalvaire/Patro)')) etree.SubElement(root, 'version').text = '0.4.0' etree.SubElement(root, 'unit').text = self._pattern.unit etree.SubElement(root, 'author') etree.SubElement(root, 'description') etree.SubElement(root, 'notes') etree.SubElement(root, 'measurements').text = str(self._vit_file.path) etree.SubElement(root, 'increments') draw_element = etree.SubElement(root, 'draw') # Fixme: draw_element.attrib['name'] = 'Pattern piece 1' # Fixme: calculation_element = etree.SubElement(draw_element, 'calculation') modeling_element = etree.SubElement(draw_element, 'modeling') details_element = etree.SubElement(draw_element, 'details') # group_element = etree.SubElement(draw_element, 'groups') for calculation in self._pattern.calculations: xml_calculation = self._calculation_dispatcher.from_calculation(calculation) # print(xml_calculation) # print(xml_calculation.to_xml_string()) calculation_element.append(xml_calculation.to_xml()) if path is None: path = self.path with open(str(path), 'wb') as f: # ElementTree.write() ? f.write(etree.tostring(root, pretty_print=True))