PySimultan 0.6.0.8__py3-none-any.whl → 0.6.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- PySimultan2/CHANGELOG.md +7 -0
- PySimultan2/__about__.py +1 -1
- PySimultan2/data_model.py +103 -26
- PySimultan2/default_types.py +25 -3
- PySimultan2/files.py +49 -10
- PySimultan2/multi_values.py +59 -2
- PySimultan2/utils.py +4 -4
- {pysimultan-0.6.0.8.dist-info → pysimultan-0.6.2.dist-info}/METADATA +1 -1
- pysimultan-0.6.2.dist-info/RECORD +76 -0
- pysimultan-0.6.2.dist-info/licenses/LICENSE.txt +29 -0
- PySimultan2/typings/SIMULTAN/Data/Assets/__init__.pyi +0 -616
- PySimultan2/typings/SIMULTAN/Data/Components/__init__.pyi +0 -2538
- PySimultan2/typings/SIMULTAN/Data/FlowNetworks/__init__.pyi +0 -499
- PySimultan2/typings/SIMULTAN/Data/Geometry/__init__.pyi +0 -2219
- PySimultan2/typings/SIMULTAN/Data/MultiValues/__init__.pyi +0 -682
- PySimultan2/typings/SIMULTAN/Data/SimMath/__init__.pyi +0 -1654
- PySimultan2/typings/SIMULTAN/Data/SimNetworks/__init__.pyi +0 -442
- PySimultan2/typings/SIMULTAN/Data/SitePlanner/__init__.pyi +0 -193
- PySimultan2/typings/SIMULTAN/Data/Taxonomy/__init__.pyi +0 -367
- PySimultan2/typings/SIMULTAN/Data/Users/__init__.pyi +0 -116
- PySimultan2/typings/SIMULTAN/Data/ValueMappings/__init__.pyi +0 -212
- PySimultan2/typings/SIMULTAN/Data/__init__.pyi +0 -232
- PySimultan2/typings/SIMULTAN/DataMapping/__init__.pyi +0 -916
- PySimultan2/typings/SIMULTAN/Excel/__init__.pyi +0 -15
- PySimultan2/typings/SIMULTAN/Exceptions/__init__.pyi +0 -268
- PySimultan2/typings/SIMULTAN/Exchange/SimNetworkConnectors/__init__.pyi +0 -32
- PySimultan2/typings/SIMULTAN/Exchange/__init__.pyi +0 -116
- PySimultan2/typings/SIMULTAN/Projects/ManagedFiles/__init__.pyi +0 -433
- PySimultan2/typings/SIMULTAN/Projects/__init__.pyi +0 -435
- PySimultan2/typings/SIMULTAN/Serializer/CODXF/__init__.pyi +0 -103
- PySimultan2/typings/SIMULTAN/Serializer/CSV/__init__.pyi +0 -122
- PySimultan2/typings/SIMULTAN/Serializer/DXF/__init__.pyi +0 -1335
- PySimultan2/typings/SIMULTAN/Serializer/Geometry/__init__.pyi +0 -48
- PySimultan2/typings/SIMULTAN/Serializer/JSON/__init__.pyi +0 -562
- PySimultan2/typings/SIMULTAN/Serializer/METADXF/__init__.pyi +0 -11
- PySimultan2/typings/SIMULTAN/Serializer/PADXF/__init__.pyi +0 -21
- PySimultan2/typings/SIMULTAN/Serializer/PPATH/__init__.pyi +0 -11
- PySimultan2/typings/SIMULTAN/Serializer/Projects/__init__.pyi +0 -112
- PySimultan2/typings/SIMULTAN/Serializer/SIMLINKS/__init__.pyi +0 -5
- PySimultan2/typings/SIMULTAN/Serializer/SPDXF/__init__.pyi +0 -13
- PySimultan2/typings/SIMULTAN/Serializer/SimGeo/__init__.pyi +0 -54
- PySimultan2/typings/SIMULTAN/Serializer/TXDXF/__init__.pyi +0 -46
- PySimultan2/typings/SIMULTAN/Serializer/XMI/__init__.pyi +0 -22
- PySimultan2/typings/SIMULTAN/Serializer/__init__.pyi +0 -32
- PySimultan2/typings/SIMULTAN/Utils/BackgroundWork/__init__.pyi +0 -43
- PySimultan2/typings/SIMULTAN/Utils/Collections/__init__.pyi +0 -216
- PySimultan2/typings/SIMULTAN/Utils/ElevationProvider/__init__.pyi +0 -66
- PySimultan2/typings/SIMULTAN/Utils/Files/__init__.pyi +0 -48
- PySimultan2/typings/SIMULTAN/Utils/Randomize/__init__.pyi +0 -11
- PySimultan2/typings/SIMULTAN/Utils/Streams/__init__.pyi +0 -59
- PySimultan2/typings/SIMULTAN/Utils/UndoRedo/__init__.pyi +0 -133
- PySimultan2/typings/SIMULTAN/Utils/__init__.pyi +0 -570
- PySimultan2/typings/System/Buffers/Binary/__init__.pyi +0 -248
- PySimultan2/typings/System/Buffers/Text/__init__.pyi +0 -91
- PySimultan2/typings/System/Buffers/__init__.pyi +0 -192
- PySimultan2/typings/System/CodeDom/Compiler/__init__.pyi +0 -137
- PySimultan2/typings/System/Collections/Concurrent/__init__.pyi +0 -47
- PySimultan2/typings/System/Collections/Generic/__init__.pyi +0 -1293
- PySimultan2/typings/System/Collections/ObjectModel/__init__.pyi +0 -166
- PySimultan2/typings/System/Collections/Specialized/__init__.pyi +0 -82
- PySimultan2/typings/System/Collections/__init__.pyi +0 -403
- PySimultan2/typings/System/ComponentModel/__init__.pyi +0 -582
- PySimultan2/typings/System/Configuration/Assemblies/__init__.pyi +0 -30
- PySimultan2/typings/System/Diagnostics/CodeAnalysis/__init__.pyi +0 -315
- PySimultan2/typings/System/Diagnostics/Contracts/__init__.pyi +0 -297
- PySimultan2/typings/System/Diagnostics/SymbolStore/__init__.pyi +0 -9
- PySimultan2/typings/System/Diagnostics/Tracing/__init__.pyi +0 -641
- PySimultan2/typings/System/Diagnostics/__init__.pyi +0 -1101
- PySimultan2/typings/System/Globalization/__init__.pyi +0 -1675
- PySimultan2/typings/System/IO/Enumeration/__init__.pyi +0 -125
- PySimultan2/typings/System/IO/__init__.pyi +0 -2747
- PySimultan2/typings/System/Linq/Expressions/__init__.pyi +0 -1815
- PySimultan2/typings/System/Net/__init__.pyi +0 -81
- PySimultan2/typings/System/Numerics/__init__.pyi +0 -2853
- PySimultan2/typings/System/Reflection/Emit/__init__.pyi +0 -1945
- PySimultan2/typings/System/Reflection/Metadata/__init__.pyi +0 -24
- PySimultan2/typings/System/Reflection/__init__.pyi +0 -2724
- PySimultan2/typings/System/Resources/__init__.pyi +0 -205
- PySimultan2/typings/System/Runtime/CompilerServices/__init__.pyi +0 -1926
- PySimultan2/typings/System/Runtime/ConstrainedExecution/__init__.pyi +0 -49
- PySimultan2/typings/System/Runtime/ExceptionServices/__init__.pyi +0 -34
- PySimultan2/typings/System/Runtime/InteropServices/ComTypes/__init__.pyi +0 -758
- PySimultan2/typings/System/Runtime/InteropServices/Marshalling/__init__.pyi +0 -461
- PySimultan2/typings/System/Runtime/InteropServices/ObjectiveC/__init__.pyi +0 -48
- PySimultan2/typings/System/Runtime/InteropServices/__init__.pyi +0 -2632
- PySimultan2/typings/System/Runtime/Intrinsics/Arm/__init__.pyi +0 -4757
- PySimultan2/typings/System/Runtime/Intrinsics/Wasm/__init__.pyi +0 -844
- PySimultan2/typings/System/Runtime/Intrinsics/X86/__init__.pyi +0 -5642
- PySimultan2/typings/System/Runtime/Intrinsics/__init__.pyi +0 -4504
- PySimultan2/typings/System/Runtime/Loader/__init__.pyi +0 -63
- PySimultan2/typings/System/Runtime/Remoting/__init__.pyi +0 -7
- PySimultan2/typings/System/Runtime/Serialization/__init__.pyi +0 -269
- PySimultan2/typings/System/Runtime/Versioning/__init__.pyi +0 -200
- PySimultan2/typings/System/Runtime/__init__.pyi +0 -141
- PySimultan2/typings/System/Security/Cryptography/__init__.pyi +0 -39
- PySimultan2/typings/System/Security/Permissions/__init__.pyi +0 -163
- PySimultan2/typings/System/Security/Principal/__init__.pyi +0 -45
- PySimultan2/typings/System/Security/__init__.pyi +0 -347
- PySimultan2/typings/System/Text/Unicode/__init__.pyi +0 -62
- PySimultan2/typings/System/Text/__init__.pyi +0 -1590
- PySimultan2/typings/System/Threading/Tasks/Sources/__init__.pyi +0 -76
- PySimultan2/typings/System/Threading/Tasks/__init__.pyi +0 -1403
- PySimultan2/typings/System/Threading/__init__.pyi +0 -1788
- PySimultan2/typings/System/Xml/Schema/__init__.pyi +0 -1255
- PySimultan2/typings/System/Xml/Serialization/__init__.pyi +0 -16
- PySimultan2/typings/System/Xml/XPath/__init__.pyi +0 -474
- PySimultan2/typings/System/Xml/__init__.pyi +0 -2410
- PySimultan2/typings/System/__init__.pyi +0 -17821
- pysimultan-0.6.0.8.dist-info/RECORD +0 -174
- pysimultan-0.6.0.8.dist-info/licenses/LICENSE.txt +0 -9
- {pysimultan-0.6.0.8.dist-info → pysimultan-0.6.2.dist-info}/WHEEL +0 -0
PySimultan2/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
Version 0.6.2 (28.03.2024)
|
2
|
+
- Added extended functionality to ComponentDictionary
|
3
|
+
- Bugfixing
|
4
|
+
|
5
|
+
Version 0.6.1 (24.02.2024)
|
6
|
+
- Bugfixes in default_types and File/Directory handling
|
7
|
+
|
1
8
|
Version 0.6.0.7 (16.02.2024)
|
2
9
|
- Fixed Asset creation and deletion bugs
|
3
10
|
|
PySimultan2/__about__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
version = '0.6.
|
1
|
+
version = '0.6.2'
|
PySimultan2/data_model.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import atexit
|
2
|
+
import pathlib
|
2
3
|
import os
|
3
4
|
import shutil
|
4
5
|
from uuid import uuid4
|
@@ -528,6 +529,17 @@ class DataModel:
|
|
528
529
|
if isinstance(target_dir, SystemFileInfo):
|
529
530
|
target_dir = target_dir.FullPath
|
530
531
|
|
532
|
+
if target_dir.startswith(self.project.ProjectUnpackFolder.FullPath):
|
533
|
+
return self.project.AddEmptyResource(SystemFileInfo(os.path.join(target_dir, str(filename))))
|
534
|
+
else:
|
535
|
+
return self.project.AddEmptyResource(SystemFileInfo(os.path.join(
|
536
|
+
self.project.ProjectUnpackFolder.FullPath,
|
537
|
+
target_dir,
|
538
|
+
str(filename))
|
539
|
+
)
|
540
|
+
)
|
541
|
+
|
542
|
+
|
531
543
|
return self.project.AddEmptyResource(FileInfo(
|
532
544
|
os.path.join(target_dir, str(filename))
|
533
545
|
)
|
@@ -595,6 +607,9 @@ class DataModel:
|
|
595
607
|
|
596
608
|
if not act_filename.startswith(self.project.ProjectUnpackFolder.FullPath) and target_dir is None:
|
597
609
|
target_dir_str = self.project.ProjectUnpackFolder.FullPath
|
610
|
+
elif act_filename.startswith(self.project.ProjectUnpackFolder.FullPath):
|
611
|
+
target_dir_str = ''
|
612
|
+
|
598
613
|
elif target_dir is not None:
|
599
614
|
if isinstance(target_dir, ResourceDirectoryEntry):
|
600
615
|
target_dir_str = target_dir.CurrentFullPath
|
@@ -626,7 +641,7 @@ class DataModel:
|
|
626
641
|
if target_dir is None:
|
627
642
|
resource = self.project.CopyResourceAsContainedFileEntry(filename,
|
628
643
|
self.project.ProjectUnpackFolder,
|
629
|
-
'1')
|
644
|
+
'{0} ({1})')
|
630
645
|
else:
|
631
646
|
if isinstance(target_dir, str):
|
632
647
|
target_dir = DirectoryInfo(target_dir)
|
@@ -685,40 +700,102 @@ class DataModel:
|
|
685
700
|
self.get_file_infos.cache_clear()
|
686
701
|
return success
|
687
702
|
|
703
|
+
def get_existing_resource_directory_entry(self,
|
704
|
+
full_path: str) -> ResourceDirectoryEntry:
|
705
|
+
return self.project_data_manager.AssetManager.GetResource(DirectoryInfo(full_path))
|
706
|
+
|
688
707
|
def create_resource_directory(self,
|
689
708
|
name: str,
|
690
|
-
parent_directory: DirectoryInfo=None,
|
709
|
+
parent_directory: Union[DirectoryInfo, str, pathlib.Path]=None,
|
710
|
+
create_parents: bool = True,
|
691
711
|
collision_name_format: str = '{0} ({1})') -> ResourceEntry:
|
692
712
|
|
693
|
-
# check if directory already exists
|
694
713
|
if parent_directory is None:
|
695
|
-
|
714
|
+
if name.startswith(self.project.ProjectUnpackFolder.FullPath):
|
715
|
+
full_path = name
|
716
|
+
else:
|
717
|
+
full_path = os.path.join(self.project.ProjectUnpackFolder.FullPath, name)
|
696
718
|
else:
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
719
|
+
if isinstance(parent_directory, str):
|
720
|
+
if not parent_directory.startswith(self.project.ProjectUnpackFolder.FullPath):
|
721
|
+
full_path = os.path.join(self.project.ProjectUnpackFolder.FullPath, parent_directory, name)
|
722
|
+
else:
|
723
|
+
full_path = parent_directory
|
724
|
+
elif isinstance(parent_directory, DirectoryInfo):
|
725
|
+
full_path = os.path.join(parent_directory.FullPath, name)
|
726
|
+
elif isinstance(parent_directory, pathlib.Path):
|
727
|
+
full_path = os.path.join(parent_directory, name)
|
728
|
+
|
729
|
+
|
730
|
+
existing_resource_directory = self.get_existing_resource_directory_entry(str(full_path))
|
731
|
+
if existing_resource_directory is not None:
|
732
|
+
return existing_resource_directory
|
733
|
+
|
734
|
+
if str(pathlib.Path(full_path).parents[0]) == self.project.ProjectUnpackFolder.FullPath:
|
735
|
+
# create directory in ProjectUnpackFolder
|
736
|
+
resource_directory_entry = self.get_existing_resource_directory_entry(full_path)
|
737
|
+
if resource_directory_entry is not None:
|
738
|
+
return resource_directory_entry
|
739
|
+
else:
|
740
|
+
new_resource_entry = self.project.CreateResourceDirIn(
|
741
|
+
str(
|
742
|
+
pathlib.Path(full_path).relative_to(pathlib.Path(self.project.ProjectUnpackFolder.FullPath))
|
743
|
+
),
|
744
|
+
None,
|
745
|
+
collision_name_format)
|
746
|
+
if new_resource_entry not in self.project_data_manager.AssetManager.Resources:
|
747
|
+
self.project_data_manager.AssetManager.AddResourceEntry(new_resource_entry)
|
748
|
+
return new_resource_entry
|
749
|
+
else:
|
750
|
+
if parent_directory is None:
|
751
|
+
parent = None
|
752
|
+
for i in reversed(range(pathlib.Path(full_path).parents.__len__())):
|
753
|
+
parent = pathlib.Path(full_path).parents[i]
|
754
|
+
if str(parent) in self.project.ProjectUnpackFolder.FullPath:
|
755
|
+
continue
|
756
|
+
if not os.path.exists(parent):
|
757
|
+
if create_parents:
|
758
|
+
parent_directory = self.create_resource_directory(parent.name,
|
759
|
+
parent_directory=parent.parents[0],
|
760
|
+
create_parents=False)
|
761
|
+
else:
|
762
|
+
raise FileNotFoundError(f'Parent directory {parent} does not exist')
|
763
|
+
else:
|
764
|
+
parent_directory = self.get_existing_resource_directory_entry(str(parent))
|
714
765
|
|
715
766
|
if parent_directory is None:
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
767
|
+
new_resource_entry = self.project.CreateResourceDirIn(
|
768
|
+
str(
|
769
|
+
pathlib.Path(full_path).relative_to(pathlib.Path(self.project.ProjectUnpackFolder.FullPath))
|
770
|
+
),
|
771
|
+
parent_directory,
|
772
|
+
collision_name_format)
|
773
|
+
self.project_data_manager.AssetManager.AddResourceEntry(new_resource_entry)
|
774
|
+
return new_resource_entry
|
775
|
+
else:
|
776
|
+
if isinstance(parent_directory, pathlib.Path):
|
777
|
+
target = str(pathlib.Path(full_path).relative_to(parent_directory))
|
778
|
+
parent_directory_target = DirectoryInfo(str(parent_directory))
|
779
|
+
elif isinstance(parent_directory, str):
|
780
|
+
target = str(pathlib.Path(full_path).relative_to(pathlib.Path(parent_directory)))
|
781
|
+
parent_directory_target = DirectoryInfo(parent_directory)
|
782
|
+
elif isinstance(parent_directory, DirectoryInfo):
|
783
|
+
target = str(pathlib.Path(full_path).relative_to(pathlib.Path(parent_directory.CurrentFullPath)))
|
784
|
+
parent_directory_target = parent_directory
|
785
|
+
elif isinstance(parent_directory, ResourceDirectoryEntry):
|
786
|
+
target = str(pathlib.Path(full_path).relative_to(pathlib.Path(parent_directory.CurrentFullPath)))
|
787
|
+
parent_directory_target = DirectoryInfo(parent_directory.CurrentFullPath)
|
721
788
|
|
789
|
+
else:
|
790
|
+
raise ValueError(f'Parent directory {parent_directory} not found')
|
791
|
+
|
792
|
+
new_resource_entry = self.project.CreateResourceDirIn(
|
793
|
+
target,
|
794
|
+
parent_directory_target,
|
795
|
+
collision_name_format)
|
796
|
+
if new_resource_entry.manager is None:
|
797
|
+
self.project_data_manager.AssetManager.AddResourceEntry(new_resource_entry)
|
798
|
+
return new_resource_entry
|
722
799
|
|
723
800
|
def add_table(self, table: SimMultiValueBigTable):
|
724
801
|
self.project_data_manager.ValueManager.Add(table)
|
PySimultan2/default_types.py
CHANGED
@@ -520,11 +520,13 @@ class ComponentDictionary(SimultanObject):
|
|
520
520
|
|
521
521
|
def __setitem__(self, key, value):
|
522
522
|
|
523
|
-
if key in self._dict:
|
524
|
-
|
523
|
+
# if key in self._dict:
|
524
|
+
# del self._dict[key]
|
525
525
|
|
526
526
|
if key in self._taxonomy_map.content_dict.keys():
|
527
527
|
content = self._taxonomy_map.content_dict[key]
|
528
|
+
elif '__dict_key__' + key in self._taxonomy_map.content_dict.keys():
|
529
|
+
content = self._taxonomy_map.content_dict['__dict_key__' + key]
|
528
530
|
else:
|
529
531
|
content = Content(text_or_key=f'__dict_key__{key}',
|
530
532
|
property_name=key,
|
@@ -574,7 +576,10 @@ class ComponentDictionary(SimultanObject):
|
|
574
576
|
|
575
577
|
def __delitem__(self, key):
|
576
578
|
self[key] = None
|
577
|
-
|
579
|
+
try:
|
580
|
+
del self._dict[key]
|
581
|
+
except KeyError:
|
582
|
+
pass
|
578
583
|
|
579
584
|
def items(self):
|
580
585
|
if self._dict is None or not self._dict:
|
@@ -686,5 +691,22 @@ class ComponentDictionary(SimultanObject):
|
|
686
691
|
}
|
687
692
|
}
|
688
693
|
|
694
|
+
def get_raw_attr(self, attr: Optional[str] = None, text_or_key: Optional[str] = None):
|
695
|
+
if attr is not None:
|
696
|
+
content = self._taxonomy_map.get_content_by_property_name(attr)
|
697
|
+
if content is None:
|
698
|
+
content = Content(text_or_key=f'__dict_key__{attr}',
|
699
|
+
property_name=attr,
|
700
|
+
type=None,
|
701
|
+
unit=None,
|
702
|
+
documentation=f'Property {attr} in ComponentDictionary',
|
703
|
+
component_policy=self.component_policy)
|
704
|
+
self._taxonomy_map.add_content(content)
|
705
|
+
|
706
|
+
return get_component_taxonomy_entry(self._wrapped_obj, content.text_or_key)
|
707
|
+
|
708
|
+
if text_or_key is not None:
|
709
|
+
return get_component_taxonomy_entry(self._wrapped_obj, text_or_key)
|
710
|
+
|
689
711
|
|
690
712
|
component_dict_map = ComponentDictionary._taxonomy_map
|
PySimultan2/files.py
CHANGED
@@ -301,12 +301,19 @@ class FileInfo(object, metaclass=MetaMock):
|
|
301
301
|
data_model = kwargs.get('data_model', config.get_default_data_model())
|
302
302
|
|
303
303
|
if target_dir is not None:
|
304
|
-
|
304
|
+
if isinstance(target_dir, DirectoryInfo):
|
305
|
+
full_path = os.path.join(target_dir.full_path, filename)
|
306
|
+
elif isinstance(target_dir, ResourceDirectoryEntry):
|
307
|
+
full_path = os.path.join(target_dir.CurrentFullPath, filename)
|
308
|
+
elif isinstance(target_dir, str):
|
309
|
+
full_path = os.path.join(target_dir, filename)
|
310
|
+
else:
|
311
|
+
raise ValueError(f'Unsupported target_dir format')
|
305
312
|
else:
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
313
|
+
if not filename.startswith(str(data_model.project.ProjectUnpackFolder)):
|
314
|
+
full_path = os.path.join(str(data_model.project.ProjectUnpackFolder), filename)
|
315
|
+
else:
|
316
|
+
full_path = filename
|
310
317
|
|
311
318
|
if os.path.isfile(full_path):
|
312
319
|
# check if resource entry exists
|
@@ -609,6 +616,15 @@ class DirectoryInfo(object, metaclass=DirectoryInfoMetaMock):
|
|
609
616
|
add_sub_directories: bool = True,
|
610
617
|
*args,
|
611
618
|
**kwargs) -> DirectoryInfo:
|
619
|
+
"""
|
620
|
+
Create a directory info object from an existing directory.
|
621
|
+
:param directory_path: the path to the directory
|
622
|
+
:param add_files:
|
623
|
+
:param add_sub_directories:
|
624
|
+
:param args:
|
625
|
+
:param kwargs:
|
626
|
+
:return:
|
627
|
+
"""
|
612
628
|
|
613
629
|
data_model = kwargs.get('data_model', config.get_default_data_model())
|
614
630
|
|
@@ -642,6 +658,33 @@ class DirectoryInfo(object, metaclass=DirectoryInfoMetaMock):
|
|
642
658
|
|
643
659
|
return directory_info
|
644
660
|
|
661
|
+
@classmethod
|
662
|
+
def from_existing_directory_copy(cls,
|
663
|
+
directory_path: str,
|
664
|
+
destination_path: str,
|
665
|
+
add_files: bool = True,
|
666
|
+
add_sub_directories: bool = True,
|
667
|
+
*args,
|
668
|
+
**kwargs) -> DirectoryInfo:
|
669
|
+
|
670
|
+
"""
|
671
|
+
Create a directory info object from an existing directory which is copied to a directory in the project.
|
672
|
+
:param directory_path:
|
673
|
+
:param destination_path: the path to the directory (in the project)
|
674
|
+
:param add_files:
|
675
|
+
:param add_sub_directories:
|
676
|
+
:param args:
|
677
|
+
:param kwargs:
|
678
|
+
:return:
|
679
|
+
"""
|
680
|
+
|
681
|
+
shutil.copytree(directory_path, destination_path)
|
682
|
+
return cls.from_existing_directory(destination_path,
|
683
|
+
add_files=add_files,
|
684
|
+
add_sub_directories=add_sub_directories,
|
685
|
+
*args,
|
686
|
+
**kwargs)
|
687
|
+
|
645
688
|
def __init__(self,
|
646
689
|
path: Optional[str] = None,
|
647
690
|
helper_file: Optional[FileInfo] = None,
|
@@ -776,11 +819,7 @@ class DirectoryInfo(object, metaclass=DirectoryInfoMetaMock):
|
|
776
819
|
|
777
820
|
def get_file(self, filename: str) -> Optional[FileInfo]:
|
778
821
|
resource = next((x for x in self.files if x.resource_entry.Name == filename), None)
|
779
|
-
|
780
|
-
return FileInfo(resource_entry=resource,
|
781
|
-
data_model=self.data_model)
|
782
|
-
else:
|
783
|
-
return None
|
822
|
+
return resource
|
784
823
|
|
785
824
|
def add_file(self,
|
786
825
|
filename: str,
|
PySimultan2/multi_values.py
CHANGED
@@ -129,7 +129,9 @@ def simultan_multi_value_big_table_to_pandas(field: SimMultiValueBigTable) -> pd
|
|
129
129
|
return df
|
130
130
|
|
131
131
|
|
132
|
-
def simultan_multi_value_field_3d_to_numpy(field: SimMultiValueField3D,
|
132
|
+
def simultan_multi_value_field_3d_to_numpy(field: SimMultiValueField3D,
|
133
|
+
assert_ordered=False,
|
134
|
+
squeeze: bool = True) -> np.ndarray:
|
133
135
|
"""
|
134
136
|
Convert a SimMultiValueField3D object to a numpy array.
|
135
137
|
:param field:
|
@@ -150,7 +152,10 @@ def simultan_multi_value_field_3d_to_numpy(field: SimMultiValueField3D, assert_o
|
|
150
152
|
for comb in product(range(len(field.XAxis)), range(len(field.YAxis)), range(len(field.ZAxis)), repeat=1):
|
151
153
|
array[comb[0], comb[1], comb[2]] = field[comb[0], comb[1], comb[2]]
|
152
154
|
|
153
|
-
|
155
|
+
if squeeze:
|
156
|
+
return np.squeeze(array)
|
157
|
+
else:
|
158
|
+
return array
|
154
159
|
|
155
160
|
|
156
161
|
def add_field_to_data_model(field: SimMultiValueField3D, data_model: 'SimultanObject') -> SimMultiValueField3D:
|
@@ -288,3 +293,55 @@ def create_field_parameter(value: Union[SimMultiValueField3D, np.ndarray, DataFr
|
|
288
293
|
)
|
289
294
|
|
290
295
|
return param
|
296
|
+
|
297
|
+
|
298
|
+
def add_row(field: SimMultiValueField3D,
|
299
|
+
dim: int = 0):
|
300
|
+
|
301
|
+
array = simultan_multi_value_field_3d_to_numpy(field, squeeze=False)
|
302
|
+
|
303
|
+
new_shape = list(array.shape)
|
304
|
+
if new_shape.__len__() - 1 < dim:
|
305
|
+
while new_shape.__len__() - 1 < dim:
|
306
|
+
new_shape.append(1)
|
307
|
+
new_shape[dim] += 1
|
308
|
+
|
309
|
+
# data = NetList[Double](Array[Double](array.astype(float).flatten(order='F')))
|
310
|
+
|
311
|
+
while field.XAxis.Count < new_shape[0]:
|
312
|
+
field.XAxis.Add(field.XAxis.Count+1)
|
313
|
+
while field.YAxis.Count < new_shape[1]:
|
314
|
+
field.YAxis.Add(field.YAxis.Count+1)
|
315
|
+
while field.ZAxis.Count < new_shape[2]:
|
316
|
+
field.ZAxis.Add(field.ZAxis.Count+1)
|
317
|
+
|
318
|
+
resized_array = np.resize(array, new_shape)
|
319
|
+
|
320
|
+
for i, j, k in zip(range(new_shape[0]), range(new_shape[1]), range(new_shape[2])):
|
321
|
+
field[i, j, k] = resized_array[i, j, k]
|
322
|
+
|
323
|
+
return field
|
324
|
+
|
325
|
+
|
326
|
+
def resize(field: SimMultiValueField3D,
|
327
|
+
new_shape: list[int]) -> SimMultiValueField3D:
|
328
|
+
"""
|
329
|
+
Resize a SimMultiValueField3D object to a new shape.
|
330
|
+
:param field: The SimMultiValueField3D object
|
331
|
+
:param new_shape: The new shape of the field, e.g. [10, 20, 30]
|
332
|
+
:return: SimMultiValueField3D
|
333
|
+
"""
|
334
|
+
array = simultan_multi_value_field_3d_to_numpy(field, squeeze=False)
|
335
|
+
resized_array = np.resize(array, new_shape)
|
336
|
+
|
337
|
+
while field.XAxis.Count < resized_array.shape[0]:
|
338
|
+
field.XAxis.Add(field.XAxis.Count+1)
|
339
|
+
while field.YAxis.Count < resized_array.shape[1]:
|
340
|
+
field.YAxis.Add(field.YAxis.Count+1)
|
341
|
+
while field.ZAxis.Count < resized_array.shape[2]:
|
342
|
+
field.ZAxis.Add(field.ZAxis.Count+1)
|
343
|
+
|
344
|
+
for i, j, k in zip(range(new_shape[0]), range(new_shape[1]), range(new_shape[2])):
|
345
|
+
field[i, j, k] = resized_array[i, j, k]
|
346
|
+
|
347
|
+
return field
|
PySimultan2/utils.py
CHANGED
@@ -2,11 +2,12 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import sys
|
4
4
|
import traceback
|
5
|
+
import functools
|
6
|
+
import numpy as np
|
7
|
+
import pandas as pd
|
5
8
|
|
6
9
|
from enum import Enum
|
7
10
|
from weakref import WeakSet
|
8
|
-
import numpy as np
|
9
|
-
import pandas as pd
|
10
11
|
from typing import List as TypeList, Union, Optional, Type, Any, TYPE_CHECKING
|
11
12
|
from SIMULTAN.Data import SimId
|
12
13
|
|
@@ -1375,7 +1376,7 @@ def set_property_to_dict(value: dict,
|
|
1375
1376
|
return
|
1376
1377
|
|
1377
1378
|
if component_idx is not None:
|
1378
|
-
|
1379
|
+
sub_component = component._wrapped_obj.Components.Items[component_idx].Component
|
1379
1380
|
|
1380
1381
|
if 'ComponentDictionary' in [x.Target.Key for x in component.Slots]:
|
1381
1382
|
if not hasattr(component, '_object_mapper'):
|
@@ -1395,7 +1396,6 @@ def set_property_to_dict(value: dict,
|
|
1395
1396
|
component_dict.update(value)
|
1396
1397
|
return component_dict
|
1397
1398
|
else:
|
1398
|
-
component._data_model.remove_subcomponent(component)
|
1399
1399
|
component._wrapped_obj.Components.RemoveItem(component_idx)
|
1400
1400
|
component_idx = None
|
1401
1401
|
if ref_component_idx is not None:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: PySimultan
|
3
|
-
Version: 0.6.
|
3
|
+
Version: 0.6.2
|
4
4
|
Project-URL: Documentation, https://github.com/Bühler Maximilian/PySimultan2#readme
|
5
5
|
Project-URL: Issues, https://github.com/Bühler Maximilian/PySimultan2/issues
|
6
6
|
Project-URL: Source, https://github.com/Bühler Maximilian/PySimultan2
|
@@ -0,0 +1,76 @@
|
|
1
|
+
PySimultan2/CHANGELOG.md,sha256=sFTVr_AgFyqf4HPmShaAHKaHcT5A3MjL2s2VVzEdoyM,1340
|
2
|
+
PySimultan2/__about__.py,sha256=Xe2PxzP7QL0-Z7rnEvT_2WabZlh03jB_5zRbC5HcVMc,19
|
3
|
+
PySimultan2/__init__.py,sha256=PGVR1uhY01dF5tHyad-znURUZ_LVB95vsjId2BT0aJM,3245
|
4
|
+
PySimultan2/data_model.py,sha256=5E-AJfnXNJq-Y6srN7DfvCDwkZXqXQBDXTsFlQ2Zh6g,44104
|
5
|
+
PySimultan2/default_types.py,sha256=NnBgf16CElEAWH7tA0qLKqFHZ4_YS00HAJpKHG1CmjQ,30581
|
6
|
+
PySimultan2/files.py,sha256=8JCWtUPS89POsPrmn_kCrHzmiBsLb1iWhw-A9qQLcR8,34716
|
7
|
+
PySimultan2/multi_values.py,sha256=CkmfifA-mHL3Cnm496tGQuUchpmNF7JCulyrhUFAM_I,15638
|
8
|
+
PySimultan2/object_mapper.py,sha256=Vq4LsX5m6J1dsdlIC6V7sXEUGZYpkgPD6kwOdo5dLRI,19888
|
9
|
+
PySimultan2/simultan_object.py,sha256=-3dTlkyoVdrXmSZgozz7fTHJnaxN63utYPXOXTCuEOY,20837
|
10
|
+
PySimultan2/taxonomy_maps.py,sha256=hhgXhRXIPTD2briG_yYZssJ9musOnzs0LC4e8e78e40,10323
|
11
|
+
PySimultan2/type_setter_lookup.py,sha256=PGa5_EtV7SM15w3uxy0fA3LiQ0TaS4Ys0LYR5zs8aNk,3748
|
12
|
+
PySimultan2/utils.py,sha256=FNJzBVqN31oRKak4NNx9xNmKMrROKphgbh6XfQCmlMw,67115
|
13
|
+
PySimultan2/geometry/__init__.py,sha256=nJolTD1i5J8qUkOQa-r3D20aq3Co3sN31Xc0n4wJpJo,248
|
14
|
+
PySimultan2/geometry/geometry_base.py,sha256=TwABfQEsqxAIGLqwvqVXEV-GA5sYGBJSJm7e58QmNzM,24015
|
15
|
+
PySimultan2/geometry/utils.py,sha256=J25YsK8sso_UL7xRusItQZvyjtvxdOsSPelBQYFABhY,8519
|
16
|
+
PySimultan2/resources/AssimpNet.dll,sha256=x8uwMHPRmEH9fDQihfEQLUdblM1u7RP-CCnUjOpXcLo,205312
|
17
|
+
PySimultan2/resources/AvalonDock.dll,sha256=9tCcw7cpaVq0bh1H2sfcxb8EWhySmgujPs89lAqIPZs,500224
|
18
|
+
PySimultan2/resources/BruTile.dll,sha256=sJOMQaku5jxcbL7XEd9pehBopyFEHOZzaLyNZyD6u7U,203264
|
19
|
+
PySimultan2/resources/ClosedXML.dll,sha256=dzkwJaBvIdou7SZSE5lsEART_jlnMMztEZu_jBuHp74,1198592
|
20
|
+
PySimultan2/resources/ComponentBuilder.dll,sha256=38kTs312oyDDfX9z4JJyvlfbb7JxMrCEFe58zYnidcQ,2324992
|
21
|
+
PySimultan2/resources/ComponentBuilder.dll.config,sha256=jP5s_8OMg9085DyGtrLZcguy8etqm4ra21KwDXq1sy0,3032
|
22
|
+
PySimultan2/resources/ComponentBuilder.runtimeconfig.json,sha256=YBIGLYblUb1QapC3Td-VcI9HtfEQ_6rQssb-JbeODyA,458
|
23
|
+
PySimultan2/resources/ComponentBuilder.xml,sha256=-P7kILmeX_v7ZCie38jMrceS5gX53h7q8ukIKNmgoyU,1044233
|
24
|
+
PySimultan2/resources/ControlzEx.dll,sha256=S26xw5MnNiIoyiCmVFbFOmUx1yx_zAyXBdDSOSidZxQ,463160
|
25
|
+
PySimultan2/resources/Cyotek.Drawing.BitmapFont.dll,sha256=JG8HbAq5s796e02jB9QklO2cOyKPtHmhY6vc7KUs0OQ,41800
|
26
|
+
PySimultan2/resources/DocumentFormat.OpenXml.dll,sha256=JjiBI17BpHztipV39dQdXAjhHo6Ic0xRjijJJuBQLLo,6108592
|
27
|
+
PySimultan2/resources/DotSpatial.Projections.dll,sha256=dK4oFLcpUK3i6CZ3qIcX5B0npYd-W4hHJz7RJvM77ho,1541632
|
28
|
+
PySimultan2/resources/ExcelNumberFormat.dll,sha256=18Rbjk4QJ4MeL8jN-UH69R0opm_qz8qqc32nMmLntQc,30720
|
29
|
+
PySimultan2/resources/Fluent.dll,sha256=9b7QKKj8-Ynhe4WqSVfiP82PkxYz_kI_GXYi_KQxrEU,2598400
|
30
|
+
PySimultan2/resources/HelixToolkit.Core.Wpf.dll,sha256=EU2-CBh2SDNjwZEP2pOBJhLUf0lKpOL77dYH4fS3wZY,592384
|
31
|
+
PySimultan2/resources/HelixToolkit.SharpDX.Core.Wpf.dll,sha256=KgLYD_QrOxwu90Zk59KLsquAGthP_B-FW0GyHEkyDk0,613888
|
32
|
+
PySimultan2/resources/HelixToolkit.SharpDX.Core.dll,sha256=iKE8TEEAEX9FCg-6CipTCq79aiR79d6jep0rwMCAwgM,1733120
|
33
|
+
PySimultan2/resources/HelixToolkit.dll,sha256=HjbeIX5qCQOm-2HhJCd7ZjCwGAeVoaAHo3SHV8_B4ak,6656
|
34
|
+
PySimultan2/resources/MathNet.Numerics.dll,sha256=g7QaYI5DhAnWUg0BEk9S9XQJ4il5HA4HTjFazn2fQTQ,1600000
|
35
|
+
PySimultan2/resources/Microsoft.AspNetCore.Authorization.dll,sha256=P7_HRwCqdk3q1MD7UtlAXMN_E8gZRdzme6ESGZsXHNk,49272
|
36
|
+
PySimultan2/resources/Microsoft.AspNetCore.Metadata.dll,sha256=jddQwxkgfjmJ6vlXok-PbfnIquNDs6VHvspFEopiTAY,17016
|
37
|
+
PySimultan2/resources/Microsoft.Extensions.DependencyInjection.Abstractions.dll,sha256=jrAm-30mcWoI54hsUTOr-RMOzHIq-zO8ZuRBVrBvCoo,43656
|
38
|
+
PySimultan2/resources/Microsoft.Extensions.Logging.Abstractions.dll,sha256=7mzUlhcvGxpAVO4h_Tka7P_8IXF9ZsHkGt0fp5aufkU,63096
|
39
|
+
PySimultan2/resources/Microsoft.Extensions.Options.dll,sha256=gcHJrSv1wTqvF9SyWTEKcbiShn4mkgfnm9-tRQr4OqY,60584
|
40
|
+
PySimultan2/resources/Microsoft.Extensions.Primitives.dll,sha256=q4ruoDSCjV_QJY9ZkzV6uxvEvZUVrDbyUjU3SSh5SlE,41616
|
41
|
+
PySimultan2/resources/Microsoft.WindowsAPICodePack.Shell.dll,sha256=VTVjKHwRDUz4rGoy8r3j4IP94wHDQky2NImHsqFhbHk,514048
|
42
|
+
PySimultan2/resources/Microsoft.WindowsAPICodePack.dll,sha256=aB9FK5RWRh1onyayOe0YIuu2Qtqv7hq-HmZQH40Mk4s,103936
|
43
|
+
PySimultan2/resources/Microsoft.Xaml.Behaviors.dll,sha256=j3gq16397qqTMYMGWqegvpOHq85gOOkSRV54UnsErco,145272
|
44
|
+
PySimultan2/resources/Newtonsoft.Json.Bson.dll,sha256=tgbl3XemJCyh8d9xfrU9lQWuVpBDMLl1wI1PLb6MMxk,90624
|
45
|
+
PySimultan2/resources/Newtonsoft.Json.dll,sha256=etsgJYQb32kqVnEuCK_nXhcwxx1keKj-1XWs1DJRpmI,639488
|
46
|
+
PySimultan2/resources/Python.Runtime.dll,sha256=i8TGSK2uHgN4_JwFn8llwTNrhXtqU-l05M8-CdMIy7Q,438784
|
47
|
+
PySimultan2/resources/SIMULTAN.Lang.dll,sha256=Z_vMisLt6CHABBO_FYcA9utI8n10kSqXy7tE5iFa8o0,2587136
|
48
|
+
PySimultan2/resources/SIMULTAN.Lang.xml,sha256=5kUlwuT-UNog9Y2JjjSFdBW8b1s14Nf1B_YyQlrf7Vg,739070
|
49
|
+
PySimultan2/resources/SIMULTAN.UI.dll,sha256=Iya297q6BCfLAHxLnJEoFi6h9-H54Dg0Ps6bbyDyOuQ,1245184
|
50
|
+
PySimultan2/resources/SIMULTAN.UI.xml,sha256=NVUg3RiPzvUJk87fHnVya0BVdL3tECz8gg2GQZHOevs,836095
|
51
|
+
PySimultan2/resources/SIMULTAN.dll,sha256=eNB8MyMrrqNk398r2q39Y3-L5eeymN4eShFFD_OmKcA,1771520
|
52
|
+
PySimultan2/resources/SIMULTAN.xml,sha256=oRopamMm95ByupGN_8VzQmMlHA_AfFS2bFBZ1pPAlY0,1968806
|
53
|
+
PySimultan2/resources/SharpDX.D3DCompiler.dll,sha256=qyDGhqpcMo0zkNAdSdyX9sxWRxGRZq6ce4FIBXvxXSI,57856
|
54
|
+
PySimultan2/resources/SharpDX.DXGI.dll,sha256=GMTsibwN7s_JG8t-ofKaJpDpKIADw2bqAnAzdLS21zI,147968
|
55
|
+
PySimultan2/resources/SharpDX.Direct2D1.dll,sha256=HkRyV1YspAeY05q0E4pkFy3Hrrkd96fr9SzlmVnDekc,495616
|
56
|
+
PySimultan2/resources/SharpDX.Direct3D11.dll,sha256=XBwIVxhA-_S5haBjrGlXEwWWqIg5IKcXT7fFFoJt2rE,282624
|
57
|
+
PySimultan2/resources/SharpDX.Direct3D9.dll,sha256=aXAe2nQzrAAQq6QWudnCRc14aUdw1LtrdUG4O6zkHVU,338944
|
58
|
+
PySimultan2/resources/SharpDX.Mathematics.dll,sha256=ajtUzW3A6l7UwfVpZgpPacGrVHHP24QZmdSyZOHOThI,215552
|
59
|
+
PySimultan2/resources/SharpDX.dll,sha256=UY1FparshMs36D7iz1jFA6tqJf67jEi1MxY0DJZ-hL0,275968
|
60
|
+
PySimultan2/resources/Sprache.Calc.dll,sha256=QyBWSMEL5UXootOCTzYWlAkDgGcseeVgDaw_W7K32zc,22528
|
61
|
+
PySimultan2/resources/Sprache.dll,sha256=4c4dDHP9ztxaBDpgWYBUhKKkzPl5eISQimpThGZl0t8,59904
|
62
|
+
PySimultan2/resources/System.Collections.Immutable.dll,sha256=YkpsmK2tsFnupt75OgF4vW0fedXRXfvGwPKONArInxk,245920
|
63
|
+
PySimultan2/resources/System.Data.OleDb.dll,sha256=-AAzdmyo01QNgD9qnUIewEImplJFVNDGUlqE17qmRYA,124024
|
64
|
+
PySimultan2/resources/System.Net.Http.Formatting.dll,sha256=xQBIin4YpqDObdqwau_5dr8XQEq54hlxV4gBgNtUqbc,179672
|
65
|
+
PySimultan2/resources/System.Reflection.Metadata.dll,sha256=Z_vYHE6IgOvBi3DLxLhfX-v6o01lTfD0B4oW6B8IsyA,475912
|
66
|
+
PySimultan2/resources/System.Reflection.MetadataLoadContext.dll,sha256=P4PBRwAGWL27sNc754Wd28t_koJKBFYJ2FyolrD1xo0,242296
|
67
|
+
PySimultan2/resources/XAMLMarkupExtensions.dll,sha256=Ck7FAAFHqSSwMtGJebfun-88OCEplWxY2YKMwlFaF84,36864
|
68
|
+
PySimultan2/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
69
|
+
PySimultan2/resources/assimp.dll,sha256=HwfDwXqoPDTFRyoQpA3qmgZoUdFtziJkV5fNtktEZEU,6081536
|
70
|
+
PySimultan2/resources/componentmanager.user,sha256=hrzr1US4pqkFnLHXcvPkvrgGd7QvlxaV8mhS6fuikEs,760
|
71
|
+
PySimultan2/resources/defaultsettings.xml,sha256=QCkKmhNMxwSvC-aYUUMhkCkXqRSrkuFt5s4FEI5x1wo,396
|
72
|
+
PySimultan2/resources/setup.bat,sha256=fjvvYfVM6TalS-QTSiKAbAId5nTsk8kGGo06ba-wWaY,32
|
73
|
+
pysimultan-0.6.2.dist-info/METADATA,sha256=-Lz0rtIVZlhR-nrLMcLnWeC06dvcJOmzAd11IeAK8yA,6548
|
74
|
+
pysimultan-0.6.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
75
|
+
pysimultan-0.6.2.dist-info/licenses/LICENSE.txt,sha256=FgR_YZkk7oYz4087xYXfcBCXGuTStFrsOWTH4bbpa3c,1821
|
76
|
+
pysimultan-0.6.2.dist-info/RECORD,,
|
@@ -0,0 +1,29 @@
|
|
1
|
+
Academic GPL License Agreement
|
2
|
+
|
3
|
+
1. PreambleThis software is released under the terms of the GNU General Public License (GPL) with additional restrictions limiting use to academic, research, and educational purposes. It ensures that users have the freedom to study, modify, and share the software while prohibiting commercial exploitation.
|
4
|
+
|
5
|
+
2. Grant of LicenseThis software is licensed under the GPL with the following additional condition: You may use, modify, and distribute this software only for academic, research, and educational purposes. Any commercial use, including but not limited to for-profit applications, services, or product development, is strictly prohibited.
|
6
|
+
|
7
|
+
3. RestrictionsYou may not:
|
8
|
+
|
9
|
+
Use the software for commercial, business, or for-profit purposes.
|
10
|
+
|
11
|
+
Distribute, sublicense, or sell copies of the software for commercial purposes.
|
12
|
+
|
13
|
+
Remove or alter any copyright, trademark, or proprietary notices.
|
14
|
+
|
15
|
+
4. Compliance with GPLAs this software is based on the GPL, you must also:
|
16
|
+
|
17
|
+
Include a copy of this license with any distribution.
|
18
|
+
|
19
|
+
Provide access to the source code when distributing the software.
|
20
|
+
|
21
|
+
Allow modifications and redistribution under these same terms.
|
22
|
+
|
23
|
+
5. Disclaimer of WarrantyThis software is provided "as is" without any warranties, express or implied. The author disclaims any liability for damages arising from its use.
|
24
|
+
|
25
|
+
6. TerminationThis license is effective until terminated. If you violate any terms, your rights under this license will terminate automatically, and you must cease using the software and delete all copies.
|
26
|
+
|
27
|
+
7. Governing LawThis agreement shall be governed by and construed in accordance with the laws of [Your Jurisdiction].
|
28
|
+
|
29
|
+
By using this software, you agree to abide by the terms of this license and the GNU General Public License.
|