Added width and height rendering

This commit is contained in:
Sven Heidemann 2022-01-16 14:56:02 +01:00
parent 4b7f0e8231
commit 61b1838c40
6 changed files with 131 additions and 91 deletions

View File

@ -1,3 +1,5 @@
import traceback
from cpl_core.application import ApplicationABC from cpl_core.application import ApplicationABC
from cpl_core.configuration import ConfigurationABC from cpl_core.configuration import ConfigurationABC
from cpl_core.console import Console from cpl_core.console import Console
@ -54,13 +56,19 @@ class Application(ApplicationABC):
Console.write_line(f'Input path:', self._path) Console.write_line(f'Input path:', self._path)
Console.write_line(f'Output path:', self._file) Console.write_line(f'Output path:', self._file)
classes: list[PythonClass] = self._parser.parse() xml = ''
# self._console_output(classes) try:
xml = self._umlet_creator.generate_xml(classes) classes: list[PythonClass] = self._parser.parse()
Console.write_line(f'Found {len(classes)} classes')
# self._console_output(classes)
xml = self._umlet_creator.generate_xml(classes)
except Exception as e:
Console.error('Parsing failed', f'{e} -> {traceback.format_exc()}')
exit()
if not self._file.endswith('.uxf'): if not self._file.endswith('.uxf'):
Console.error(f'Unexpected file {self._file}') Console.error(f'Unexpected file {self._file}')
return exit()
with open(self._file, 'w+') as file: with open(self._file, 'w+') as file:
file.write(xml) file.write(xml)

View File

@ -16,11 +16,11 @@ class PythonClass:
return self._name return self._name
@property @property
def functions(self) -> list[PythonFunction]: def functions(self) -> List[PythonFunction]:
return self._functions return self._functions
@property @property
def attributes(self) -> list[PythonClassAttribute]: def attributes(self) -> List[PythonClassAttribute]:
return self._attributes return self._attributes
def add_function(self, func: PythonFunction): def add_function(self, func: PythonFunction):

View File

@ -27,22 +27,43 @@ class UMLClass:
def position(self) -> Position: def position(self) -> Position:
return self._position return self._position
@position.setter
def position(self, value: int):
self._position = value
@property @property
def dimension(self) -> Dimension: def dimension(self) -> Dimension:
return self._dimension return self._dimension
@dimension.setter
def dimension(self, value: int):
self._dimension = value
def as_xml(self) -> str: def as_xml(self) -> str:
px_per_line = 16
px_per_char = 2.9
self._dimension.height += (self._cls.attributes.count() + self._cls.functions.count()) * px_per_line
longest_line_length = self._dimension.width / px_per_char
attributes = '' attributes = ''
functions = '' functions = ''
if len(self._cls.attributes) > 0: if len(self._cls.attributes) > 0:
for atr in self._cls.attributes: for atr in self._cls.attributes:
attributes += f'{atr.access_modifier.value}{atr.name}: {atr.type}\n' attribute = f'{atr.access_modifier.value}{atr.name}: {atr.type}\n'
if len(attribute) > longest_line_length:
longest_line_length = len(attribute)
attributes += attribute
if len(self._cls.functions) > 0: if len(self._cls.functions) > 0:
for func in self._cls.functions: for func in self._cls.functions:
args = '' args = ''
functions += f'{func.access_modifier.value}{func.name}({args}): {func.return_type}\n' 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._dimension.width = round(longest_line_length * px_per_char * px_per_char)
return f"""\ return f"""\
<element> <element>

View File

@ -1,5 +1,6 @@
from typing import Optional from typing import Optional
from cpl_core.console import Console
from cpl_query.extension import List from cpl_query.extension import List
from py_to_uxf_core.abc.function_scanner_abc import FunctionScannerABC from py_to_uxf_core.abc.function_scanner_abc import FunctionScannerABC
@ -20,47 +21,48 @@ class FunctionScannerService(FunctionScannerABC):
# async def xy(x: int, y: int) -> int: # async def xy(x: int, y: int) -> int:
# async def _xy(x: int, y: int) -> int: # async def _xy(x: int, y: int) -> int:
# async def __xy(x: int, y: int) -> int: # async def __xy(x: int, y: int) -> int:
if line.startswith('def') or line.startswith('async def'): if not line.startswith('def ') and not line.startswith('async def '):
line = line.replace('\t', '') return None
# name
name = line.split('def ')[1] line = line.replace('\t', '')
name = name.split('(')[0] # name
name = line.split('def ')[1]
name = name.split('(')[0]
access_modifier = AccessModifierEnum.public
args = List()
return_type = 'None'
# access_modifier
if name == '__init__' or name == '__repr__' or name == '__str__':
access_modifier = AccessModifierEnum.public access_modifier = AccessModifierEnum.public
args = List() elif name.startswith('_'):
return_type = 'None' access_modifier = AccessModifierEnum.protected
elif name.startswith('__'):
access_modifier = AccessModifierEnum.private
# access_modifier # args
if name == '__init__' or name == '__repr__' or name == '__str__': args_str = line.split('(')[1]
access_modifier = AccessModifierEnum.public args_str = args_str.split(')')[0]
elif name.startswith('_'): # multiple arguments
access_modifier = AccessModifierEnum.protected if ',' in args_str:
elif name.startswith('__'): for argument in args_str.split(','):
access_modifier = AccessModifierEnum.private # with type
type_str = 'any'
arg_name = argument
if ':' in argument:
type_str = argument.split(': ')[1]
arg_name = argument.split(': ')[0]
# without type
if arg_name.startswith(' '):
arg_name = arg_name.split(' ')[1]
args.append(PythonFunctionArgument(arg_name, type_str))
# single argument
elif args_str != '':
pass
# args # return_type
args_str = line.split('(')[1] if '->' in line:
args_str = args_str.split(')')[0] return_type_str = line.split('-> ')[1]
# multiple arguments return_type = return_type_str.split(':')[0]
if ',' in args_str:
for argument in args_str.split(','):
# with type
type_str = 'any'
arg_name = argument
if ':' in argument:
type_str = argument.split(': ')[1]
arg_name = argument.split(': ')[0]
# without type
if arg_name.startswith(' '):
arg_name = arg_name.split(' ')[1]
args.append(PythonFunctionArgument(arg_name, type_str))
# single argument
elif args_str != '':
pass
# return_type return PythonFunction(access_modifier, name, args, return_type)
if '->' in line:
return_type_str = line.split('-> ')[1]
return_type = return_type_str.split(':')[0]
return PythonFunction(access_modifier, name, args, return_type)
return None

View File

@ -1,3 +1,4 @@
import traceback
from typing import Optional from typing import Optional
from cpl_core.console import Console from cpl_core.console import Console
@ -35,53 +36,60 @@ class PythonParserService(PythonParserABC):
is_function = False is_function = False
with open(file, 'r') as file_content: with open(file, 'r') as file_content:
cls: Optional[PythonClass] = None cls: Optional[PythonClass] = None
for line in file_content.readlines(): lines = file_content.readlines()
line_with_tabs = line
line = line.replace(' ', '')
line = line.replace('\t', '')
# replace line break at the end of line
if line.endswith('\n'):
line = line.replace('\n', '')
if line == '\n' or line == '': for i in range(len(lines)):
if is_function: try:
is_function = False line = lines[i]
continue line_with_tabs = line
line = line.replace(' ', '')
line = line.replace('\t', '')
# replace line break at the end of line
if line.endswith('\n'):
line = line.replace('\n', '')
# one line comments if line == '\n' or line == '':
if line != '"""' and line.startswith('"""') and line.endswith('"""') or line.startswith('#'): if is_function:
continue is_function = False
continue
# start multi line comment # one line comments
if line.startswith('"""') or line.count('"""') == 1 and not is_comment: if line != '"""' and line.startswith('"""') and line.endswith('"""') or line.startswith('#'):
is_comment = True continue
continue
# end multi line comment # start multi line comment
if line.startswith('"""') or line.endswith('"""') or line.count('"""') == 1 and is_comment: if line.startswith('"""') or line.count('"""') == 1 and not is_comment:
is_comment = False is_comment = True
continue continue
if is_comment: # end multi line comment
continue if line.startswith('"""') or line.endswith('"""') or line.count('"""') == 1 and is_comment:
is_comment = False
continue
if cls is None: if is_comment:
cls = self._class_scanner.scan_line_for_classes(line) continue
if cls is not None:
classes.append(cls)
continue
func = self._function_scanner.scan_line_for_function(line) if cls is None:
if func is not None: cls = self._class_scanner.scan_line_for_classes(line)
cls.add_function(func) if cls is not None:
is_function = True if func.name != '__init__' else False classes.append(cls)
continue continue
if len(line_with_tabs.split(' ')) > 3 or is_function: func = self._function_scanner.scan_line_for_function(line)
continue if func is not None:
cls.add_function(func)
is_function = True if func.name != '__init__' else False
continue
attribute = self._attribute_scanner.scan_line_for_attribute(line) if len(line_with_tabs.split(' ')) > 3 or is_function:
if attribute is not None: continue
cls.add_attribute(attribute)
attribute = self._attribute_scanner.scan_line_for_attribute(line)
if attribute is not None:
cls.add_attribute(attribute)
except Exception as e:
Console.error(f'Parsing {file}@{i}', f'{e} -> {traceback.format_exc()}')
exit()
return classes return classes

View File

@ -16,14 +16,15 @@ class UmletCreatorService(UmletCreatorABC):
def generate_xml(self, classes: list[PythonClass]) -> str: def generate_xml(self, classes: list[PythonClass]) -> str:
xml_cls = '' xml_cls = ''
width = 400 default_width = 80
height = 300 default_height = 80
next_x = 10 next_x = 10
for cls in classes: for cls in classes:
uml_cls = UMLClass(cls, Position(next_x, 10), Dimension(width, height)) uml_cls = UMLClass(cls, Position(next_x, 10), Dimension(default_width, default_height))
# save class xml
xml_cls += uml_cls.as_xml() xml_cls += uml_cls.as_xml()
next_x += width + 10 next_x += round(uml_cls.dimension.width, -1) + 10
return f"""\ return f"""\
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>