Added rendering of implementations
This commit is contained in:
parent
c53b3b7669
commit
5ba24cafaf
@ -64,9 +64,8 @@ class Application(ApplicationABC):
|
||||
Console.write_line(f'Found {classes.count()} classes')
|
||||
implementations: List[ClassImplementation] = self._parser.parse_implementations(classes)
|
||||
Console.write_line(f'Found {implementations.count()} implementations')
|
||||
Console.write_line(implementations)
|
||||
# self._console_output(classes)
|
||||
xml = self._umlet_creator.generate_xml(classes)
|
||||
xml = self._umlet_creator.generate_xml(classes, implementations)
|
||||
except Exception as e:
|
||||
Console.error('Parsing failed', f'{e} -> {traceback.format_exc()}')
|
||||
exit()
|
||||
|
@ -1,5 +1,6 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from py_to_uxf_core.model.class_implementation import ClassImplementation
|
||||
from py_to_uxf_core.model.python_class import PythonClass
|
||||
|
||||
|
||||
@ -9,4 +10,4 @@ class UmletCreatorABC(ABC):
|
||||
def __init__(self): pass
|
||||
|
||||
@abstractmethod
|
||||
def generate_xml(self, classes: list[PythonClass]) -> str: pass
|
||||
def generate_xml(self, classes: list[PythonClass], implementations: list[ClassImplementation]) -> str: pass
|
||||
|
@ -19,3 +19,9 @@ class Dimension:
|
||||
@height.setter
|
||||
def height(self, value: int):
|
||||
self._height = value
|
||||
|
||||
def __str__(self):
|
||||
return f'(w:{self._width}, h:{self._height})'
|
||||
|
||||
def __repr__(self):
|
||||
return f'(w:{self._width}, h:{self._height})'
|
||||
|
@ -19,3 +19,9 @@ class Position:
|
||||
@y.setter
|
||||
def y(self, value: int):
|
||||
self._y = value
|
||||
|
||||
def __str__(self):
|
||||
return f'(x:{self._x}, y:{self._y})'
|
||||
|
||||
def __repr__(self):
|
||||
return f'(x:{self._x}, y:{self._y})'
|
||||
|
@ -1,3 +1,4 @@
|
||||
import math
|
||||
import textwrap
|
||||
|
||||
from cpl_core.console import Console
|
||||
@ -19,6 +20,9 @@ class UMLClass:
|
||||
self._position = pos
|
||||
self._dimension = dim
|
||||
|
||||
self._attributes = ''
|
||||
self._functions = ''
|
||||
|
||||
@property
|
||||
def cls(self) -> PythonClass:
|
||||
return self._cls
|
||||
@ -39,21 +43,54 @@ class UMLClass:
|
||||
def dimension(self, value: int):
|
||||
self._dimension = value
|
||||
|
||||
def as_xml(self) -> str:
|
||||
def implementation_as_xml(self, base_class: 'UMLClass') -> str:
|
||||
base_point = Position(
|
||||
base_class.position.x + int(base_class.dimension.width / 2),
|
||||
base_class.position.y + base_class.dimension.height
|
||||
)
|
||||
|
||||
sub_point = Position(
|
||||
self._position.x + int(self._dimension.width / 2),
|
||||
self._position.y
|
||||
)
|
||||
|
||||
path_dimension = Dimension(
|
||||
self._position.x - base_class.position.x,
|
||||
sub_point.y - base_point.y
|
||||
)
|
||||
path_position = Position(
|
||||
0,
|
||||
0
|
||||
)
|
||||
|
||||
return f"""\
|
||||
<element>
|
||||
<id>Relation</id>
|
||||
<coordinates>
|
||||
<x>{base_point.x}</x>
|
||||
<y>{base_point.y}</y>
|
||||
<w>0</w>
|
||||
<h>0</h>
|
||||
</coordinates>
|
||||
<panel_attributes>lt=<<-
|
||||
</panel_attributes>
|
||||
<additional_attributes>{float(path_position.x)};{float(path_position.y)};{float(path_dimension.width)};{float(path_dimension.height)}</additional_attributes>
|
||||
</element>
|
||||
""".replace(' ', '').replace('\t', '')
|
||||
|
||||
def create_xml_body(self):
|
||||
px_per_line = 16
|
||||
px_per_char = 2.9
|
||||
self._dimension.height += (self._cls.attributes.count() + self._cls.functions.count()) * px_per_line
|
||||
self._dimension.height += int(
|
||||
round((self._cls.attributes.count() + self._cls.functions.count()) * px_per_line, -1))
|
||||
longest_line_length = self._dimension.width / px_per_char
|
||||
|
||||
attributes = ''
|
||||
functions = ''
|
||||
|
||||
if len(self._cls.attributes) > 0:
|
||||
for atr in self._cls.attributes:
|
||||
attribute = f'{atr.access_modifier.value}{atr.name}: {atr.type}\n'
|
||||
if len(attribute) > longest_line_length:
|
||||
longest_line_length = len(attribute)
|
||||
attributes += attribute
|
||||
self._attributes += attribute
|
||||
|
||||
if len(self._cls.functions) > 0:
|
||||
for func in self._cls.functions:
|
||||
@ -61,10 +98,11 @@ class UMLClass:
|
||||
function = f'{func.access_modifier.value}{func.name}({args}): {func.return_type}\n'
|
||||
if len(function) > longest_line_length:
|
||||
longest_line_length = len(function)
|
||||
functions += function
|
||||
self._functions += function
|
||||
|
||||
self._dimension.width = round(longest_line_length * px_per_char * px_per_char)
|
||||
self._dimension.width = int(round(longest_line_length * px_per_char * px_per_char, -1))
|
||||
|
||||
def as_xml(self) -> str:
|
||||
return f"""\
|
||||
<element>
|
||||
<id>UMLClass</id>
|
||||
@ -77,9 +115,9 @@ class UMLClass:
|
||||
<panel_attributes>
|
||||
{self._cls.name}
|
||||
--
|
||||
{attributes}
|
||||
{self._attributes}
|
||||
--
|
||||
{functions}
|
||||
{self._functions}
|
||||
</panel_attributes>
|
||||
<additional_attributes></additional_attributes>
|
||||
</element>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import textwrap
|
||||
|
||||
from cpl_core.console import Console
|
||||
import classes as classes
|
||||
from cpl_query.extension import List
|
||||
|
||||
from py_to_uxf_core.abc.umlet_creator_abc import UmletCreatorABC
|
||||
from py_to_uxf_core.model.class_implementation import ClassImplementation
|
||||
from py_to_uxf_core.model.dimension import Dimension
|
||||
from py_to_uxf_core.model.position import Position
|
||||
from py_to_uxf_core.model.python_class import PythonClass
|
||||
@ -14,22 +14,81 @@ class UmletCreatorService(UmletCreatorABC):
|
||||
def __init__(self):
|
||||
UmletCreatorABC.__init__(self)
|
||||
|
||||
def generate_xml(self, classes: list[PythonClass]) -> str:
|
||||
xml_cls = ''
|
||||
self._padding = 30
|
||||
|
||||
def _sort_by_implementation(self, uml_classes: List[UMLClass], implementations: List[ClassImplementation]):
|
||||
base_sub_map: dict[UMLClass, list[UMLClass]] = {}
|
||||
moved_classes: list[UMLClass] = []
|
||||
for implementation in implementations:
|
||||
sub_class: UMLClass = uml_classes.where(lambda u: u.cls == implementation.subclass).first()
|
||||
base_class: UMLClass = uml_classes.where(lambda u: u.cls == implementation.base).first()
|
||||
|
||||
if base_class not in base_sub_map:
|
||||
base_sub_map[base_class] = []
|
||||
|
||||
base_sub_map[base_class].append(sub_class)
|
||||
|
||||
last_base = Position(self._padding, self._padding)
|
||||
for base in base_sub_map:
|
||||
subclasses = base_sub_map[base]
|
||||
|
||||
base.position.x = last_base.x
|
||||
base.position.y = self._padding
|
||||
|
||||
last_sub = Position(base.position.x, base.position.y + base.dimension.height)
|
||||
for sub_class in subclasses:
|
||||
sub_class.position.x = base.position.x
|
||||
sub_class.position.y = base.position.y
|
||||
|
||||
if sub_class.position.x <= last_sub.x:
|
||||
sub_class.position.x = last_sub.x
|
||||
if sub_class.position.y <= last_sub.y:
|
||||
sub_class.position.y = last_sub.y + self._padding * 2
|
||||
|
||||
last_sub.x = sub_class.position.x + sub_class.dimension.width + self._padding
|
||||
moved_classes.append(sub_class)
|
||||
|
||||
last_base = last_sub
|
||||
last_base.x += self._padding * 2
|
||||
moved_classes.append(base)
|
||||
|
||||
for cls in uml_classes:
|
||||
if cls in moved_classes:
|
||||
continue
|
||||
|
||||
cls.position.x = last_base.x + self._padding
|
||||
last_base.x = cls.position.x + cls.dimension.width
|
||||
|
||||
def generate_xml(self, classes: List[PythonClass], implementations: List[ClassImplementation]) -> str:
|
||||
uml_classes = List(UMLClass)
|
||||
xml_classes = ''
|
||||
xml_relations = ''
|
||||
default_width = 80
|
||||
default_height = 80
|
||||
next_x = 10
|
||||
next_x = self._padding
|
||||
|
||||
for cls in classes:
|
||||
uml_cls = UMLClass(cls, Position(next_x, 10), Dimension(default_width, default_height))
|
||||
uml_cls = UMLClass(cls, Position(next_x, self._padding), Dimension(default_width, default_height))
|
||||
# trigger xml generation to render coordinates
|
||||
uml_cls.create_xml_body()
|
||||
next_x += round(uml_cls.dimension.width, -1) + self._padding
|
||||
uml_classes.append(uml_cls)
|
||||
|
||||
self._sort_by_implementation(uml_classes, implementations)
|
||||
for implementation in implementations:
|
||||
sub_class: UMLClass = uml_classes.where(lambda u: u.cls == implementation.subclass).first()
|
||||
base_class: UMLClass = uml_classes.where(lambda u: u.cls == implementation.base).first()
|
||||
xml_relations += sub_class.implementation_as_xml(base_class)
|
||||
|
||||
for uml_cls in uml_classes:
|
||||
# save class xml
|
||||
xml_cls += uml_cls.as_xml()
|
||||
next_x += round(uml_cls.dimension.width, -1) + 10
|
||||
xml_classes += uml_cls.as_xml()
|
||||
|
||||
return f"""\
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<diagram program="umlet" version="14.1.1">
|
||||
<zoom_level>10</zoom_level>
|
||||
{xml_cls}
|
||||
{xml_classes}
|
||||
{xml_relations}
|
||||
</diagram>
|
||||
""".replace(' ', '').replace('\t', '')
|
||||
|
Loading…
Reference in New Issue
Block a user