Newer
Older
####################################################################################################
#
# 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 <http://www.gnu.org/licenses/>.
#
####################################################################################################
####################################################################################################
import logging
from pathlib import Path
from lxml import etree
from Patro.Common.Xml.XmlFile import XmlFileMixin
from .VitFormat import (
Point,
Line,
Spline,
ModelingPoint,
ModelingSpline,
Detail,
DetailData,
DetailPatternInfo,
DetailGrainline,
DetailNode,
)
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class Dispatcher:
__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,
}
##############################################
# Fixme: could be done in class definition
self._mapping = {} # used for Calculation -> XML
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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
####################################################################################################
__TAGS__ = {
'point': ModelingPoint,
'spline': ModelingSpline,
}
####################################################################################################
class DetailDispatcher(Dispatcher):
__TAGS__ = {
'grainline': DetailGrainline,
'patternInfo': DetailPatternInfo,
'data': DetailData,
}
####################################################################################################
class ValFile(XmlFileMixin):
_logger = _module_logger.getChild('ValFile')
_calculation_dispatcher = CalculationDispatcher()
_modeling_dispatcher = ModelingDispatcher()
_detail_dispatcher = DetailDispatcher()
##############################################
def __init__(self, path=None):
if path is None:
path = ''
XmlFileMixin.__init__(self, path)
self._vit_file = None
self._pattern = None
# if path is not None:
if path != '':
self._read()
##############################################
def Write(self, path, vit_file, pattern):
self._vit_file = vit_file
self._pattern = pattern
self.write(path)
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
##############################################
@property
def measurements(self):
return self._vit_file.measurements
@property
def pattern(self):
return self._pattern
##############################################
def _read(self):
# <?xml version='1.0' encoding='UTF-8'?>
# <pattern>
# <!--Pattern created with Valentina (http://www.valentina-project.org/).-->
# <version>0.4.0</version>
# <unit>cm</unit>
# <author/>
# <description/>
# <notes/>
# <measurements/>
# <increments/>
# <draw name="Pattern piece 1">
# <calculation/>
# <modeling/>
# <details/>
# <groups/>
# </draw>
# </pattern>
tree = self._parse()
measurements_path = self._get_xpath_element(tree, 'measurements').text
if measurements_path is not None:
measurements_path = Path(measurements_path)
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)
measurements = self._vit_file.measurements
else:
self._vit_file = None
measurements = None
unit = self._get_xpath_element(tree, 'unit').text
pattern = Pattern(measurements, unit)
self._pattern = pattern
for element in self._get_xpath_element(tree, 'draw/calculation'):
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)))
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)
details = []
for detail_element in self._get_xpath_element(tree, 'draw/details'):
xml_detail = Detail(modeling, detail_element)
details.append(xml_detail)
for element in detail_element:
if element.tag == 'nodes':
for node in element:
xml_node = DetailNode(node)
xml_detail.append_node(xml_node)
else:
xml_modeling_item = self._detail_dispatcher.from_xml(element)
# Fixme: xml_detail. = 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))