Compare commits

..

No commits in common. "placement_improvement" and "master" have entirely different histories.

10 changed files with 129 additions and 130 deletions

35
scripts/compile.sh Normal file
View File

@ -0,0 +1,35 @@
#!/bin/bash
cd src
#pyinstaller --specpath ../dist/cspec \
# --workpath ../dist/cbuild \
# --distpath ../dist/cdist \
# --add-data ../../src/py_to_uxf/appsettings.json:. \
# --hidden-import pynput.keyboard._xorg \
# --hidden-import pynput.mouse._xorg \
# --hidden-import pyfiglet.fonts \
# --collect-data pyfiglet \
# -n py_to_uxf \
# -y \
# py_to_uxf/main.py
build=../dist/cbuild/
python -m nuitka \
--follow-imports \
--include-package=pkg_resources \
--include-package=pynput.keyboard._xorg \
--include-package=pynput.mouse._xorg \
--include-package=Xlib \
--output-dir=$build \
py_to_uxf/main.py
file="py_to_uxf/appsettings.json"
if [ -f "$file" ]; then
if [ -d "$build/main.dist/" ]; then
build=$build/main.dist/
fi
cp $file $build
fi
cd ../

View File

@ -18,10 +18,13 @@ class Application(ApplicationABC):
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC): def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
ApplicationABC.__init__(self, config, services) ApplicationABC.__init__(self, config, services)
config.parse_console_arguments(self._services)
self._path = config.get_configuration('p') self._path = config.get_configuration('p')
self._file = config.get_configuration('o') self._file = ''
try:
self._file = config.get_configuration('-pAdditionalArguments')[1]
except Exception as e:
Console.error('Expected file')
sys.exit()
self._parser: PythonParserABC = services.get_service(PythonParserABC) self._parser: PythonParserABC = services.get_service(PythonParserABC)
self._umlet_creator: UmletCreatorABC = services.get_service(UmletCreatorABC) self._umlet_creator: UmletCreatorABC = services.get_service(UmletCreatorABC)

View File

@ -2,7 +2,6 @@
"Parser": { "Parser": {
"IgnoreClassNames": [ "IgnoreClassNames": [
"ABC", "ABC",
"ABCMeta",
"Enum" "Enum"
] ]
}, },

View File

@ -16,14 +16,13 @@
"LicenseName": "", "LicenseName": "",
"LicenseDescription": "", "LicenseDescription": "",
"Dependencies": [ "Dependencies": [
"cpl-core==2022.10.0.post7", "sh_cpl-core>=2021.11.0.post3",
"cpl-query==2022.10.0.post2" "sh_cpl-query==2021.11.0.post3"
], ],
"DevDependencies": [ "PythonVersion": ">=3.9.2",
"cpl-cli==2022.10.1" "PythonPath": {
], "linux": ""
"PythonVersion": ">=3.10.4", },
"PythonPath": {},
"Classifiers": [] "Classifiers": []
}, },
"BuildSettings": { "BuildSettings": {

View File

@ -2,7 +2,7 @@ import os
import sys import sys
from cpl_core.application import StartupABC from cpl_core.application import StartupABC
from cpl_core.configuration import ConfigurationABC, ArgumentTypeEnum from cpl_core.configuration import ConfigurationABC, ConsoleArgument
from cpl_core.console import Console from cpl_core.console import Console
from cpl_core.dependency_injection import ServiceProviderABC, ServiceCollectionABC from cpl_core.dependency_injection import ServiceProviderABC, ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironment from cpl_core.environment import ApplicationEnvironment
@ -38,9 +38,10 @@ class Startup(StartupABC):
Console.error(f'Expected config file appsettings.json') Console.error(f'Expected config file appsettings.json')
sys.exit() sys.exit()
configuration.create_console_argument(ArgumentTypeEnum.Variable, '-', 'p', [], ' ') configuration.add_console_argument(ConsoleArgument('-', 'p', [], ' ', False, [
configuration.create_console_argument(ArgumentTypeEnum.Variable, '-', 'o', [], ' ') ConsoleArgument('-', 'o', [], ' ', is_value_token_optional=False)
]))
configuration.add_console_arguments(error=False)
return configuration return configuration
def configure_services(self, services: ServiceCollectionABC, environment: ApplicationEnvironment) -> ServiceProviderABC: def configure_services(self, services: ServiceCollectionABC, environment: ApplicationEnvironment) -> ServiceProviderABC:

View File

@ -6,7 +6,7 @@ class ClassImplementation:
def __init__(self, base: PythonClass, subclass: PythonClass): def __init__(self, base: PythonClass, subclass: PythonClass):
self._base = base self._base = base
self._subclass = subclass self._subclass = subclass
self._is_first = True self.is_first = True
@property @property
def base(self) -> PythonClass: def base(self) -> PythonClass:
@ -16,16 +16,8 @@ class ClassImplementation:
def subclass(self) -> PythonClass: def subclass(self) -> PythonClass:
return self._subclass return self._subclass
@property
def is_first(self) -> bool:
return self._is_first
@is_first.setter
def is_first(self, value: bool):
self._is_first = value
def __str__(self): def __str__(self):
return f'<{type(self).__name__} {self._is_first} {self._subclass.name} -> {self._base.name}>' return f'<{type(self).__name__} {self.is_first} {self._subclass.name} -> {self._base.name}>'
def __repr__(self): def __repr__(self):
return f'<{type(self).__name__} {self._is_first} {self._subclass.name} -> {self._base.name}>' return f'<{type(self).__name__} {self.is_first} {self._subclass.name} -> {self._base.name}>'

View File

@ -1,4 +1,7 @@
from cpl_query.extension import List import math
import textwrap
from cpl_core.console import Console
from py_to_uxf_core.model.dimension import Dimension from py_to_uxf_core.model.dimension import Dimension
from py_to_uxf_core.model.position import Position from py_to_uxf_core.model.position import Position
@ -17,9 +20,6 @@ class UMLClass:
self._position = pos self._position = pos
self._dimension = dim self._dimension = dim
self._base_classes = List(type(self))
self._sub_classes = List(type(self))
self._attributes = '' self._attributes = ''
self._functions = '' self._functions = ''
@ -43,22 +43,6 @@ class UMLClass:
def dimension(self, value: int): def dimension(self, value: int):
self._dimension = value self._dimension = value
@property
def base_classes(self) -> List['UMLClass']:
return self._base_classes
@base_classes.setter
def base_classes(self, value: List['UMLClass']):
self._base_classes = value
@property
def sub_classes(self) -> List['UMLClass']:
return self._sub_classes
@sub_classes.setter
def sub_classes(self, value: List['UMLClass']):
self._sub_classes = value
def __str__(self): def __str__(self):
return f'<{type(self).__name__} {self._cls}: {self._position} {self._dimension}>' return f'<{type(self).__name__} {self._cls}: {self._position} {self._dimension}>'

View File

@ -16,12 +16,9 @@
"LicenseName": "", "LicenseName": "",
"LicenseDescription": "", "LicenseDescription": "",
"Dependencies": [ "Dependencies": [
"cpl-core==2022.10.0.post2" "sh_cpl-core>=2021.11.0.post3"
], ],
"DevDependencies": [ "PythonVersion": ">=3.9.2",
"cpl-cli==2022.10.1"
],
"PythonVersion": ">=3.10.4",
"PythonPath": { "PythonPath": {
"linux": "" "linux": ""
}, },

View File

@ -16,9 +16,11 @@ class ImplementationScannerService(ImplementationScannerABC):
ImplementationScannerABC.__init__(self) ImplementationScannerABC.__init__(self)
self._parser_settings: ParserSettings = configuration.get_configuration(ParserSettings) self._parser_settings: ParserSettings = configuration.get_configuration(ParserSettings)
@staticmethod def _get_implementation(self, base_name: str, sub_class: PythonClass, classes: List[PythonClass]) -> Optional[ClassImplementation]:
def _get_implementation(base_name: str, sub_class: PythonClass, classes: List[PythonClass]) -> Optional[ClassImplementation]:
base: Optional[PythonClass] = classes.where(lambda c: c.name == base_name).first_or_default() base: Optional[PythonClass] = classes.where(lambda c: c.name == base_name).first_or_default()
if base_name in self._parser_settings.ignore_class_names:
return None
if base is None: if base is None:
new_base = PythonClass(base_name) new_base = PythonClass(base_name)
classes.append(new_base) classes.append(new_base)
@ -42,20 +44,13 @@ class ImplementationScannerService(ImplementationScannerABC):
implementations.append(implementation) implementations.append(implementation)
continue continue
split_base_name = base_name.split(',') for name in base_name.split(','):
found_first_base = False
for i in range(len(split_base_name)):
name = split_base_name[i]
if '=' in name: if '=' in name:
continue continue
implementation = self._get_implementation(name, sub, classes) implementation = self._get_implementation(name, sub, classes)
if implementation is not None: if implementation is not None:
if i > 0 and found_first_base:
implementation.is_first = False implementation.is_first = False
if not found_first_base:
found_first_base = name not in self._parser_settings.ignore_class_names
implementations.append(implementation) implementations.append(implementation)
return implementations return implementations
return None return None

View File

@ -1,11 +1,8 @@
import sys
from cpl_core.configuration import ConfigurationABC from cpl_core.configuration import ConfigurationABC
from cpl_core.console import Console from cpl_core.console import Console
from cpl_query.extension import List from cpl_query.extension import List
from py_to_uxf_core.abc.umlet_creator_abc import UmletCreatorABC from py_to_uxf_core.abc.umlet_creator_abc import UmletCreatorABC
from py_to_uxf_core.configuration.parser_settings import ParserSettings
from py_to_uxf_core.configuration.uml_creator_settings import UMLCreatorSettings from py_to_uxf_core.configuration.uml_creator_settings import UMLCreatorSettings
from py_to_uxf_core.model.class_implementation import ClassImplementation 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.dimension import Dimension
@ -13,8 +10,6 @@ from py_to_uxf_core.model.position import Position
from py_to_uxf_core.model.python_class import PythonClass from py_to_uxf_core.model.python_class import PythonClass
from py_to_uxf_core.model.uml_class import UMLClass from py_to_uxf_core.model.uml_class import UMLClass
OrderedImplementation = dict[ClassImplementation, List]
class UmletCreatorService(UmletCreatorABC): class UmletCreatorService(UmletCreatorABC):
@ -22,58 +17,74 @@ class UmletCreatorService(UmletCreatorABC):
UmletCreatorABC.__init__(self) UmletCreatorABC.__init__(self)
self._settings: UMLCreatorSettings = config.get_configuration(UMLCreatorSettings) self._settings: UMLCreatorSettings = config.get_configuration(UMLCreatorSettings)
self._parser_settings: ParserSettings = config.get_configuration(ParserSettings)
self._moved_classes: list[UMLClass] = [] self._moved_classes: list[UMLClass] = []
self._padding = 30 self._padding = 30
@staticmethod def _sort_by_implementation(self, uml_classes: List[UMLClass], implementations: List[ClassImplementation]):
def _resolve_implementations(classes: List[UMLClass], implementations: List[ClassImplementation]) -> List[UMLClass]: base_sub_map: dict[UMLClass, list[UMLClass]] = {}
new_classes = List(UMLClass)
sub_classes = List(UMLClass)
for implementation in implementations: for implementation in implementations:
implementation: ClassImplementation = implementation sub_class: UMLClass = uml_classes.where(lambda u: u.cls == implementation.subclass).first()
base: UMLClass = classes.where(lambda c: c.cls == implementation.base).first() base_class: UMLClass = uml_classes.where(lambda u: u.cls == implementation.base).first()
sub: UMLClass = classes.where(lambda c: c.cls == implementation.subclass).first()
base.sub_classes.append(sub) if base_class not in base_sub_map:
sub.base_classes.append(base) base_sub_map[base_class] = []
if base not in new_classes:
new_classes.append(base)
sub_classes.append(sub)
for cls in classes: if sub_class in base_sub_map[base_class]:
if cls not in new_classes and cls not in sub_classes: continue
new_classes.append(cls) base_sub_map[base_class].append(sub_class)
return new_classes last_base = Position(self._padding, self._padding)
highest_y = self._padding
def _move_classes(self, classes: List[UMLClass], moved_classes: List[UMLClass], next_pos: Position, highest_y: int): for base in base_sub_map:
for cls in classes: subclasses = base_sub_map[base]
if cls.cls.name in self._parser_settings.ignore_class_names:
base.position.x = last_base.x
base.position.y = last_base.y
last_sub = Position(base.position.x, base.position.y + base.dimension.height)
for sub_class in subclasses:
if sub_class not in self._moved_classes:
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
if sub_class.position.y + sub_class.dimension.height > highest_y:
highest_y = sub_class.position.y + sub_class.dimension.height
self._moved_classes.append(sub_class)
last_base.x = last_sub.x
if last_base.x <= self._settings.max_pixel_x:
last_base.x += self._padding * 2
else:
last_base.x = self._padding
last_base.y = highest_y + self._padding * 2
self._moved_classes.append(base)
for cls in uml_classes:
if cls in self._moved_classes:
continue continue
if cls not in moved_classes: new_x = last_base.x
Console.write_line(f'MOVE {cls.cls.name} to {next_pos} - {cls.dimension}') if new_x <= self._settings.max_pixel_x:
cls.position.x = new_x
if next_pos.x > self._settings.max_pixel_x: last_base.x = new_x + cls.dimension.width + self._padding
next_pos.x = self._padding cls.position.y = last_base.y
next_pos.y = highest_y + self._padding * 2 else:
cls.position.x = self._padding
cls.position.x = next_pos.x cls.position.y = highest_y + self._padding * 2
cls.position.y = next_pos.y last_base = cls.position
next_pos.x = cls.position.x + cls.dimension.width + self._padding
if cls.position.y + cls.dimension.height > highest_y: if cls.position.y + cls.dimension.height > highest_y:
highest_y = cls.position.y + cls.dimension.height highest_y = cls.position.y + cls.dimension.height
moved_classes.append(cls)
if cls.sub_classes.count() > 0:
next_pos.x = cls.position.x
next_pos.y = cls.position.y + cls.dimension.height + self._padding
self._move_classes(cls.sub_classes, moved_classes, next_pos, highest_y)
def generate_xml(self, classes: List[PythonClass], implementations: List[ClassImplementation]) -> str: def generate_xml(self, classes: List[PythonClass], implementations: List[ClassImplementation]) -> str:
uml_classes = List(UMLClass) uml_classes = List(UMLClass)
xml_classes = '' xml_classes = ''
@ -85,33 +96,16 @@ class UmletCreatorService(UmletCreatorABC):
uml_cls.create_xml_body() uml_cls.create_xml_body()
uml_classes.append(uml_cls) uml_classes.append(uml_cls)
sorted_classes = self._resolve_implementations(uml_classes, implementations) self._sort_by_implementation(uml_classes, implementations)
moved_classes = List(UMLClass) for implementation in implementations:
self._move_classes(sorted_classes, moved_classes, Position(self._padding, self._padding), 0) 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 moved_classes: for uml_cls in uml_classes:
# save class xml # save class xml
if uml_cls.cls.name in self._parser_settings.ignore_class_names:
continue
xml_classes += uml_cls.as_xml() xml_classes += uml_cls.as_xml()
# self._sort_by_implementation(uml_classes, implementations)
# oi = self._sort_implementations_by_base_order(implementations)
# for uml_cls in self._move_by_implementation(oi, uml_classes):
# save class xml
# if uml_cls.cls.name in self._parser_settings.ignore_class_names:
# continue
#
# xml_classes += uml_cls.as_xml()
# 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.cls.name in self._parser_settings.ignore_class_names:
# continue
# xml_relations += sub_class.implementation_as_xml(base_class)
return f"""\ return f"""\
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<diagram program="umlet" version="14.1.1"> <diagram program="umlet" version="14.1.1">