├── .gitattributes ├── tests ├── __init__.py ├── pyut │ ├── __init__.py │ ├── ui │ │ ├── __init__.py │ │ ├── tools │ │ │ ├── __init__.py │ │ │ └── TestToolbox.py │ │ ├── eventengine │ │ │ ├── __init__.py │ │ │ ├── TestEventType.py │ │ │ └── TestInspector.py │ │ ├── wxcommands │ │ │ └── __init__.py │ │ ├── TestToolBoxHandler.py │ │ └── TestCurrentDirectoryHandler.py │ ├── dialogs │ │ ├── __init__.py │ │ ├── tips │ │ │ ├── __init__.py │ │ │ └── TestTipHandler.py │ │ └── DialogNamesEnum.py │ ├── general │ │ ├── __init__.py │ │ ├── TestGitHubAdapter.py │ │ └── TestLineSplitter.py │ ├── errorcontroller │ │ ├── __init__.py │ │ └── TestErrorManager.py │ └── preferences │ │ └── __init__.py ├── resources │ ├── __init__.py │ └── testLoggingConfiguration.json ├── unittest.tst ├── ProjectTestBase.py ├── AppTestGriddedDiagramApplication.py └── PreferencesEditorDemo.py ├── pyut ├── enums │ ├── __init__.py │ ├── ResourceTextType.py │ └── DiagramType.py ├── ui │ ├── __init__.py │ ├── dialogs │ │ ├── __init__.py │ │ ├── tips │ │ │ ├── __init__.py │ │ │ └── TipHandler.py │ │ ├── logcontrol │ │ │ ├── __init__.py │ │ │ └── DebugLevel.py │ │ ├── preferences │ │ │ ├── __init__.py │ │ │ └── BasePreferencesPage.py │ │ ├── textdialogs │ │ │ ├── __init__.py │ │ │ ├── DlgEditNote.py │ │ │ └── DlgEditText.py │ │ ├── DlgEditDescription.py │ │ ├── DlgEditParameter.py │ │ ├── Wrappers.py │ │ ├── DlgEditField.py │ │ ├── DlgEditMethodModifiers.py │ │ ├── DlgEditStereotype.py │ │ ├── DlgEditCode.py │ │ ├── BaseEditParamFieldDialog.py │ │ └── DlgEditProjectHistory.py │ ├── main │ │ └── __init__.py │ ├── tools │ │ ├── __init__.py │ │ ├── SharedTypes.py │ │ ├── ToolboxTypes.py │ │ ├── Tool.py │ │ ├── ToolboxOwner.py │ │ └── SharedIdentifiers.py │ ├── eventengine │ │ ├── __init__.py │ │ ├── eventinformation │ │ │ ├── __init__.py │ │ │ ├── MiniProjectInformation.py │ │ │ ├── ActiveProjectInformation.py │ │ │ └── NewProjectDiagramInformation.py │ │ ├── inspector │ │ │ ├── __init__.py │ │ │ ├── EventSender.py │ │ │ ├── EventEngineDiagnostics.py │ │ │ ├── RegisteredListener.py │ │ │ └── Inspector.py │ │ └── IEventEngine.py │ ├── menuhandlers │ │ ├── __init__.py │ │ ├── BaseMenuHandler.py │ │ ├── ToolsMenuHandler.py │ │ ├── PyutFileDropTarget.py │ │ ├── EditMenuHandler.py │ │ └── HelpMenuHandler.py │ ├── umlframes │ │ ├── __init__.py │ │ ├── UmlUseCaseDiagramsFrame.py │ │ ├── UmlFrameShapeHandler.py │ │ ├── UmlClassDiagramFrameMenuHandler.py │ │ └── UmlSequenceDiagramsFrame.py │ ├── wxcommands │ │ ├── __init__.py │ │ ├── Types.py │ │ ├── CommandDeleteOglNote.py │ │ ├── CommandDeleteOglActor.py │ │ ├── CommandDeleteOglText.py │ │ ├── CommandDeleteOglUseCase.py │ │ ├── CommandCreateOglUseCase.py │ │ ├── CommandCreateOglActor.py │ │ ├── CommandCreateOglNote.py │ │ ├── CommandCreateOglLink.py │ │ ├── CommandCreateOglText.py │ │ ├── CommandDeleteOglLink.py │ │ ├── CommandDeleteOglClass.py │ │ ├── BaseWxDeleteCommand.py │ │ └── CommandCreateLollipopInterface.py │ ├── FileHistoryConfiguration.py │ ├── ProjectException.py │ ├── Action.py │ ├── CurrentDirectoryHandler.py │ ├── IPyutDocument.py │ ├── PyutPrintout.py │ ├── Types.py │ ├── ToolBoxHandler.py │ ├── PyutDocument.py │ ├── IPyutProject.py │ └── PluginProjectCreator.py ├── general │ ├── __init__.py │ ├── datatypes │ │ ├── __init__.py │ │ └── ToolBarIconSize.py │ ├── exceptions │ │ ├── __init__.py │ │ ├── UnsupportedOperation.py │ │ ├── InvalidCategoryException.py │ │ ├── PreferencesLocationNotSet.py │ │ ├── UnsupportedXmlFileFormat.py │ │ └── UnsupportedFileTypeException.py │ ├── PyutSystemMetrics.py │ ├── LineSplitter.py │ ├── Version.py │ └── GitHubAdapter.py ├── preferences │ ├── __init__.py │ └── FileHistoryPreference.py ├── resources │ ├── __init__.py │ ├── img │ │ ├── __init__.py │ │ ├── splash │ │ │ ├── __init__.py │ │ │ ├── Splash6.odg │ │ │ ├── Splash6.png │ │ │ └── PyutScreenShotBends.png │ │ ├── Arrow.bmp │ │ ├── Pyut.icns │ │ ├── blank.bmp │ │ ├── pyut.bmp │ │ ├── pyut.ico │ │ ├── ImgPyut.png │ │ ├── TipsLogo.bmp │ │ ├── ToolboxUnknown.bmp │ │ ├── defaultPreferences.png │ │ └── DefaultPreferences.py │ ├── Help.txt │ ├── Kilroy-Pyut.txt │ ├── tips.txt │ └── loggingConfiguration.json ├── errorcontroller │ ├── __init__.py │ ├── PyutException.py │ ├── ErrorViewType.py │ ├── IErrorView.py │ ├── RaiseErrorView.py │ ├── TextErrorView.py │ └── GraphicErrorView.py ├── experimental │ ├── __init__.py │ └── DebugErrorViews.py ├── _version.py ├── __init__.py └── PyutConstants.py ├── archive ├── help │ ├── __init__.py │ ├── up.png │ ├── next.png │ ├── prev.png │ ├── up_g.png │ ├── index.png │ ├── next_g.png │ ├── prev_g.png │ ├── contents.png │ ├── crossref.png │ ├── pics │ │ ├── pyut.png │ │ ├── GSfinal.png │ │ ├── splash.png │ │ ├── toolbar.png │ │ ├── pyut-logo.png │ │ ├── eivd_small.png │ │ ├── pyut_small.png │ │ ├── uml_class1.png │ │ ├── uml_class2.png │ │ ├── uml_class3.png │ │ ├── uml_usecase.png │ │ └── GSclass_editor.png │ ├── main.css │ ├── node2.html │ ├── footnode.html │ └── node7.html ├── i18n │ └── msgfmt.py ├── HISTORY ├── FAQ └── CREDITS ├── developer ├── CommandV2.put ├── PyutDialog.put ├── UmlFrames.put ├── EventEngine.put ├── Pyut Dependency Tree.png ├── Pyut Dependency Tree.xmind ├── FileLoadSequenceDiagram.put └── agpl-license-web-badge-version-2-256x48.png ├── scripts ├── iconset │ ├── PyutLogo-512.png │ └── makeicn.sh ├── pyut.bld ├── query.slg └── ci.sh ├── .gitignore ├── .mypi.ini ├── requirements.txt ├── .circleci └── config.yml ├── setup.py └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/enums/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/ui/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/pyut/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /archive/help/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/general/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/preferences/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/resources/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/ui/main/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/ui/tools/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/pyut/ui/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/resources/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/errorcontroller/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/experimental/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/resources/img/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/tips/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/ui/eventengine/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/ui/menuhandlers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/ui/umlframes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/pyut/dialogs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/pyut/general/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/pyut/ui/tools/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/general/datatypes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/general/exceptions/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/resources/img/splash/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/logcontrol/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/pyut/dialogs/tips/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/pyut/errorcontroller/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/pyut/preferences/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/pyut/ui/eventengine/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/pyut/ui/wxcommands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/preferences/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/textdialogs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyut/_version.py: -------------------------------------------------------------------------------- 1 | __version__: str = '9.8.5' 2 | -------------------------------------------------------------------------------- /pyut/ui/eventengine/eventinformation/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /archive/help/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/up.png -------------------------------------------------------------------------------- /archive/help/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/next.png -------------------------------------------------------------------------------- /archive/help/prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/prev.png -------------------------------------------------------------------------------- /archive/help/up_g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/up_g.png -------------------------------------------------------------------------------- /pyut/errorcontroller/PyutException.py: -------------------------------------------------------------------------------- 1 | 2 | class PyutException(Exception): 3 | pass 4 | -------------------------------------------------------------------------------- /archive/help/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/index.png -------------------------------------------------------------------------------- /archive/help/next_g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/next_g.png -------------------------------------------------------------------------------- /archive/help/prev_g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/prev_g.png -------------------------------------------------------------------------------- /archive/i18n/msgfmt.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/i18n/msgfmt.py -------------------------------------------------------------------------------- /developer/CommandV2.put: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/developer/CommandV2.put -------------------------------------------------------------------------------- /developer/PyutDialog.put: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/developer/PyutDialog.put -------------------------------------------------------------------------------- /developer/UmlFrames.put: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/developer/UmlFrames.put -------------------------------------------------------------------------------- /archive/help/contents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/contents.png -------------------------------------------------------------------------------- /archive/help/crossref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/crossref.png -------------------------------------------------------------------------------- /archive/help/pics/pyut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/pics/pyut.png -------------------------------------------------------------------------------- /developer/EventEngine.put: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/developer/EventEngine.put -------------------------------------------------------------------------------- /archive/help/pics/GSfinal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/pics/GSfinal.png -------------------------------------------------------------------------------- /archive/help/pics/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/pics/splash.png -------------------------------------------------------------------------------- /archive/help/pics/toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/pics/toolbar.png -------------------------------------------------------------------------------- /pyut/general/exceptions/UnsupportedOperation.py: -------------------------------------------------------------------------------- 1 | 2 | class UnsupportedOperation(Exception): 3 | pass 4 | -------------------------------------------------------------------------------- /pyut/resources/img/Arrow.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/pyut/resources/img/Arrow.bmp -------------------------------------------------------------------------------- /pyut/resources/img/Pyut.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/pyut/resources/img/Pyut.icns -------------------------------------------------------------------------------- /pyut/resources/img/blank.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/pyut/resources/img/blank.bmp -------------------------------------------------------------------------------- /pyut/resources/img/pyut.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/pyut/resources/img/pyut.bmp -------------------------------------------------------------------------------- /pyut/resources/img/pyut.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/pyut/resources/img/pyut.ico -------------------------------------------------------------------------------- /archive/help/pics/pyut-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/pics/pyut-logo.png -------------------------------------------------------------------------------- /pyut/resources/img/ImgPyut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/pyut/resources/img/ImgPyut.png -------------------------------------------------------------------------------- /pyut/resources/img/TipsLogo.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/pyut/resources/img/TipsLogo.bmp -------------------------------------------------------------------------------- /archive/help/pics/eivd_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/pics/eivd_small.png -------------------------------------------------------------------------------- /archive/help/pics/pyut_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/pics/pyut_small.png -------------------------------------------------------------------------------- /archive/help/pics/uml_class1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/pics/uml_class1.png -------------------------------------------------------------------------------- /archive/help/pics/uml_class2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/pics/uml_class2.png -------------------------------------------------------------------------------- /archive/help/pics/uml_class3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/pics/uml_class3.png -------------------------------------------------------------------------------- /archive/help/pics/uml_usecase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/pics/uml_usecase.png -------------------------------------------------------------------------------- /developer/Pyut Dependency Tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/developer/Pyut Dependency Tree.png -------------------------------------------------------------------------------- /pyut/general/exceptions/InvalidCategoryException.py: -------------------------------------------------------------------------------- 1 | 2 | class InvalidCategoryException(Exception): 3 | pass 4 | -------------------------------------------------------------------------------- /pyut/general/exceptions/PreferencesLocationNotSet.py: -------------------------------------------------------------------------------- 1 | 2 | class PreferencesLocationNotSet(Exception): 3 | pass 4 | -------------------------------------------------------------------------------- /pyut/general/exceptions/UnsupportedXmlFileFormat.py: -------------------------------------------------------------------------------- 1 | 2 | class UnsupportedXmlFileFormat(Exception): 3 | pass 4 | -------------------------------------------------------------------------------- /scripts/iconset/PyutLogo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/scripts/iconset/PyutLogo-512.png -------------------------------------------------------------------------------- /scripts/pyut.bld: -------------------------------------------------------------------------------- 1 | rm -rf build dist Pyut.egg-info 2 | python -O setup.py py2app --iconfile pyut/resources/img/Pyut.icns -------------------------------------------------------------------------------- /archive/help/pics/GSclass_editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/archive/help/pics/GSclass_editor.png -------------------------------------------------------------------------------- /developer/Pyut Dependency Tree.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/developer/Pyut Dependency Tree.xmind -------------------------------------------------------------------------------- /developer/FileLoadSequenceDiagram.put: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/developer/FileLoadSequenceDiagram.put -------------------------------------------------------------------------------- /pyut/general/exceptions/UnsupportedFileTypeException.py: -------------------------------------------------------------------------------- 1 | 2 | class UnsupportedFileTypeException(Exception): 3 | pass 4 | -------------------------------------------------------------------------------- /pyut/resources/img/ToolboxUnknown.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/pyut/resources/img/ToolboxUnknown.bmp -------------------------------------------------------------------------------- /pyut/resources/img/splash/Splash6.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/pyut/resources/img/splash/Splash6.odg -------------------------------------------------------------------------------- /pyut/resources/img/splash/Splash6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/pyut/resources/img/splash/Splash6.png -------------------------------------------------------------------------------- /pyut/__init__.py: -------------------------------------------------------------------------------- 1 | from pyut._version import __version__ 2 | 3 | 4 | START_STOP_MARKER: str = '-----------------------------------' -------------------------------------------------------------------------------- /pyut/resources/img/defaultPreferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/pyut/resources/img/defaultPreferences.png -------------------------------------------------------------------------------- /pyut/resources/img/splash/PyutScreenShotBends.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/pyut/resources/img/splash/PyutScreenShotBends.png -------------------------------------------------------------------------------- /pyut/ui/eventengine/inspector/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | These classes are necessary since we do not have access to the 3 | underlying wxWidgets tables 4 | """ -------------------------------------------------------------------------------- /developer/agpl-license-web-badge-version-2-256x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasii2011/pyut/HEAD/developer/agpl-license-web-badge-version-2-256x48.png -------------------------------------------------------------------------------- /pyut/ui/tools/SharedTypes.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import NewType 3 | from typing import Dict 4 | 5 | ToolboxIdMap = NewType('ToolboxIdMap', Dict[int, str]) 6 | -------------------------------------------------------------------------------- /scripts/query.slg: -------------------------------------------------------------------------------- 1 | hasii2011/code-ally-basic,codeallybasic 2 | hasii2011/code-ally-advanced,codeallyadvanced 3 | hasii2011/pyutmodelv2 4 | hasii2011/ogl 5 | hasii2011/oglio 6 | hasii2011/pyutplugins 7 | -------------------------------------------------------------------------------- /pyut/ui/FileHistoryConfiguration.py: -------------------------------------------------------------------------------- 1 | from wx import FileConfig 2 | 3 | 4 | class FileHistoryConfiguration(FileConfig): 5 | """ 6 | Just some syntactic sugar of the standard wxPython FileConfig class 7 | """ 8 | pass 9 | -------------------------------------------------------------------------------- /tests/unittest.tst: -------------------------------------------------------------------------------- 1 | # unittest tests.pyut.ui.wxcommands.TestCommandModify 2 | # unittest tests.pyut.uiv2.eventengine.TestEventType 3 | unittest tests.pyut.ui.tools.TestToolbox 4 | unittest tests.pyut.dialogs.tips.TestTipHandler 5 | tests.TestAll -------------------------------------------------------------------------------- /pyut/resources/Help.txt: -------------------------------------------------------------------------------- 1 | 2 | Syntax: pyut [filename] [--version] [--help] file1 file2 ... 3 | 4 | e.g. pyut --version display version number 5 | pyut --help display this help 6 | pyut file1 file2 load files 7 | -------------------------------------------------------------------------------- /pyut/enums/ResourceTextType.py: -------------------------------------------------------------------------------- 1 | 2 | from enum import Enum 3 | 4 | 5 | class ResourceTextType(Enum): 6 | 7 | INTRODUCTION_TEXT_TYPE = 'Kilroy-Pyut.txt' 8 | HELP_TEXT_TYPE = 'Help.txt' 9 | 10 | def __str__(self): 11 | return str(self.name) 12 | -------------------------------------------------------------------------------- /pyut/errorcontroller/ErrorViewType.py: -------------------------------------------------------------------------------- 1 | 2 | from enum import Enum 3 | 4 | 5 | class ErrorViewType(Enum): 6 | 7 | GRAPHIC_ERROR_VIEW = 'GraphicErrorView' 8 | TEXT_ERROR_VIEW = 'TextErrorView' 9 | RAISE_ERROR_VIEW = 'RaiseErrorView' 10 | 11 | def __str__(self): 12 | return str(self.name) 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | __pycache__/ 3 | .DS_Store 4 | /html/ 5 | build/ 6 | dist/ 7 | Pyut.egg-info 8 | /pyenv-3.11.5/ 9 | /pyenv-3.12.7/ 10 | /site/ 11 | /docs/ 12 | /pyut.log 13 | pyutHistory* 14 | *.log 15 | *.dat 16 | *.put copy 17 | /BaseSave.xml 18 | junk*.* 19 | /.mypy_cache 20 | /pyut.app/ 21 | .envrc 22 | .python-version 23 | -------------------------------------------------------------------------------- /pyut/ui/eventengine/eventinformation/MiniProjectInformation.py: -------------------------------------------------------------------------------- 1 | 2 | from dataclasses import dataclass 3 | 4 | 5 | @dataclass 6 | class MiniProjectInformation: 7 | """ 8 | Typical use is by code that wants to update the application title 9 | """ 10 | projectName: str = '' 11 | frameZoom: float = 1.0 12 | projectModified: bool = False 13 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/Types.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Union 3 | 4 | from ogl.OglClass import OglClass 5 | from ogl.OglLink import OglLink 6 | from ogl.OglObject import OglObject 7 | from ogl.OglInterface2 import OglInterface2 8 | 9 | from ogl.sd.OglSDInstance import OglSDInstance 10 | 11 | DoableObjectType = Union[OglObject, OglClass, OglLink, OglInterface2, OglSDInstance] 12 | -------------------------------------------------------------------------------- /pyut/ui/tools/ToolboxTypes.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Dict 3 | from typing import List 4 | from typing import NewType 5 | 6 | from pyut.ui.tools.Tool import Category 7 | from pyut.ui.tools.Tool import Tool 8 | 9 | Tools = NewType('Tools', List[Tool]) 10 | CategoryNames = NewType('CategoryNames', List[Category]) 11 | ToolCategories = NewType('ToolCategories', Dict[Category, Tools]) 12 | -------------------------------------------------------------------------------- /pyut/ui/eventengine/eventinformation/ActiveProjectInformation.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import cast 3 | 4 | from dataclasses import dataclass 5 | 6 | from pyut.ui.umlframes.UmlDiagramsFrame import UmlDiagramsFrame 7 | from pyut.ui.IPyutProject import IPyutProject 8 | 9 | 10 | @dataclass 11 | class ActiveProjectInformation: 12 | """ 13 | Typical use is by the dialogs that edit UML objects 14 | """ 15 | pyutProject: IPyutProject = cast(IPyutProject, None) 16 | umlFrame: UmlDiagramsFrame = cast(UmlDiagramsFrame, None) 17 | -------------------------------------------------------------------------------- /scripts/ci.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | clear 4 | # project_slug, takes the form: // 5 | 6 | projectType='github' 7 | orgName='hasii2011' 8 | repoName='pyut' 9 | 10 | project_slug="${projectType}/${orgName}/${repoName}" 11 | 12 | echo "${project_slug}" 13 | # curl -u ${CIRCLECI_TOKEN}: https://circleci.com/api/v2/me 14 | 15 | curl -u ${CIRCLECI_TOKEN}: -X POST --header "Content-Type: application/json" -d '{ 16 | "branch": "master" 17 | }' https://circleci.com/api/v2/project/${project_slug}/pipeline 18 | -------------------------------------------------------------------------------- /pyut/ui/menuhandlers/BaseMenuHandler.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Optional 3 | 4 | from wx import Menu 5 | from wx import Window 6 | 7 | from pyut.ui.eventengine.IEventEngine import IEventEngine 8 | 9 | 10 | class BaseMenuHandler: 11 | 12 | def __init__(self, menu: Menu, eventEngine: Optional[IEventEngine]): 13 | 14 | self._menu: Menu = menu 15 | self._eventEngine: IEventEngine = eventEngine # type: ignore 16 | self._parent: Window = self._menu.GetWindow() # TODO this does not work at init 17 | -------------------------------------------------------------------------------- /pyut/resources/Kilroy-Pyut.txt: -------------------------------------------------------------------------------- 1 | ... 2 | / \\ 3 | ooO | O O | Ooo 4 | ============================================================================= 5 | ____ __ __ _ _ _____ 6 | | _ \ \ \ / / | | | | |_ _| 7 | | |_) | \ V / | | | | | | 8 | | __/ | | | |_| | | | 9 | |_| |_| \___/ |_| 10 | 11 | 12 | A little UML 1.4+ editor 13 | -------------------------------------------------------------------------------- /pyut/general/datatypes/ToolBarIconSize.py: -------------------------------------------------------------------------------- 1 | 2 | from enum import Enum 3 | 4 | 5 | class ToolBarIconSize(Enum): 6 | 7 | SIZE_32 = '32' 8 | SIZE_16 = '16' 9 | 10 | @classmethod 11 | def deSerialize(cls, value: str) -> 'ToolBarIconSize': 12 | 13 | toolBarIconSize: ToolBarIconSize = ToolBarIconSize.SIZE_32 14 | if value == '16': 15 | toolBarIconSize = ToolBarIconSize.SIZE_16 16 | 17 | return toolBarIconSize 18 | 19 | def __str__(self): 20 | return self.value 21 | 22 | def __repr__(self): 23 | return self.__str__() 24 | -------------------------------------------------------------------------------- /.mypi.ini: -------------------------------------------------------------------------------- 1 | 2 | [mypy] 3 | python_version = 3.11 4 | pretty = True 5 | implicit_optional = True 6 | warn_unused_ignores = True 7 | strict_equality = True 8 | 9 | [mypy-wx.*] 10 | ignore_missing_imports = True 11 | 12 | [mypy-antlr4.*] 13 | ignore_missing_imports = True 14 | 15 | [mypy-orthogonal.*] 16 | ignore_missing_imports = True 17 | 18 | [mypy-xmlschema.*] 19 | ignore_missing_imports = True 20 | 21 | [mypy-networkx.*] 22 | ignore_missing_imports = True 23 | 24 | [mypy-HtmlTestRunner.*] 25 | ignore_missing_imports = True 26 | 27 | [mypy-semantic_version.*] 28 | ignore_missing_imports = True 29 | -------------------------------------------------------------------------------- /pyut/errorcontroller/IErrorView.py: -------------------------------------------------------------------------------- 1 | 2 | from abc import ABC 3 | 4 | from wx import Window 5 | 6 | 7 | class IErrorView(ABC): 8 | """ 9 | Prototypical interface 10 | """ 11 | def __init__(self): 12 | pass 13 | 14 | def displayFatalError(self, msg: str, title: str | None = None, parent: Window | None = None): 15 | pass 16 | 17 | def displayWarning(self, msg: str, title: str | None = None, parent: Window | None = None): 18 | pass 19 | 20 | def displayInformation(self, msg: str, title: str | None = None, parent: Window | None = None): 21 | pass 22 | -------------------------------------------------------------------------------- /tests/ProjectTestBase.py: -------------------------------------------------------------------------------- 1 | 2 | from codeallyadvanced.ui.UnitTestBaseW import UnitTestBaseW 3 | 4 | 5 | class ProjectTestBase(UnitTestBaseW): 6 | 7 | RESOURCES_TEST_CLASSES_PACKAGE_NAME: str = 'tests.resources.testclass' 8 | RESOURCES_TEST_JAVA_CLASSES_PACKAGE_NAME: str = 'tests.resources.testclass.ozzee' 9 | RESOURCES_TEST_DATA_PACKAGE_NAME: str = 'tests.resources.testdata' 10 | RESOURCES_TEST_IMAGES_PACKAGE_NAME: str = 'tests.resources.testimages' 11 | 12 | def setUp(self): 13 | super().setUp() 14 | 15 | def tearDown(self): 16 | super().tearDown() 17 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | wheel==0.44.0 2 | # Don't upgrade past this version as 70.x.x does not work 3 | setuptools==70.1.1 4 | 5 | # the following necessary when building the 6 | # application with py2App 7 | chardet==5.2.0 8 | PyGithub==2.6.1 9 | Deprecated==1.2.14 10 | 11 | py2app==0.28.8 12 | buildlackey==1.8.1 13 | 14 | html-testRunner~=1.2.1 15 | mypy==1.15.0 16 | mypy-extensions==1.0.0 17 | types-Deprecated==1.2.9.20240311 18 | types-cffi==1.16.0.20250307 19 | 20 | wxPython==4.2.2 21 | semantic-version==2.10.0 22 | 23 | codeallybasic==1.10.0 24 | codeallyadvanced==1.4.2 25 | pyutmodelv2==2.2.3 26 | ogl==3.6.7 27 | oglio==2.4.0 28 | pyutplugins==3.2.6 29 | -------------------------------------------------------------------------------- /pyut/ui/eventengine/inspector/EventSender.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Dict 3 | from typing import List 4 | from typing import NewType 5 | 6 | from dataclasses import dataclass 7 | 8 | from pyut.ui.eventengine.EventType import EventType 9 | 10 | SenderName = NewType('SenderName', str) 11 | 12 | 13 | @dataclass 14 | class EventSender: 15 | senderName: SenderName = SenderName('') 16 | callCount: int = 0 17 | 18 | 19 | EventSenders = NewType('EventSenders', List[EventSender]) 20 | EventSendersMap = NewType('EventSendersMap', Dict[EventType, EventSenders]) 21 | 22 | 23 | def createEventSendersFactory() -> EventSendersMap: 24 | return EventSendersMap({}) 25 | -------------------------------------------------------------------------------- /pyut/ui/eventengine/eventinformation/NewProjectDiagramInformation.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Callable 3 | from typing import cast 4 | 5 | from dataclasses import dataclass 6 | 7 | from pyut.enums.DiagramType import DiagramType 8 | from pyut.ui.IPyutDocument import IPyutDocument 9 | from pyut.ui.IPyutProject import IPyutProject 10 | 11 | NewProjectDiagramCallback = Callable[[IPyutDocument], None] 12 | 13 | 14 | @dataclass 15 | class NewProjectDiagramInformation: 16 | pyutProject: IPyutProject = cast(IPyutProject, None) 17 | diagramType: DiagramType = DiagramType.UNKNOWN_DIAGRAM 18 | diagramName: str = '' 19 | callback: NewProjectDiagramCallback = cast(NewProjectDiagramCallback, None) 20 | 21 | -------------------------------------------------------------------------------- /pyut/ui/eventengine/inspector/EventEngineDiagnostics.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from dataclasses import dataclass 4 | from dataclasses import field 5 | 6 | from pyut.ui.eventengine.inspector.EventSender import EventSendersMap 7 | from pyut.ui.eventengine.inspector.EventSender import createEventSendersFactory 8 | 9 | from pyut.ui.eventengine.inspector.RegisteredListener import RegisteredListenerMap 10 | from pyut.ui.eventengine.inspector.RegisteredListener import createRegisteredListenersMapFactory 11 | 12 | 13 | @dataclass 14 | class EventEngineDiagnostics: 15 | registeredListenersMap: RegisteredListenerMap = field(default_factory=createRegisteredListenersMapFactory) 16 | eventSendersMap: EventSendersMap = field(default_factory=createEventSendersFactory) 17 | -------------------------------------------------------------------------------- /pyut/errorcontroller/RaiseErrorView.py: -------------------------------------------------------------------------------- 1 | 2 | from pyut.errorcontroller.IErrorView import IErrorView 3 | 4 | from pyut.errorcontroller.PyutException import PyutException 5 | 6 | 7 | class RaiseErrorView(IErrorView): 8 | """ 9 | This class is an error view which will raise all errors as exceptions 10 | """ 11 | def __init__(self): 12 | super().__init__() 13 | 14 | def displayFatalError(self, msg, title=None, parent=None): 15 | raise PyutException(f"FATAL ERROR: {title} - {msg}") 16 | 17 | def displayWarning(self, msg, title=None, parent=None): 18 | raise PyutException(f"WARNING: {title} - {msg}") 19 | 20 | def displayInformation(self, msg, title=None, parent=None): 21 | raise PyutException(f"INFORMATION: {title} 0 {msg}") 22 | -------------------------------------------------------------------------------- /pyut/resources/tips.txt: -------------------------------------------------------------------------------- 1 | Welcome to PyUt 7.x ! You will find news in this dialog. You can activate/deactivate the startup tip display via the Pyut preferences dialog 2 | Remember, if you do not submit bugs at https://github.com/hasii2011/PyUt/issues, we can not correct them. You can also submit feature requests. 3 | Right-Click on a line to add or remove bends 4 | You can split lines into multi-lines. Select a Line end and press or 5 | You can convert a multiline in a spline by pressing 6 | You can find more tips on the PyUt wiki: https://github.com/hasii2011/PyUt/wiki 7 | You can submit bugs, feature requests and support requests at https://github.com/hasii2011/PyUt/issues 8 | The File->Open menu item allows you to select multiple projects 9 | Left-Click on a UML class and use the experimental Add lollipop type interface -------------------------------------------------------------------------------- /pyut/general/PyutSystemMetrics.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | from wx import DisplaySize 6 | from wx import ScreenDC 7 | from wx import Size 8 | 9 | from codeallybasic.SingletonV3 import SingletonV3 10 | 11 | from pyut.preferences.PyutPreferences import PyutPreferences 12 | 13 | 14 | class PyutSystemMetrics(metaclass=SingletonV3): 15 | """ 16 | Wrapper class for some simple stuff I want 17 | """ 18 | 19 | def __init__(self): 20 | self.logger: Logger = getLogger(__name__) 21 | 22 | @property 23 | def displaySize(self) -> Size: 24 | return DisplaySize() 25 | 26 | @property 27 | def screenResolution(self) -> Size: 28 | return ScreenDC().GetPPI() 29 | 30 | @property 31 | def toolBarIconSize(self) -> str: 32 | return PyutPreferences().toolBarIconSize.value 33 | -------------------------------------------------------------------------------- /pyut/ui/eventengine/IEventEngine.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Callable 3 | 4 | from abc import ABC 5 | from abc import abstractmethod 6 | 7 | from wx import PyEventBinder 8 | 9 | from pyut.ui.eventengine.EventType import EventType 10 | from pyut.ui.eventengine.inspector.EventEngineDiagnostics import EventEngineDiagnostics 11 | 12 | 13 | class IEventEngine(ABC): 14 | """ 15 | Implement an interface using standard Python library. I found zope too abstract 16 | and python interface could not handle subclasses 17 | """ 18 | @abstractmethod 19 | def registerListener(self, pyEventBinder: PyEventBinder, callback: Callable): 20 | pass 21 | 22 | @abstractmethod 23 | def sendEvent(self, eventType: EventType, **kwargs): 24 | pass 25 | 26 | @property 27 | @abstractmethod 28 | def eventEngineDiagnostics(self) -> EventEngineDiagnostics: 29 | pass 30 | -------------------------------------------------------------------------------- /pyut/ui/eventengine/inspector/RegisteredListener.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Dict 3 | from typing import List 4 | from typing import NewType 5 | 6 | from dataclasses import dataclass 7 | 8 | from pyut.ui.eventengine.EventType import EventType 9 | 10 | RegisteredBy = NewType('RegisteredBy', str) 11 | EventHandler = NewType('EventHandler', str) 12 | 13 | 14 | @dataclass 15 | class RegisteredListener: 16 | """ 17 | This data class necessary since we do not have access to the wxPython event table 18 | """ 19 | eventType: EventType = EventType.NOT_SET 20 | eventHandler: EventHandler = EventHandler('') 21 | 22 | 23 | RegisteredListeners = NewType('RegisteredListeners', List[RegisteredListener]) 24 | RegisteredListenerMap = NewType('RegisteredListenerMap', Dict[RegisteredBy, RegisteredListeners]) 25 | 26 | 27 | def createRegisteredListenersMapFactory() -> RegisteredListenerMap: 28 | return RegisteredListenerMap({}) 29 | -------------------------------------------------------------------------------- /pyut/ui/umlframes/UmlUseCaseDiagramsFrame.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | from pyut.ui.eventengine.IEventEngine import IEventEngine 6 | from pyut.ui.umlframes.UmlDiagramsFrame import UmlDiagramsFrame 7 | 8 | 9 | class UmlUseCaseDiagramsFrame(UmlDiagramsFrame): 10 | """ 11 | This is a marker class to prevent use case symbols from appearing on 12 | non use case diagrams 13 | 14 | """ 15 | 16 | UMLUseCaseFrameNextId: int = 0x00FFF # UML Class Diagrams Frame ID 17 | 18 | def __init__(self, parent, eventEngine: IEventEngine): 19 | 20 | self.localLogger: Logger = getLogger(__name__) 21 | super().__init__(parent, eventEngine=eventEngine) 22 | 23 | self._frameId: int = UmlUseCaseDiagramsFrame.UMLUseCaseFrameNextId 24 | 25 | def __repr__(self) -> str: 26 | 27 | debugId: str = f'0x{self._frameId:06X}' 28 | return f'UmlUseCaseDiagramsFrame:[{debugId=}]' 29 | -------------------------------------------------------------------------------- /archive/HISTORY: -------------------------------------------------------------------------------- 1 | (this is pyut's HISTORY file. see https://github.com/hasii2011/PyUt/ for more informations) 2 | This file contain the project history. 3 | 4 | V6.2 20 October -- https://github.com/hasii2011/PyUt/releases/tag/6.2 5 | V6.1.1 12 September -- https://github.com/hasii2011/PyUt/releases/tag/6.1.1 6 | V6.0 26 July 2020 -- https://github.com/hasii2011/PyUt/wiki/ReleaseNotes-6.0 7 | V5.0 9 June 2020 -- Python 3.8.2 / wxPython 4.1.0 8 | V4.0 15 April 2020 -- See the ChangeLog on why big version bump 9 | V1.9 2020 25 January 10 | V1.7 2019 Christmas Release - Lots plugins made to work again; Internal maintainability changes 11 | V1.6 2019 - See https://github.com/hasii2011/PyUt/releases 12 | V1.5 2019 - Python 3 update; Fork to comply with PEP-8, packaging typing, and latest wxPython API 13 | V1.2 - November 2002 - Release with miniOGL and tools plugins 14 | V1.1 - July 2002 - Release with use-case and plugins 15 | V1.0 - February 2002 - First release 16 | -------------------------------------------------------------------------------- /pyut/preferences/FileHistoryPreference.py: -------------------------------------------------------------------------------- 1 | 2 | from enum import Enum 3 | 4 | from wx import FH_PATH_SHOW_ALWAYS 5 | from wx import FH_PATH_SHOW_IF_DIFFERENT 6 | from wx import FH_PATH_SHOW_NEVER 7 | 8 | 9 | class FileHistoryPreference(Enum): 10 | """ 11 | Implemented so we can present easy to read values to developer 12 | """ 13 | SHOW_IF_DIFFERENT = 'If Different' 14 | SHOW_NEVER = 'Never' 15 | SHOW_ALWAYS = 'Always' 16 | 17 | @classmethod 18 | def toWxMenuPathStyle(cls, value: 'FileHistoryPreference') -> int | None: 19 | 20 | match value: 21 | case FileHistoryPreference.SHOW_IF_DIFFERENT: 22 | pathStyle: int = FH_PATH_SHOW_IF_DIFFERENT 23 | case FileHistoryPreference.SHOW_NEVER: 24 | pathStyle = FH_PATH_SHOW_NEVER 25 | case FileHistoryPreference.SHOW_ALWAYS: 26 | pathStyle = FH_PATH_SHOW_ALWAYS 27 | case _: 28 | eMsg: str = f'Unknown enumeration value {value}' 29 | print(eMsg) 30 | assert False, eMsg 31 | 32 | return pathStyle 33 | -------------------------------------------------------------------------------- /pyut/errorcontroller/TextErrorView.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | from pyut.errorcontroller.IErrorView import IErrorView 6 | 7 | 8 | class TextErrorView(IErrorView): 9 | """ 10 | This class is an error view which will display an error as entries 11 | in the Pyut log file. 12 | """ 13 | def __init__(self): 14 | super().__init__() 15 | self.logger: Logger = getLogger(__name__) 16 | 17 | def displayFatalError(self, msg, title=None, parent=None): 18 | 19 | from pyut.errorcontroller.ErrorManager import ErrorManager # Avoid cyclical dependency 20 | 21 | if title is None: 22 | title = 'An error occurred...' 23 | errMsg: str = ErrorManager.getErrorInfo() 24 | 25 | self.logger.error(f"FATAL ERROR: {title} {errMsg} - parent {parent}") 26 | 27 | def displayWarning(self, msg, title=None, parent=None): 28 | self.logger.error(f"WARNING: {title} - {msg} - parent {parent}") 29 | 30 | def displayInformation(self, msg, title=None, parent=None): 31 | self.logger.error(f"INFORMATION: {title} - {msg} - parent {parent}") 32 | -------------------------------------------------------------------------------- /pyut/ui/ProjectException.py: -------------------------------------------------------------------------------- 1 | 2 | from enum import Enum 3 | 4 | from pyut.ui.IPyutProject import IPyutProject 5 | 6 | 7 | class ProjectExceptionType(Enum): 8 | INVALID_PROJECT = 'Invalid Project' 9 | PROJECT_NOT_FOUND = 'Project Not Found' 10 | ATTRIBUTE_ERROR = 'Attribute Error' 11 | TYPE_ERROR = 'Type Error' 12 | ASSERTION_ERROR = 'Assertion Error' 13 | GENERAL_ERROR = 'General Error' 14 | 15 | 16 | class ProjectException(Exception): 17 | 18 | def __init__(self, exceptionType: ProjectExceptionType, message: str, project: IPyutProject): 19 | 20 | self._exceptionType: ProjectExceptionType = exceptionType 21 | 22 | self._message: str = message 23 | self._badProject: IPyutProject = project 24 | 25 | @property 26 | def message(self) -> str: 27 | return self._message 28 | 29 | @property 30 | def project(self) -> IPyutProject: 31 | return self._badProject 32 | 33 | @property 34 | def exceptionType(self) -> ProjectExceptionType: 35 | return self._exceptionType 36 | 37 | def __str__(self) -> str: 38 | return f'{self._message}' 39 | -------------------------------------------------------------------------------- /tests/pyut/dialogs/DialogNamesEnum.py: -------------------------------------------------------------------------------- 1 | 2 | from enum import Enum 3 | 4 | 5 | class DialogNamesEnum(Enum): 6 | 7 | DLG_LOG_CONTROL = 'DlgLogControl' 8 | DLG_DEBUG_EVENT_ENGINE_DIALOG = 'DlgDebugEventEngineDialog' 9 | 10 | DLG_EDIT_INTERFACE = 'DlgEditInterface' 11 | DLG_TIPS_V2 = 'DlgTipsV2' 12 | DLG_EDIT_METHOD_MODIFIERS = 'DlgEditMethodModifiers' 13 | DLG_EDIT_PROJECT_HISTORY = 'DlgEditProjectHistory' 14 | 15 | DLG_EDIT_LINK = 'DlgEditLink' 16 | DLG_EDIT_CLASS = 'DlgEditClass' 17 | DLG_EDIT_USE_CASE = 'DlgEditUseCase' 18 | DLG_EDIT_ACTOR = 'DlgEditActor' 19 | DLG_EDIT_NOTE = 'DlgEditNote' 20 | DLG_EDIT_DESCRIPTION = 'DlgEditDescription' 21 | DLG_EDIT_TEXT = 'DlgEditText' 22 | DLG_EDIT_PARAMETER = 'DlgEditParameter' 23 | DLG_EDIT_FIELD = 'DlgEditField' 24 | DLG_EDIT_METHOD = 'DlgEditMethod' 25 | DLG_EDIT_CODE = 'DlgEditCode' 26 | 27 | DLG_EDIT_DIAGRAM_TITLE = 'DlgEditDiagramTitle' 28 | DLG_PYUT_PREFERENCES = 'DlgPyutPreferences' 29 | DLG_EDIT_STEREOTYPES = 'DlgEditStereotypes' 30 | -------------------------------------------------------------------------------- /pyut/enums/DiagramType.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class DiagramType(Enum): 5 | 6 | CLASS_DIAGRAM = 0 7 | SEQUENCE_DIAGRAM = 1 8 | USECASE_DIAGRAM = 2 9 | UNKNOWN_DIAGRAM = 3 10 | 11 | def __str__(self): 12 | return str(self.name) 13 | 14 | def __repr__(self): 15 | return self.__str__() 16 | 17 | @classmethod 18 | def toEnum(cls, strValue: str) -> 'DiagramType': 19 | """ 20 | Converts the input string to the correct diagram type 21 | Args: 22 | strValue: A string value 23 | 24 | Returns: The diagram type enumeration 25 | """ 26 | canonicalStr: str = strValue.strip(' ').lower() 27 | 28 | if canonicalStr == 'class_diagram': 29 | return DiagramType.CLASS_DIAGRAM 30 | elif canonicalStr == 'sequence_diagram': 31 | return DiagramType.SEQUENCE_DIAGRAM 32 | elif canonicalStr == 'usecase_diagram': 33 | return DiagramType.USECASE_DIAGRAM 34 | else: 35 | print(f'Warning: did not recognize this diagram type: {canonicalStr}') 36 | return DiagramType.CLASS_DIAGRAM 37 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/CommandDeleteOglNote.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | from wx import Yield as wxYield 6 | 7 | from pyutmodelv2.PyutNote import PyutNote 8 | 9 | from ogl.OglNote import OglNote 10 | 11 | from pyut.ui.wxcommands.BaseWxDeleteCommand import BaseWxDeleteCommand 12 | 13 | from pyut.ui.eventengine.EventType import EventType 14 | from pyut.ui.eventengine.IEventEngine import IEventEngine 15 | 16 | 17 | class CommandDeleteOglNote(BaseWxDeleteCommand): 18 | 19 | def __init__(self, oglNote: OglNote, eventEngine: IEventEngine): 20 | 21 | super().__init__(name='Delete Note', doableObject=oglNote, eventEngine=eventEngine) 22 | 23 | self.logger: Logger = getLogger(__name__) 24 | 25 | self._pyutNote: PyutNote = oglNote.pyutObject 26 | 27 | def Undo(self) -> bool: 28 | """ 29 | Override this member method to un-execute a previous Do. 30 | """ 31 | self._objectToDelete = OglNote(self._pyutNote, w=self._oglObjWidth, h=self._oglObjHeight) # create new 32 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbGetActiveUmlFrameForUndoDelete) 33 | 34 | wxYield() 35 | return True 36 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/CommandDeleteOglActor.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | from wx import Yield as wxYield 6 | 7 | from pyutmodelv2.PyutActor import PyutActor 8 | 9 | from ogl.OglActor import OglActor 10 | 11 | from pyut.ui.wxcommands.BaseWxDeleteCommand import BaseWxDeleteCommand 12 | 13 | from pyut.ui.eventengine.EventType import EventType 14 | from pyut.ui.eventengine.IEventEngine import IEventEngine 15 | 16 | 17 | class CommandDeleteOglActor(BaseWxDeleteCommand): 18 | 19 | def __init__(self, oglActor: OglActor, eventEngine: IEventEngine): 20 | 21 | super().__init__(name='Delete Actor', doableObject=oglActor, eventEngine=eventEngine) 22 | 23 | self.logger: Logger = getLogger(__name__) 24 | 25 | self._pyutActor: PyutActor = oglActor.pyutObject 26 | 27 | def Undo(self) -> bool: 28 | """ 29 | Override this member method to un-execute a previous Do. 30 | """ 31 | self._objectToDelete = OglActor(self._pyutActor, w=self._oglObjWidth, h=self._oglObjHeight) # create new 32 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbGetActiveUmlFrameForUndoDelete) 33 | 34 | wxYield() 35 | return True 36 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/CommandDeleteOglText.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | from wx import Yield as wxYield 6 | 7 | from pyutmodelv2.PyutText import PyutText 8 | 9 | from ogl.OglText import OglText 10 | 11 | from pyut.ui.wxcommands.BaseWxDeleteCommand import BaseWxDeleteCommand 12 | 13 | from pyut.ui.eventengine.EventType import EventType 14 | from pyut.ui.eventengine.IEventEngine import IEventEngine 15 | 16 | 17 | class CommandDeleteOglText(BaseWxDeleteCommand): 18 | 19 | def __init__(self, oglText: OglText, eventEngine: IEventEngine): 20 | 21 | super().__init__(name='Delete Ogl Text', doableObject=oglText, eventEngine=eventEngine) 22 | 23 | self.logger: Logger = getLogger(__name__) 24 | 25 | self._pyutText: PyutText = oglText.pyutObject 26 | 27 | def Undo(self) -> bool: 28 | """ 29 | Override this member method to un-execute a previous Do. 30 | """ 31 | self._objectToDelete = OglText(self._pyutText, width=self._oglObjWidth, height=self._oglObjHeight) # create new 32 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbGetActiveUmlFrameForUndoDelete) 33 | 34 | wxYield() 35 | return True 36 | -------------------------------------------------------------------------------- /tests/resources/testLoggingConfiguration.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "disable_existing_loggers": false, 4 | "formatters": { 5 | "simple": { 6 | "format": "%(asctime)s.%(msecs)03d %(levelname)s %(module)s: %(message)s" 7 | }, 8 | "testSimple": { 9 | "format": "%(levelname)s: %(module)s: %(message)s" 10 | } 11 | }, 12 | "handlers": { 13 | "consoleHandler": { 14 | "class": "logging.StreamHandler", 15 | "formatter": "testSimple", 16 | "stream": "ext://sys.stdout" 17 | } 18 | }, 19 | "loggers": { 20 | "root": { 21 | "level": "WARNING", 22 | "handlers": ["consoleHandler"], 23 | "propagate": false 24 | }, 25 | "__main__": { 26 | "level": "WARNING", 27 | "propagate": false 28 | }, 29 | "pyut": { 30 | "level": "WARNING", 31 | "propagate": false 32 | }, 33 | "tests": { 34 | "level": "WARNING", 35 | "propagate": false 36 | }, 37 | "pyut.ui.dialogs": { 38 | "level": "INFO", 39 | "propagate": false 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/CommandDeleteOglUseCase.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | from wx import Yield as wxYield 6 | 7 | from pyutmodelv2.PyutUseCase import PyutUseCase 8 | 9 | from ogl.OglUseCase import OglUseCase 10 | 11 | from pyut.ui.eventengine.EventType import EventType 12 | from pyut.ui.eventengine.IEventEngine import IEventEngine 13 | 14 | from pyut.ui.wxcommands.BaseWxDeleteCommand import BaseWxDeleteCommand 15 | 16 | 17 | class CommandDeleteOglUseCase(BaseWxDeleteCommand): 18 | 19 | def __init__(self, oglUseCase: OglUseCase, eventEngine: IEventEngine): 20 | 21 | super().__init__(name='Delete Use Case', doableObject=oglUseCase, eventEngine=eventEngine) 22 | 23 | self.logger: Logger = getLogger(__name__) 24 | 25 | self._pyutUseCase: PyutUseCase = oglUseCase.pyutObject 26 | 27 | def Undo(self) -> bool: 28 | """ 29 | Override this member method to un-execute a previous Do 30 | """ 31 | self._objectToDelete = OglUseCase(self._pyutUseCase, w=self._oglObjWidth, h=self._oglObjHeight) # create new 32 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbGetActiveUmlFrameForUndoDelete) 33 | 34 | wxYield() 35 | return True 36 | -------------------------------------------------------------------------------- /archive/help/main.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | font-family: "Benton Sans", "Helvetica Neue", helvetica, arial, sans-serif; 4 | margin: 2em; 5 | background-color: HoneyDew; 6 | } 7 | 8 | 9 | /* Century Schoolbook font is very similar to Computer Modern Math: cmmi */ 10 | .MATH { font-family: "Century Schoolbook", serif; } 11 | .MATH I { font-family: "Century Schoolbook", serif; font-style: italic } 12 | .BOLDMATH { font-family: "Century Schoolbook", serif; font-weight: bold } 13 | 14 | /* implement both fixed-size and relative sizes */ 15 | SMALL.XTINY { font-size : xx-small } 16 | SMALL.TINY { font-size : x-small } 17 | SMALL.SCRIPTSIZE { font-size : smaller } 18 | SMALL.FOOTNOTESIZE { font-size : small } 19 | SMALL.SMALL { } 20 | BIG.LARGE { } 21 | BIG.XLARGE { font-size : large } 22 | BIG.XXLARGE { font-size : x-large } 23 | BIG.HUGE { font-size : larger } 24 | BIG.XHUGE { font-size : xx-large } 25 | 26 | /* heading styles */ 27 | H1 { } 28 | H2 { } 29 | H3 { } 30 | H4 { } 31 | H5 { } 32 | 33 | /* mathematics styles */ 34 | DIV.displaymath { } /* math displays */ 35 | TD.eqno { } /* equation-number cells */ 36 | 37 | 38 | /* document-specific styles come next */ 39 | 40 | .super-duper-title { 41 | font-weight: bold; 42 | font-size: 2.0em; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /scripts/iconset/makeicn.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # 5 | # The generated .icns file is copied to src/org/pyut/resources/img/Pyut.icns 6 | # 7 | # The file Pyut.iconset/icon_512x512@2x.png is reduced to 33% of the original size 8 | # and copied into src/org/pyut/resources/img/ImgPyut.png 9 | # 10 | # 11 | # and is embedded as follows: 12 | # 13 | # img2py -n embeddedImage -i ImgPyut.png ImgPyut.py 14 | # 15 | # 16 | ICON_SET_DIR='Pyut.iconset' 17 | ICON_IMAGE='PyutLogo-512.png' 18 | 19 | mkdir ${ICON_SET_DIR} 20 | 21 | 22 | sips -z 16 16 ${ICON_IMAGE} --out ${ICON_SET_DIR}/icon_16x16.png 23 | sips -z 32 32 ${ICON_IMAGE} --out ${ICON_SET_DIR}/icon_16x16@2x.png 24 | 25 | sips -z 32 32 ${ICON_IMAGE} --out ${ICON_SET_DIR}/icon_32x32.png 26 | sips -z 64 64 ${ICON_IMAGE} --out ${ICON_SET_DIR}/icon_32x32@2x.png 27 | 28 | sips -z 128 128 ${ICON_IMAGE} --out ${ICON_SET_DIR}/icon_128x128.png 29 | sips -z 256 256 ${ICON_IMAGE} --out ${ICON_SET_DIR}/icon_128x128@2x.png 30 | 31 | sips -z 256 256 ${ICON_IMAGE} --out ${ICON_SET_DIR}/icon_256x256.png 32 | sips -z 512 512 ${ICON_IMAGE} --out ${ICON_SET_DIR}/icon_256x256@2x.png 33 | 34 | sips -z 512 512 ${ICON_IMAGE} --out ${ICON_SET_DIR}/icon_512x512.png 35 | 36 | 37 | cp ${ICON_IMAGE} ${ICON_SET_DIR}/icon_512x512@2x.png 38 | 39 | iconutil -c icns ${ICON_SET_DIR} 40 | 41 | # rm -rf ${ICON_SET_DIR} 42 | -------------------------------------------------------------------------------- /pyut/ui/tools/Tool.py: -------------------------------------------------------------------------------- 1 | 2 | from dataclasses import dataclass 3 | 4 | from typing import Callable 5 | from typing import NewType 6 | from typing import cast 7 | 8 | from wx import Bitmap 9 | from wx import WindowIDRef 10 | 11 | 12 | Category = NewType('Category', str) 13 | 14 | 15 | @dataclass 16 | class Tool: 17 | """ 18 | Tool : a tool for pyut's toolboxes 19 | """ 20 | id: str = '' 21 | """ 22 | A unique ID for this tool (plugin id + tool id) 23 | """ 24 | img: Bitmap = cast(Bitmap, None) 25 | """ 26 | An image for the tool 27 | """ 28 | caption: str = '' 29 | """ 30 | A caption for the tool 31 | """ 32 | tooltip: str = '' 33 | """ 34 | A tooltip: tip for this tool 35 | """ 36 | category: Category = Category('') 37 | """ 38 | A category for this tool 39 | """ 40 | actionCallback: Callable = cast(Callable, None) 41 | """ 42 | A eventHandler method for doing this tool's actions 43 | """ 44 | propertiesCallback: Callable = cast(Callable, None) 45 | """ 46 | A eventHandler function for displaying this tool's properties 47 | """ 48 | wxID: WindowIDRef = cast(WindowIDRef, None) 49 | """ 50 | A wx unique ID, used for the eventHandler 51 | """ 52 | isToggle: bool = False 53 | """ 54 | True if the tool can be toggled 55 | """ 56 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/preferences/BasePreferencesPage.py: -------------------------------------------------------------------------------- 1 | 2 | from abc import abstractmethod 3 | from abc import ABCMeta 4 | 5 | from wx import Window 6 | 7 | from wx.lib.sized_controls import SizedPanel 8 | 9 | from pyut.preferences.PyutPreferences import PyutPreferences 10 | 11 | 12 | class MyMetaBasePreferencesPage(ABCMeta, type(SizedPanel)): # type: ignore 13 | """ 14 | I have no idea why this works: 15 | https://stackoverflow.com/questions/66591752/metaclass-conflict-when-trying-to-create-a-python-abstract-class-that-also-subcl 16 | """ 17 | pass 18 | 19 | 20 | class BasePreferencesPage(SizedPanel): 21 | 22 | __metaclass__ = MyMetaBasePreferencesPage 23 | 24 | def __init__(self, parent: Window): 25 | 26 | super().__init__(parent) 27 | 28 | self._preferences: PyutPreferences = PyutPreferences() 29 | 30 | @property 31 | @abstractmethod 32 | def name(self) -> str: 33 | pass 34 | 35 | @abstractmethod 36 | def _setControlValues(self): 37 | pass 38 | 39 | def _fixPanelSize(self, panel: SizedPanel): 40 | """ 41 | Use this method or a dialog will not get resized correctly 42 | A little trick to make sure that the sizer cannot be resized to 43 | less screen space than the controls needs; 44 | 45 | Args: 46 | panel: 47 | """ 48 | panel.Fit() 49 | panel.SetMinSize(panel.GetSize()) 50 | -------------------------------------------------------------------------------- /tests/pyut/ui/TestToolBoxHandler.py: -------------------------------------------------------------------------------- 1 | 2 | from unittest import TestSuite 3 | from unittest import main as unitTestMain 4 | 5 | from codeallyadvanced.ui.UnitTestBaseW import UnitTestBaseW 6 | 7 | from pyut.ui.ToolBoxHandler import ToolBoxHandler 8 | 9 | 10 | class TestToolBoxHandler(UnitTestBaseW): 11 | """ 12 | Auto generated by the one and only: 13 | Gato Malo - Humberto A. Sanchez II 14 | Generated: 17 January 2024 15 | """ 16 | 17 | @classmethod 18 | def setUpClass(cls): 19 | super().setUpClass() 20 | 21 | def setUp(self): 22 | super().setUp() 23 | self._toolBoxHandler: ToolBoxHandler = ToolBoxHandler(frame=self._topLevelWindow) 24 | 25 | def tearDown(self): 26 | super().tearDown() 27 | 28 | def testSingletonBehavior(self): 29 | toolBoxHandler1: ToolBoxHandler = ToolBoxHandler() 30 | toolBoxHandler2: ToolBoxHandler = ToolBoxHandler() 31 | 32 | self.logger.info(f'{toolBoxHandler1=} {toolBoxHandler2=} {self._toolBoxHandler=}') 33 | self.assertEqual(toolBoxHandler1, toolBoxHandler2, 'Should be the same') 34 | 35 | 36 | def suite() -> TestSuite: 37 | import unittest 38 | 39 | testSuite: TestSuite = TestSuite() 40 | 41 | testSuite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(testCaseClass=TestToolBoxHandler)) 42 | 43 | return testSuite 44 | 45 | 46 | if __name__ == '__main__': 47 | unitTestMain() 48 | -------------------------------------------------------------------------------- /archive/FAQ: -------------------------------------------------------------------------------- 1 | (This is pyut's FAQ file. see https://github.com/hasii2011/PyUt/ for more information) 2 | This file contain the project FAQ's. 3 | (File maintainer: Humberto A. Sanchez. II ) 4 | 5 | 6 | 7 | 8 | 1. Reverse engineering Python 9 | ============================= 10 | Reverse engineering Python launches my program. Why ? 11 | Q: When I do Python Reverse engineering on my program, my program 12 | is launched. Why ? 13 | A: Python Reverse does an 'import' on python files. 14 | An Import launches all the code that is found in the imported module. 15 | To fix it, you must add this kind of code in your program : 16 | if __name__=="__main__": 17 | do_some_stuff 18 | This is general stuff, it is a good idea to put this in all your code. 19 | (Answered by C.Dutoit ) 20 | 21 | 22 | 2. How to run Pyut 23 | ============================= 24 | Q: How do I get Pyut binaries? 25 | 26 | A. Unfortunately, at this time I can only generate binaries for Mac OS X. 27 | Also, I have not yet started work on generating Window exe files 28 | 29 | The mac binary is here: https://github.com/hasii2011/PyUt/releases/tag/4.0 30 | 31 | 3. Pyut Development 32 | ============================= 33 | Q: Why do some unit tests fail with: "wx._core.PyNoAppError: The wx.App object must be created first!" 34 | 35 | A. Make sure that the Debug preference debug_basic_shape is set to False 36 | debug_basic_shape = False 37 | -------------------------------------------------------------------------------- /pyut/experimental/DebugErrorViews.py: -------------------------------------------------------------------------------- 1 | from wx import CommandEvent 2 | 3 | from pyut.errorcontroller.ErrorManager import ErrorManager 4 | from pyut.errorcontroller.ErrorViewType import ErrorViewType 5 | 6 | 7 | class DebugErrorViews: 8 | 9 | # noinspection PyUnusedLocal 10 | @staticmethod 11 | def debugGraphicErrorView(commandEvent: CommandEvent): 12 | 13 | em: ErrorManager = ErrorManager() 14 | DebugErrorViews._makeCalls(em=em, viewType=ErrorViewType.GRAPHIC_ERROR_VIEW) 15 | 16 | # noinspection PyUnusedLocal 17 | @staticmethod 18 | def debugTextErrorView(commandEvent: CommandEvent): 19 | 20 | em: ErrorManager = ErrorManager() 21 | DebugErrorViews._makeCalls(em=em, viewType=ErrorViewType.TEXT_ERROR_VIEW) 22 | 23 | # noinspection PyUnusedLocal 24 | @staticmethod 25 | def debugRaiseErrorView(commandEvent: CommandEvent): 26 | 27 | em: ErrorManager = ErrorManager() 28 | DebugErrorViews._makeCalls(em=em, viewType=ErrorViewType.RAISE_ERROR_VIEW) 29 | 30 | @staticmethod 31 | def _makeCalls(em: ErrorManager, viewType: ErrorViewType): 32 | 33 | em.errorViewType = viewType 34 | 35 | em.displayInformation(msg=f'{viewType} Message', title=f'{viewType} Title', parent=None) 36 | em.displayWarning(msg=f'{viewType} - Warning Message', title=f'{viewType} - WarningTitle', parent=None) 37 | em.displayFatalError(msg=f'{viewType}: Fatal Message', title=f'{viewType} - Fatal Title', parent=None) 38 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | 2 | version: '2.1' 3 | 4 | orbs: 5 | python: circleci/python@2.1.1 6 | 7 | workflows: 8 | main: 9 | jobs: 10 | - build: 11 | filters: 12 | tags: 13 | only: /.*/ 14 | 15 | jobs: 16 | build: 17 | docker: 18 | - image: cimg/python:3.12-browsers 19 | executor: python/default 20 | steps: 21 | - checkout 22 | - run: 23 | name: Install library dependencies 24 | command: | 25 | sudo apt update 26 | sudo apt install libnotify-dev 27 | sudo apt install libgtk-3-dev 28 | sudo apt-get install -y libsdl2-dev 29 | pip install --upgrade pip 30 | pip install wheel 31 | pip install setuptools 32 | pip install attrdict3~=2.0.2 33 | pip install -v -U -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-22.04 wxPython 34 | pip install semantic-version==2.10.0 35 | pip install PyGithub==2.6.1 36 | pip install codeallybasic==1.10.0 37 | pip install codeallyadvanced==1.4.2 38 | pip install pyutmodelv2==2.2.3 39 | pip install ogl==3.6.7 40 | pip install oglio==2.4.0 41 | pip install pyutplugins==3.2.6 42 | pip install html-testRunner==1.2.1 43 | pip install buildlackey==1.8.1 44 | - run: 45 | name: run tests 46 | command: | 47 | unittests 48 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/logcontrol/DebugLevel.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import DEBUG 3 | from logging import ERROR 4 | from logging import FATAL 5 | from logging import INFO 6 | from logging import NOTSET 7 | from logging import WARNING 8 | 9 | from enum import Enum 10 | 11 | 12 | class DebugLevel(Enum): 13 | """ 14 | A wrapper enumeration around Python log levels; The 15 | value is the Pythong logging level 16 | """ 17 | 18 | NOTSET = NOTSET 19 | ERROR = ERROR 20 | WARNING = WARNING 21 | INFO = INFO 22 | DEBUG = DEBUG 23 | FATAL = FATAL 24 | 25 | @classmethod 26 | def toEnum(cls, pythonLevel: int) -> 'DebugLevel': 27 | """ 28 | This is needed when we retrieve the actual logger. Need the 29 | enumeration so we can treat it as a 30 | Args: 31 | pythonLevel: 32 | 33 | Returns: 34 | 35 | """ 36 | import logging 37 | 38 | match pythonLevel: 39 | case logging.NOTSET: 40 | return DebugLevel.NOTSET 41 | case logging.ERROR: 42 | return DebugLevel.ERROR 43 | case logging.WARNING: 44 | return DebugLevel.WARNING 45 | case logging.INFO: 46 | return DebugLevel.INFO 47 | case logging.DEBUG: 48 | return DebugLevel.DEBUG 49 | case logging.FATAL: 50 | return DebugLevel.FATAL 51 | case _: 52 | return DebugLevel.NOTSET 53 | -------------------------------------------------------------------------------- /pyut/ui/Action.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class Action(Enum): 5 | 6 | SELECTOR = 'Selector' 7 | NEW_CLASS = 'NewClass' 8 | NEW_ACTOR = 'NewActor' 9 | NEW_USECASE = 'NewUseCase' 10 | NEW_NOTE = 'NewNote' 11 | NEW_IMPLEMENT_LINK = 'NewImplementLink' 12 | NEW_INTERFACE = 'NewInterface' 13 | NEW_INHERIT_LINK = 'NewInheritLink' 14 | NEW_AGGREGATION_LINK = 'NewAggregationLink', 15 | NEW_COMPOSITION_LINK = 'NewCompositionLink' 16 | NEW_ASSOCIATION_LINK = 'NewAssociationLink' 17 | NEW_NOTE_LINK = 'NewNoteLink' 18 | NEW_TEXT = 'NewText' 19 | 20 | DESTINATION_IMPLEMENT_LINK = 'DestinationImplementLink' 21 | DESTINATION_INHERIT_LINK = 'DestinationInheritLink' 22 | DESTINATION_AGGREGATION_LINK = 'DestinationAggregationLink' 23 | DESTINATION_COMPOSITION_LINK = 'DestinationCompositionLink' 24 | DESTINATION_ASSOCIATION_LINK = 'DestinationAssociationLink' 25 | DESTINATION_NOTE_LINK = 'DestinationNoteLink' 26 | NEW_SD_INSTANCE = 'NewSDInstance' 27 | NEW_SD_MESSAGE = 'NewSDMessage' 28 | DESTINATION_SD_MESSAGE = 'DestinationSDMessage' 29 | ZOOM_IN = 'ZoomIn' 30 | ZOOM_OUT = 'ZoomOut' 31 | NO_ACTION = 'NoAction' 32 | 33 | def __str__(self): 34 | return str(self.name) 35 | 36 | def __repr__(self): 37 | return self.name 38 | 39 | -------------------------------------------------------------------------------- /tests/pyut/ui/eventengine/TestEventType.py: -------------------------------------------------------------------------------- 1 | 2 | from unittest import TestSuite 3 | from unittest import main as unitTestMain 4 | 5 | from codeallybasic.UnitTestBase import UnitTestBase 6 | 7 | from pyut.ui.eventengine.Events import EVENT_NEW_NAMED_PROJECT 8 | from pyut.ui.eventengine.EventType import EventType 9 | 10 | 11 | class TestEventType(UnitTestBase): 12 | """ 13 | Auto generated by the one and only: 14 | Gato Malo – Humberto A. Sanchez II 15 | Generated: 16 December 2024 16 | """ 17 | 18 | @classmethod 19 | def setUpClass(cls): 20 | super().setUpClass() 21 | 22 | def setUp(self): 23 | super().setUp() 24 | 25 | def tearDown(self): 26 | super().tearDown() 27 | 28 | def testBasicCreation(self): 29 | 30 | eventType: EventType = EventType.NewProject 31 | self.assertIsNotNone(eventType, 'We must be able to create it') 32 | 33 | def testCorrectedEventTypeId(self): 34 | 35 | eventType: EventType = EventType.NewNamedProject 36 | 37 | expectedId: int = EVENT_NEW_NAMED_PROJECT.typeId 38 | self.assertEqual(expectedId, eventType.value) 39 | 40 | def testEnsureWeHaveDefinedAllTypeIDs(self): 41 | pass 42 | 43 | 44 | def suite() -> TestSuite: 45 | import unittest 46 | 47 | testSuite: TestSuite = TestSuite() 48 | 49 | testSuite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(testCaseClass=TestEventType)) 50 | 51 | return testSuite 52 | 53 | 54 | if __name__ == '__main__': 55 | unitTestMain() 56 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/DlgEditDescription.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Union 3 | 4 | from wx import EVT_TEXT 5 | from wx import TE_MULTILINE 6 | 7 | from wx import TextCtrl 8 | from wx import Window 9 | 10 | from wx.lib.sized_controls import SizedPanel 11 | 12 | from pyutmodelv2.PyutClass import PyutClass 13 | from pyutmodelv2.PyutInterface import PyutInterface 14 | 15 | from pyut.ui.dialogs.BaseEditDialog import BaseEditDialog 16 | 17 | 18 | class DlgEditDescription(BaseEditDialog): 19 | """ 20 | Edit a class description 21 | """ 22 | def __init__(self, parent: Window, pyutModel: Union[PyutClass, PyutInterface]): 23 | """ 24 | 25 | Args: 26 | parent: 27 | pyutModel: 28 | """ 29 | super().__init__(parent, title="Edit Description") 30 | 31 | self._pyutModel: Union[PyutClass, PyutInterface] = pyutModel 32 | 33 | sizedPanel: SizedPanel = self.GetContentsPane() 34 | 35 | self._txtCtrl: TextCtrl = TextCtrl(sizedPanel, value=self._pyutModel.description, style=TE_MULTILINE) 36 | self._txtCtrl.SetSizerProps(expand=True, proportion=1) 37 | self._txtCtrl.SetFocus() 38 | 39 | self._layoutStandardOkCancelButtonSizer() 40 | 41 | # text events 42 | self.Bind(EVT_TEXT, self._onTxtDescriptionChange, self._txtCtrl) 43 | 44 | self.Centre() 45 | 46 | @property 47 | def description(self) -> str: 48 | return self._pyutModel.description 49 | 50 | def _onTxtDescriptionChange(self, event): 51 | self._pyutModel.description = event.GetString() 52 | -------------------------------------------------------------------------------- /pyut/ui/CurrentDirectoryHandler.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | from os import sep as osSeparator 6 | from os import environ as osEnviron 7 | 8 | from codeallybasic.SingletonV3 import SingletonV3 9 | 10 | from pyut.preferences.PyutPreferences import PyutPreferences 11 | 12 | 13 | class CurrentDirectoryHandler(metaclass=SingletonV3): 14 | 15 | HOME_ENV_VAR: str = 'HOME' 16 | DIAGRAMS_DIRECTORY_NAME: str = '' 17 | 18 | def __init__(self): 19 | 20 | self.logger: Logger = getLogger(__name__) 21 | self._preferences: PyutPreferences = PyutPreferences() 22 | self._currentDirectory: str = '' 23 | 24 | if self._preferences.diagramsDirectory == '': 25 | if CurrentDirectoryHandler.HOME_ENV_VAR in osEnviron: 26 | self._currentDirectory = osEnviron[CurrentDirectoryHandler.HOME_ENV_VAR] 27 | self._preferences.diagramsDirectory = osEnviron[CurrentDirectoryHandler.HOME_ENV_VAR] 28 | else: 29 | self._currentDirectory = self._preferences.diagramsDirectory 30 | 31 | @property 32 | def currentDirectory(self): 33 | 34 | return self._currentDirectory 35 | 36 | @currentDirectory.setter 37 | def currentDirectory(self, fullPath: str): 38 | """ 39 | Set the current working directory. 40 | We'll strip off the filename 41 | 42 | Args: 43 | fullPath: Full path, with filename 44 | """ 45 | self._currentDirectory = fullPath[:fullPath.rindex(osSeparator)] 46 | -------------------------------------------------------------------------------- /pyut/ui/IPyutDocument.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import List 3 | from typing import NewType 4 | 5 | from abc import ABC 6 | from abc import abstractmethod 7 | 8 | from wx import TreeItemId 9 | 10 | from pyut.enums.DiagramType import DiagramType 11 | from pyut.ui.Types import UmlFrameType 12 | 13 | 14 | class IPyutDocument(ABC): 15 | """ 16 | Something to fool mypy 17 | """ 18 | def __init__(self): 19 | pass 20 | 21 | @property 22 | @abstractmethod 23 | def diagramFrame(self) -> UmlFrameType: 24 | """ 25 | Return the document's frame 26 | 27 | Returns: this document's uml frame 28 | """ 29 | pass 30 | 31 | @property 32 | @abstractmethod 33 | def title(self) -> str: 34 | pass 35 | 36 | @title.setter 37 | @abstractmethod 38 | def title(self, theNewValue: str): 39 | pass 40 | 41 | @property 42 | @abstractmethod 43 | def treeRoot(self) -> TreeItemId: 44 | """ 45 | Returns: The tree root ItemId for this document's node 46 | """ 47 | pass 48 | 49 | @treeRoot.setter 50 | @abstractmethod 51 | def treeRoot(self, value: TreeItemId): 52 | pass 53 | 54 | @property 55 | @abstractmethod 56 | def diagramType(self) -> DiagramType: 57 | """ 58 | Returns: 59 | The document type 60 | """ 61 | pass 62 | 63 | @abstractmethod 64 | def updateTreeText(self): 65 | """ 66 | Implemented only by legacy Pyut 67 | """ 68 | pass 69 | 70 | 71 | PyutDocuments = NewType('PyutDocuments', List[IPyutDocument]) 72 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/CommandCreateOglUseCase.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import cast 3 | 4 | from logging import Logger 5 | from logging import getLogger 6 | 7 | from pyutmodelv2.PyutUseCase import PyutUseCase 8 | 9 | from ogl.OglUseCase import OglUseCase 10 | 11 | from pyut.ui.wxcommands.BaseWxCreateCommand import BaseWxCreateCommand 12 | 13 | from pyut.ui.eventengine.EventType import EventType 14 | 15 | from pyut.ui.eventengine.IEventEngine import IEventEngine 16 | 17 | 18 | class CommandCreateOglUseCase(BaseWxCreateCommand): 19 | 20 | def __init__(self, x: int, y: int, eventEngine: IEventEngine): 21 | 22 | super().__init__(canUndo=True, name='Create Use Case', x=x, y=y, eventEngine=eventEngine) 23 | 24 | self.logger: Logger = getLogger(__name__) 25 | 26 | def _createPrototypeInstance(self) -> OglUseCase: 27 | 28 | pyutUseCase: PyutUseCase = PyutUseCase(name=self._oglPreferences.defaultNameUsecase) 29 | oglUseCase: OglUseCase = OglUseCase(pyutUseCase) 30 | 31 | return oglUseCase 32 | 33 | def _placeShapeOnFrame(self): 34 | 35 | oglUseCase: OglUseCase = cast(OglUseCase, self._shape) # get old or prototype on first use 36 | 37 | pyutUseCase: PyutUseCase = cast(PyutUseCase, oglUseCase.pyutObject) 38 | self._oglObjWidth, self._oglObjHeight = oglUseCase.GetSize() 39 | 40 | self._shape = OglUseCase(pyutUseCase, w=self._oglObjWidth, h=self._oglObjHeight) # create new one 41 | 42 | self._eventEngine.sendEvent(EventType.EditUseCase, pyutUseCase=pyutUseCase) 43 | 44 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbAddOglObjectToFrame) 45 | -------------------------------------------------------------------------------- /tests/AppTestGriddedDiagramApplication.py: -------------------------------------------------------------------------------- 1 | 2 | from wx import BOTTOM 3 | from wx import DEFAULT_FRAME_STYLE 4 | from wx import EXPAND 5 | from wx import ID_ANY 6 | from wx import VERTICAL 7 | 8 | from wx import FileDropTarget 9 | from wx import App 10 | from wx import Frame 11 | from wx import BoxSizer 12 | 13 | from miniogl.DiagramFrame import DiagramFrame 14 | 15 | from tests.ProjectTestBase import ProjectTestBase 16 | 17 | WINDOW_WIDTH: int = 900 18 | WINDOW_HEIGHT: int = 500 19 | 20 | 21 | class PyutFileDropTarget(FileDropTarget): 22 | 23 | def OnDropFiles(self, x, y, filenames): 24 | 25 | for fileName in filenames: 26 | print(f'You dropped: {fileName}') 27 | 28 | return True 29 | 30 | 31 | class TestGriddedDiagramApplication(App): 32 | 33 | def OnInit(self): 34 | 35 | ProjectTestBase.setUpLogging() 36 | 37 | frameTop: Frame = Frame(parent=None, id=ID_ANY, title="Test Gridded Diagram", size=(WINDOW_WIDTH, WINDOW_HEIGHT), style=DEFAULT_FRAME_STYLE) 38 | 39 | diagramFrame: DiagramFrame = DiagramFrame(frameTop) 40 | diagramFrame.SetSize((WINDOW_WIDTH, WINDOW_HEIGHT)) 41 | diagramFrame.SetScrollbars(10, 10, 100, 100) 42 | 43 | frameTop.SetAutoLayout(True) 44 | 45 | mainSizer: BoxSizer = BoxSizer(orient=VERTICAL) 46 | 47 | mainSizer.Add(diagramFrame, 1, EXPAND | BOTTOM, 10) 48 | frameTop.SetSizer(mainSizer) 49 | mainSizer.Fit(frameTop) 50 | 51 | frameTop.SetDropTarget(PyutFileDropTarget()) 52 | 53 | frameTop.Show(True) 54 | 55 | return True 56 | 57 | 58 | testApp: App = TestGriddedDiagramApplication(redirect=False) 59 | 60 | testApp.MainLoop() 61 | -------------------------------------------------------------------------------- /tests/pyut/general/TestGitHubAdapter.py: -------------------------------------------------------------------------------- 1 | 2 | from unittest import TestSuite 3 | from unittest import main as unitTestMain 4 | 5 | from codeallybasic.SemanticVersion import SemanticVersion 6 | from codeallybasic.UnitTestBase import UnitTestBase 7 | 8 | from pyut.general.GitHubAdapter import GitHubAdapter 9 | 10 | 11 | class TestGitHubAdapter(UnitTestBase): 12 | """ 13 | """ 14 | @classmethod 15 | def setUpClass(cls): 16 | UnitTestBase.setUpClass() 17 | 18 | import warnings 19 | # To ignore this warning: 20 | # GithubAdapter.py:60: ResourceWarning: unclosed TestSuite: 41 | """You need to change the name of the test class here also.""" 42 | import unittest 43 | 44 | testSuite: TestSuite = TestSuite() 45 | 46 | testSuite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(testCaseClass=TestGitHubAdapter)) 47 | 48 | return testSuite 49 | 50 | 51 | if __name__ == '__main__': 52 | unitTestMain() 53 | -------------------------------------------------------------------------------- /pyut/PyutConstants.py: -------------------------------------------------------------------------------- 1 | from os import sep as osSep 2 | 3 | from pyut.enums.DiagramType import DiagramType 4 | 5 | # Types of diagrams labels 6 | # TODO: Make this an enumeration 7 | DiagramsLabels = { 8 | DiagramType.CLASS_DIAGRAM: "Class Diagram", 9 | DiagramType.SEQUENCE_DIAGRAM: "Sequence Diagram", 10 | DiagramType.USECASE_DIAGRAM: "Use-Case Diagram", 11 | DiagramType.UNKNOWN_DIAGRAM: "Unknown Diagram", 12 | } 13 | 14 | DiagramsStrings = { 15 | DiagramType.CLASS_DIAGRAM: "CLASS_DIAGRAM", 16 | DiagramType.SEQUENCE_DIAGRAM: "SEQUENCE_DIAGRAM", 17 | DiagramType.USECASE_DIAGRAM: "USECASE_DIAGRAM", 18 | DiagramType.UNKNOWN_DIAGRAM: "UNKNOWN_DIAGRAM", 19 | } 20 | 21 | 22 | class PyutConstants: 23 | 24 | PYUT_EXTENSION: str = '.put' 25 | XML_EXTENSION: str = '.xml' 26 | 27 | DEFAULT_PROJECT_NAME: str = 'Untitled' 28 | DEFAULT_FILE_NAME: str = f'{DEFAULT_PROJECT_NAME}{PYUT_EXTENSION}' 29 | 30 | APP_MODE: str = 'APP_MODE' 31 | # noinspection SpellCheckingInspection 32 | PYTHON_OPTIMIZE: str = 'PYTHONOPTIMIZE' 33 | 34 | THE_GREAT_MAC_PLATFORM: str = 'darwin' 35 | 36 | # Used to log error messages or general logging messages by using the standard 37 | # Python logging mechanism 38 | # Needs to match the name in loggingConfiguration.json 39 | MAIN_LOGGING_NAME: str = "Pyut" 40 | 41 | BASE_RESOURCES_PACKAGE: str = 'pyut.resources' 42 | IMAGE_RESOURCES_PACKAGE: str = f'{BASE_RESOURCES_PACKAGE}.img' 43 | BASE_RESOURCE_PATH: str = f'pyut{osSep}resources' 44 | 45 | # These are given a name because wxPython is weird and did not name them 46 | 47 | WX_SIZER_NOT_CHANGEABLE: int = 0 48 | WX_SIZER_CHANGEABLE: int = 1 49 | 50 | TIPS_FILENAME: str = 'tips.txt' 51 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/textdialogs/DlgEditNote.py: -------------------------------------------------------------------------------- 1 | 2 | from wx import EVT_TEXT 3 | from wx import TE_MULTILINE 4 | 5 | from wx import CommandEvent 6 | from wx import TextCtrl 7 | from wx import Window 8 | 9 | from wx.lib.sized_controls import SizedPanel 10 | 11 | from pyutmodelv2.PyutNote import PyutNote 12 | 13 | from pyut.ui.dialogs.BaseEditDialog import BaseEditDialog 14 | 15 | 16 | class DlgEditNote(BaseEditDialog): 17 | """ 18 | Defines a multi-line text control dialog for note editing. 19 | This dialog is used to ask the user to enter the text that will be 20 | displayed in a UML note. 21 | 22 | Sample use: 23 | with DlgEditNote(umlFrame, pyutNote) as dlg: 24 | if dlg.ShowModal() == ID_OK: 25 | self._eventEngine.sendEvent(EventType.UMLDiagramModified) 26 | 27 | """ 28 | def __init__(self, parent: Window, pyutNote: PyutNote): 29 | """ 30 | 31 | Args: 32 | parent: parent window to center on 33 | pyutNote: Model object we are editing 34 | """ 35 | super().__init__(parent, title="Edit Note") 36 | 37 | self._pyutNote: PyutNote = pyutNote 38 | 39 | sizedPanel: SizedPanel = self.GetContentsPane() 40 | 41 | self._txtCtrl: TextCtrl = TextCtrl(sizedPanel, value=self._pyutNote.content, size=(400, 180), style=TE_MULTILINE) 42 | self._txtCtrl.SetFocus() 43 | 44 | self._layoutStandardOkCancelButtonSizer() 45 | 46 | self.Bind(EVT_TEXT, self._onTxtNoteChange, self._txtCtrl) 47 | 48 | self.Centre() 49 | 50 | def _onTxtNoteChange(self, event: CommandEvent): 51 | """ 52 | Handle changes to the text in the widget identified by TXT_NOTE 53 | 54 | Args: 55 | event: 56 | """ 57 | self._pyutNote.content = event.GetString() 58 | -------------------------------------------------------------------------------- /pyut/ui/PyutPrintout.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from wx import Printout 4 | from wx import DC 5 | 6 | 7 | class PyutPrintout(Printout): 8 | """ 9 | Class to prepare for printing 10 | 11 | This class is used to prepare printing 12 | It is a copy from the wx.Python documentation 13 | 14 | """ 15 | def __init__(self, canvas): 16 | 17 | super().__init__() 18 | 19 | self.canvas = canvas 20 | self.nbPages = 1 21 | 22 | def HasPage(self, page): 23 | return page <= self.nbPages 24 | 25 | def GetPageInfo(self): 26 | return 1, self.nbPages, 1, self.nbPages 27 | 28 | def OnPrintPage(self, page): 29 | """ 30 | Called by printing method 31 | """ 32 | dc: DC = self.GetDC() 33 | 34 | maxX = self.canvas.getWidth() 35 | maxY = self.canvas.getHeight() 36 | 37 | # Let's have at least 50 device units margin 38 | marginX = 50 39 | marginY = 50 40 | 41 | # Add the margin to the graphic size 42 | maxX = maxX + (2 * marginX) 43 | maxY = maxY + (2 * marginY) 44 | 45 | # Get the size of the DC in pixels 46 | (w, h) = dc.GetSize() 47 | # Calculate a suitable scaling factor 48 | scaleX = float(w) / maxX 49 | scaleY = float(h) / maxY 50 | 51 | # Use x or y scaling factor, whichever fits on the DC 52 | actualScale = min(scaleX, scaleY) 53 | 54 | # Calculate the position on the DC for centering the graphic 55 | posX = (w - (self.canvas.getWidth() * actualScale)) / 2.0 56 | posY = (h - (self.canvas.getHeight() * actualScale)) / 2.0 57 | 58 | # Set the scale and origin 59 | dc.SetUserScale(actualScale, actualScale) 60 | dc.SetDeviceOrigin(int(posX), int(posY)) 61 | 62 | self.canvas.Redraw(dc) 63 | return True 64 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/CommandCreateOglActor.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import cast 3 | 4 | from logging import Logger 5 | from logging import getLogger 6 | 7 | from pyutmodelv2.PyutActor import PyutActor 8 | 9 | from ogl.OglActor import OglActor 10 | 11 | from pyut.ui.wxcommands.BaseWxCreateCommand import BaseWxCreateCommand 12 | from pyut.ui.eventengine.EventType import EventType 13 | 14 | from pyut.ui.eventengine.IEventEngine import IEventEngine 15 | 16 | 17 | class CommandCreateOglActor(BaseWxCreateCommand): 18 | 19 | def __init__(self, x: int, y: int, eventEngine: IEventEngine): 20 | 21 | super().__init__(canUndo=True, name='Create Actor', x=x, y=y, eventEngine=eventEngine) 22 | 23 | self.logger: Logger = getLogger(__name__) 24 | 25 | def _createPrototypeInstance(self) -> OglActor: 26 | """ 27 | Implement required abstract method 28 | Creates an appropriate actor for the new command 29 | 30 | Returns: The newly created actor 31 | """ 32 | pyutActor: PyutActor = PyutActor(actorName=self._oglPreferences.defaultNameActor) 33 | 34 | oglActor: OglActor = OglActor(pyutActor) 35 | 36 | return oglActor 37 | 38 | def _placeShapeOnFrame(self): 39 | """ 40 | Create new self._shape based on saved data 41 | Place self._shape on the UML frame 42 | """ 43 | oglActor: OglActor = cast(OglActor, self._shape) # get old or prototype on first use 44 | pyutActor: PyutActor = cast(PyutActor, oglActor.pyutObject) 45 | 46 | self._oglObjWidth, self._oglObjHeight = oglActor.GetSize() 47 | self._shape = OglActor(pyutActor, w=self._oglObjWidth, h=self._oglObjHeight) # create new one 48 | 49 | self._eventEngine.sendEvent(EventType.EditActor, pyutActor=pyutActor) 50 | 51 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbAddOglObjectToFrame) 52 | -------------------------------------------------------------------------------- /pyut/general/LineSplitter.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import List 3 | from typing import Tuple 4 | 5 | from wx import DC 6 | 7 | 8 | class LineSplitter: 9 | """ 10 | This class implements a text split algorithm. 11 | You provide text to the method to split and it returns 12 | a list of strings; The length of text for each string <= total width. 13 | 14 | Sample use: 15 | text: str = "Hi, how are you today ?" 16 | splitLines: List[str] = LineSplitter().split(text, dc, 12) 17 | """ 18 | 19 | def split(self, text: str, dc: DC, textWidth: int) -> List[str]: 20 | """ 21 | Split the `text` into lines that fit into `textWidth` pixels. 22 | 23 | Args: 24 | text: The text to split 25 | dc: Device Context 26 | textWidth: The width of the text in pixels 27 | 28 | Returns: 29 | A list of strings that are no wider than the input pixel `width` 30 | """ 31 | splitLines: List[str] = text.splitlines() 32 | newLines: List[str] = [] 33 | 34 | for line in splitLines: 35 | words: List[str] = line.split() 36 | lineWidth: int = 0 37 | newLine: str = "" 38 | for wordX in words: 39 | word: str = f'{wordX} ' 40 | 41 | extentSize: Tuple[int, int] = dc.GetTextExtent(word) # width, height 42 | wordWidth: int = extentSize[0] 43 | if lineWidth + wordWidth <= textWidth: 44 | newLine = f'{newLine}{word}' 45 | lineWidth += wordWidth 46 | else: 47 | newLines.append(newLine[:-1]) # remove last space 48 | newLine = word 49 | lineWidth = wordWidth 50 | 51 | newLines.append(newLine[:-1]) 52 | 53 | return newLines 54 | -------------------------------------------------------------------------------- /pyut/resources/loggingConfiguration.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "disable_existing_loggers": false, 4 | "formatters": { 5 | "simple": { 6 | "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s" 7 | }, 8 | "pyutSimple": { 9 | "format": "%(asctime)s.%(msecs)03d %(levelname)s %(module)s: %(message)s", 10 | "datefmt" : "%H:%M:%S" 11 | }, 12 | "functionNames": { 13 | "format": "%(asctime)s.%(msecs)03d %(levelname)s %(module)s.%(funcName)s() %(message)s", 14 | "datefmt" : "%H:%M:%S" 15 | }, 16 | "bestFormat": { 17 | "format": "%(asctime)s.%(msecs)03d %(levelname)-6s %(name)-15s - %(message)s", 18 | "datefmt": "%Y-%m-%d %H:%M:%S" 19 | } 20 | }, 21 | "handlers": { 22 | "consoleHandler": { 23 | "class": "logging.StreamHandler", 24 | "formatter": "pyutSimple", 25 | "stream": "ext://sys.stdout" 26 | }, 27 | "consoleHandlerFunctionNames": { 28 | "class": "logging.StreamHandler", 29 | "formatter": "functionNames", 30 | "stream": "ext://sys.stdout" 31 | }, 32 | 33 | "rotatingFileHandler": { 34 | "class": "logging.handlers.RotatingFileHandler", 35 | "formatter": "bestFormat", 36 | "filename": "/tmp/pyut.log", 37 | "mode": "a", 38 | "maxBytes": 81920, 39 | "backupCount": 5, 40 | "encoding": "utf-8" 41 | } 42 | }, 43 | "loggers": { 44 | "Pyut": { 45 | "level": "INFO", 46 | "propagate": false, 47 | "handlers": ["rotatingFileHandler"] 48 | }, 49 | "pyut": { 50 | "level": "INFO", 51 | "propagate": false, 52 | "handlers": ["rotatingFileHandler"] 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /pyut/ui/dialogs/textdialogs/DlgEditText.py: -------------------------------------------------------------------------------- 1 | 2 | from wx import EVT_TEXT 3 | from wx import TE_MULTILINE 4 | 5 | from wx import CommandEvent 6 | from wx import TextCtrl 7 | from wx import Window 8 | 9 | from wx.lib.sized_controls import SizedPanel 10 | 11 | from pyut.ui.dialogs.BaseEditDialog import BaseEditDialog 12 | 13 | from pyutmodelv2.PyutText import PyutText 14 | 15 | 16 | class DlgEditText(BaseEditDialog): 17 | """ 18 | Defines a multi-line text control dialog for placing an editable 19 | text on the UML Diagram 20 | 21 | 22 | Sample use: 23 | with DlgEditText(parent=self._frame, eventEngine=self._eventEngine, pyutText=pyutText) as dlg: 24 | 25 | if dlg.ShowModal() == OK: 26 | return f'Retrieved data: {pyutText.content=}' 27 | else: 28 | return f'Cancelled' 29 | 30 | """ 31 | def __init__(self, parent: Window, pyutText: PyutText): 32 | """ 33 | 34 | Args: 35 | parent: parent window to center on 36 | pyutText: Model object we are editing 37 | """ 38 | super().__init__(parent, title='Diagram Text') 39 | 40 | sizedPanel: SizedPanel = self.GetContentsPane() 41 | 42 | self.pyutText: PyutText = pyutText 43 | 44 | self._txtCtrl: TextCtrl = TextCtrl(sizedPanel, value=self.pyutText.content, style=TE_MULTILINE) 45 | self._txtCtrl.SetSizerProps(expand=True, proportion=1) 46 | self._txtCtrl.SetFocus() 47 | 48 | self._layoutStandardOkCancelButtonSizer() 49 | 50 | self.Bind(EVT_TEXT, self._onTextLineChange, self._txtCtrl) 51 | 52 | self.Centre() 53 | 54 | def _onTextLineChange(self, event: CommandEvent): 55 | """ 56 | Handle changes to the text in the widget identified by TXT_NOTE 57 | 58 | Args: 59 | event: 60 | """ 61 | self.pyutText.content = event.GetString() 62 | -------------------------------------------------------------------------------- /tests/pyut/ui/TestCurrentDirectoryHandler.py: -------------------------------------------------------------------------------- 1 | 2 | from unittest import TestSuite 3 | from unittest import main as unitTestMain 4 | 5 | from codeallybasic.UnitTestBase import UnitTestBase 6 | 7 | from pyut.preferences.PyutPreferences import PyutPreferences 8 | 9 | # from pyut.ui.CurrentDirectoryHandler import CurrentDirectoryHandler 10 | 11 | 12 | class TestCurrentDirectoryHandler(UnitTestBase): 13 | """ 14 | Auto generated by the one and only: 15 | Gato Malo – Humberto A. Sanchez II 16 | Generated: 18 January 2024 17 | """ 18 | 19 | @classmethod 20 | def setUpClass(cls): 21 | super().setUpClass() 22 | 23 | def setUp(self): 24 | super().setUp() 25 | 26 | self._preferences: PyutPreferences = PyutPreferences() 27 | 28 | # TODO Broken when run as part of all unit tests; Works standalone 29 | 30 | def tearDown(self): 31 | super().tearDown() 32 | 33 | # def testSingletonBehavior(self): 34 | # 35 | # instance1: CurrentDirectoryHandler = CurrentDirectoryHandler() 36 | # instance2: CurrentDirectoryHandler = CurrentDirectoryHandler() 37 | # 38 | # self.assertEqual(instance1, instance2, 'Ouch no longer a Singleton') 39 | # def testGetPreferenceValue(self): 40 | # 41 | # expectedValue: str = self._preferences.diagramsDirectory 42 | # self._logger.info(f'{expectedValue=}') 43 | # 44 | # cdh: CurrentDirectoryHandler = CurrentDirectoryHandler() 45 | # 46 | # actualValue: str = cdh.currentDirectory 47 | # 48 | # self.assertEqual(expectedValue, actualValue, 'We did not get preferences value') 49 | 50 | 51 | def suite() -> TestSuite: 52 | import unittest 53 | 54 | testSuite: TestSuite = TestSuite() 55 | 56 | testSuite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(testCaseClass=TestCurrentDirectoryHandler)) 57 | 58 | return testSuite 59 | 60 | 61 | if __name__ == '__main__': 62 | unitTestMain() 63 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/CommandCreateOglNote.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import cast 3 | 4 | from logging import Logger 5 | from logging import getLogger 6 | 7 | from pyutmodelv2.PyutNote import PyutNote 8 | 9 | from ogl.OglNote import OglNote 10 | 11 | from pyut.ui.wxcommands.BaseWxCreateCommand import BaseWxCreateCommand 12 | 13 | from pyut.ui.eventengine.EventType import EventType 14 | from pyut.ui.eventengine.IEventEngine import IEventEngine 15 | 16 | 17 | class CommandCreateOglNote(BaseWxCreateCommand): 18 | 19 | def __init__(self, x: int, y: int, eventEngine: IEventEngine): 20 | 21 | super().__init__(canUndo=True, name='Create Note', x=x, y=y, eventEngine=eventEngine) 22 | 23 | self.logger: Logger = getLogger(__name__) 24 | 25 | def _createPrototypeInstance(self) -> OglNote: 26 | """ 27 | Implement required abstract method 28 | 29 | Create a new Note 30 | 31 | Returns: the newly created OglNote 32 | """ 33 | pyutNote: PyutNote = PyutNote(content=self._oglPreferences.noteText) 34 | oglNote: OglNote = OglNote(pyutNote) 35 | 36 | return oglNote 37 | 38 | def _placeShapeOnFrame(self): 39 | """ 40 | Place self._shape on the UML frame 41 | """ 42 | # Yet another reason to re-write miniogl. I don't understand the model 43 | # stuff that it is maintaining; However, I understand I have to recreate 44 | # the visuals so Shape._views is correct 45 | oglNote: OglNote = cast(OglNote, self._shape) # get old 46 | pyutNote: PyutNote = cast(PyutNote, oglNote.pyutObject) 47 | 48 | self._oglObjWidth, self._oglObjHeight = oglNote.GetSize() 49 | self._shape = OglNote(pyutNote, w=self._oglObjWidth, h=self._oglObjHeight) # create new 50 | 51 | self._eventEngine.sendEvent(EventType.EditNote, pyutNote=pyutNote) 52 | 53 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbAddOglObjectToFrame) 54 | -------------------------------------------------------------------------------- /pyut/ui/umlframes/UmlFrameShapeHandler.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Union 3 | 4 | from logging import Logger 5 | from logging import getLogger 6 | 7 | from wx import Brush 8 | from wx import Pen 9 | from wx import Window 10 | 11 | from miniogl.DiagramFrame import DiagramFrame 12 | from miniogl.SelectAnchorPoint import SelectAnchorPoint 13 | 14 | from ogl.OglInterface2 import OglInterface2 15 | from ogl.OglAssociationLabel import OglAssociationLabel 16 | from ogl.OglObject import OglObject 17 | from ogl.OglLink import OglLink 18 | 19 | from ogl.sd.OglSDInstance import OglSDInstance 20 | 21 | from pyut.preferences.PyutPreferences import PyutPreferences 22 | 23 | 24 | class UmlFrameShapeHandler(DiagramFrame): 25 | 26 | def __init__(self, parent: Window): 27 | """ 28 | 29 | Args: 30 | parent: The window where we put the UML Diagram Frames 31 | """ 32 | 33 | super().__init__(parent) 34 | 35 | self.logger: Logger = getLogger(__name__) 36 | self._preferences: PyutPreferences = PyutPreferences() 37 | 38 | def addShape(self, shape: Union[OglObject, OglInterface2, SelectAnchorPoint, OglLink, OglAssociationLabel, OglSDInstance], 39 | x: int, y: int, pen: Pen = None, brush: Brush = None, withModelUpdate: bool = True): 40 | """ 41 | Add a shape to the UmlFrame. 42 | 43 | Args: 44 | shape: the shape to add 45 | x: coord of the center of the shape 46 | y: coord of the center of the shape 47 | pen: pen to use 48 | brush: brush to use 49 | withModelUpdate: IUf true, the model of the shape will update from the shape (view) when added to the diagram. 50 | """ 51 | shape.draggable = True 52 | shape.SetPosition(x, y) 53 | if pen is not None: 54 | shape.pen = pen 55 | if brush is not None: 56 | shape.brush = brush 57 | self._diagram.AddShape(shape, withModelUpdate) 58 | -------------------------------------------------------------------------------- /pyut/ui/menuhandlers/ToolsMenuHandler.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | from pyutplugins.PluginManager import PluginDetails 6 | from wx import CommandEvent 7 | from wx import Menu 8 | 9 | from pyutplugins.PluginManager import PluginManager 10 | from pyutplugins.IPluginAdapter import IPluginAdapter 11 | 12 | from pyut.ui.menuhandlers.BaseMenuHandler import BaseMenuHandler 13 | 14 | from pyut.ui.tools.SharedTypes import ToolboxIdMap 15 | from pyut.ui.tools.Tool import Category 16 | 17 | from pyut.ui.PluginAdapter import PluginAdapter 18 | from pyut.ui.ToolBoxHandler import ToolBoxHandler 19 | 20 | from pyut.ui.eventengine.IEventEngine import IEventEngine 21 | 22 | 23 | class ToolsMenuHandler(BaseMenuHandler): 24 | """ 25 | Handles calling Tool plugins and I/O Plugins 26 | """ 27 | def __init__(self, toolsMenu: Menu, pluginManager: PluginManager, toolboxIds: ToolboxIdMap, eventEngine: IEventEngine): 28 | 29 | super().__init__(menu=toolsMenu, eventEngine=eventEngine) 30 | 31 | self.logger: Logger = getLogger(__name__) 32 | self._pluginManager: PluginManager = pluginManager 33 | self._toolboxIds: ToolboxIdMap = toolboxIds 34 | 35 | self._pluginAdapter: IPluginAdapter = PluginAdapter(eventEngine=eventEngine) 36 | 37 | def onToolPlugin(self, event: CommandEvent): 38 | """ 39 | 40 | Args: 41 | event: 42 | """ 43 | wxId: int = event.GetId() 44 | pluginDetails: PluginDetails = self._pluginManager.doToolAction(wxId=wxId) 45 | 46 | self.logger.info(f'Import {pluginDetails=}') 47 | 48 | def onToolboxMenuClick(self, event: CommandEvent): 49 | 50 | toolBoxHandler: ToolBoxHandler = ToolBoxHandler() 51 | 52 | # self._mediator.displayToolbox(self._toolboxIds[event.GetId()]) # TODO 53 | eventId: int = event.GetId() 54 | categoryStr: str = self._toolboxIds[eventId] 55 | 56 | toolBoxHandler.displayToolbox(Category(categoryStr)) 57 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/DlgEditParameter.py: -------------------------------------------------------------------------------- 1 | 2 | from wx import CommandEvent 3 | from wx import Window 4 | 5 | from pyutmodelv2.PyutParameter import PyutParameter 6 | from pyutmodelv2.PyutType import PyutType 7 | 8 | from pyut.ui.dialogs.BaseEditParamFieldDialog import BaseEditParamFieldDialog 9 | 10 | 11 | class DlgEditParameter(BaseEditParamFieldDialog): 12 | 13 | def __init__(self, parent: Window, parameterToEdit: PyutParameter): 14 | """ 15 | The Dialog to edit PyutParameters 16 | Args: 17 | parent: 18 | parameterToEdit: The parameter that is being edited 19 | """ 20 | super().__init__(parent, title="Edit Parameter", layoutField=False) 21 | 22 | self._parameterToEdit: PyutParameter = parameterToEdit 23 | 24 | self._name.SetValue(self._parameterToEdit.name) 25 | paramType: PyutType = self._parameterToEdit.type 26 | self._type.SetValue(paramType.value) 27 | self._defaultValue.SetValue(self._convertNone(self._parameterToEdit.defaultValue)) 28 | 29 | self._name.SetFocus() 30 | # a little trick to make sure that you can't resize the dialog to 31 | # less screen space than the controls need 32 | self.Fit() 33 | self.SetMinSize(self.GetSize()) 34 | 35 | # noinspection PyUnusedLocal 36 | def _onOk (self, event: CommandEvent): 37 | """ 38 | Add additional behavior to super class method 39 | Args: 40 | event: 41 | """ 42 | 43 | nameValue: str = self._name.GetValue() 44 | if nameValue == '': 45 | self._indicateEmptyTextCtrl(self._name) 46 | return 47 | 48 | self._parameterToEdit.name = nameValue 49 | paramType: PyutType = PyutType(self._type.GetValue()) 50 | self._parameterToEdit.type = paramType 51 | if self._defaultValue.GetValue() != "": 52 | self._parameterToEdit.defaultValue = self._defaultValue.GetValue() 53 | else: 54 | self._parameterToEdit.defaultValue = '' 55 | 56 | super()._onOk(event) 57 | -------------------------------------------------------------------------------- /pyut/ui/Types.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import List 3 | from typing import NewType 4 | from typing import Tuple 5 | from typing import Union 6 | 7 | from wx import Notebook 8 | 9 | from pyut.PyutConstants import DiagramsLabels 10 | 11 | from pyut.enums.DiagramType import DiagramType 12 | 13 | from pyut.ui.umlframes.UmlClassDiagramsFrame import UmlClassDiagramsFrame 14 | from pyut.ui.umlframes.UmlSequenceDiagramsFrame import UmlSequenceDiagramsFrame 15 | from pyut.ui.eventengine.IEventEngine import IEventEngine 16 | from pyut.ui.umlframes.UmlUseCaseDiagramsFrame import UmlUseCaseDiagramsFrame 17 | 18 | UmlFrameType = Union[UmlClassDiagramsFrame, UmlSequenceDiagramsFrame, UmlUseCaseDiagramsFrame] 19 | Frames = NewType('Frames', List[UmlFrameType]) 20 | 21 | 22 | def createDiagramFrame(parentFrame: Notebook, diagramType: DiagramType, 23 | eventEngine: IEventEngine) -> Tuple[UmlFrameType, str]: 24 | """ 25 | 26 | Args: 27 | parentFrame: 28 | diagramType: 29 | eventEngine: 30 | 31 | Returns: The tuple of the new diagram frame and the default diagram name 32 | """ 33 | 34 | match diagramType: 35 | case DiagramType.CLASS_DIAGRAM: 36 | defaultDiagramName: str = DiagramsLabels[diagramType] 37 | diagramFrame: UmlFrameType = UmlClassDiagramsFrame(parentFrame, eventEngine=eventEngine) 38 | case DiagramType.SEQUENCE_DIAGRAM: 39 | defaultDiagramName = DiagramsLabels[diagramType] 40 | diagramFrame = UmlSequenceDiagramsFrame(parentFrame, eventEngine=eventEngine) 41 | case DiagramType.USECASE_DIAGRAM: 42 | defaultDiagramName = DiagramsLabels[diagramType] 43 | diagramFrame = UmlUseCaseDiagramsFrame(parentFrame, eventEngine=eventEngine) 44 | case _: 45 | print(f'Unsupported diagram type; replacing with class diagram: {diagramType}') 46 | defaultDiagramName = DiagramsLabels[DiagramType.CLASS_DIAGRAM] 47 | diagramFrame = UmlClassDiagramsFrame(parentFrame, eventEngine=eventEngine) 48 | 49 | return diagramFrame, defaultDiagramName 50 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/CommandCreateOglLink.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | from wx import Point 6 | from wx import Yield as wxYield 7 | 8 | from pyutmodelv2.enumerations.PyutLinkType import PyutLinkType 9 | 10 | from pyut.ui.wxcommands.BaseWxLinkCommand import BaseWxLinkCommand 11 | from pyut.ui.eventengine.EventType import EventType 12 | from pyut.ui.eventengine.IEventEngine import IEventEngine 13 | 14 | from pyut.ui.wxcommands.Types import DoableObjectType 15 | 16 | 17 | class CommandCreateOglLink(BaseWxLinkCommand): 18 | 19 | def __init__(self, eventEngine: IEventEngine, 20 | src: DoableObjectType, 21 | dst: DoableObjectType, 22 | linkType: PyutLinkType = PyutLinkType.INHERITANCE, 23 | srcPoint: Point = None, 24 | dstPoint: Point = None): 25 | """ 26 | 27 | Args: 28 | eventEngine: A references to the Pyut event engine 29 | src: The source UML Object 30 | dst: The destination UML Object 31 | linkType: The Link Type 32 | srcPoint: The source attachment point 33 | dstPoint: The destination attachment point 34 | """ 35 | super().__init__(partialName='Create', linkType=linkType, eventEngine=eventEngine) 36 | 37 | self.logger: Logger = getLogger(__name__) 38 | 39 | self._srcOglObject: DoableObjectType = src 40 | self._dstOglObject: DoableObjectType = dst 41 | 42 | self._srcPoint: Point = srcPoint 43 | self._dstPoint: Point = dstPoint 44 | 45 | def Do(self) -> bool: 46 | self._link = self._createLink() 47 | 48 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbPlaceLink) 49 | return True 50 | 51 | def Undo(self) -> bool: 52 | """ 53 | Returns: True to indicate the undo was done 54 | """ 55 | self.logger.info(f'Undo Create: {self._link}') 56 | 57 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbDoDeleteLink) 58 | 59 | wxYield() 60 | 61 | return True 62 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/CommandCreateOglText.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import cast 3 | 4 | from logging import Logger 5 | from logging import getLogger 6 | 7 | from pyutmodelv2.PyutText import PyutText 8 | 9 | from ogl.OglText import OglText 10 | 11 | from ogl.preferences.OglPreferences import OglPreferences 12 | 13 | from pyut.ui.wxcommands.BaseWxCreateCommand import BaseWxCreateCommand 14 | from pyut.ui.wxcommands.Types import DoableObjectType 15 | 16 | from pyut.ui.eventengine.EventType import EventType 17 | from pyut.ui.eventengine.IEventEngine import IEventEngine 18 | 19 | 20 | class CommandCreateOglText(BaseWxCreateCommand): 21 | 22 | def __init__(self, x: int, y: int, eventEngine: IEventEngine): 23 | 24 | self.logger: Logger = getLogger(__name__) 25 | 26 | super().__init__(canUndo=True, name='Create Text', x=x, y=y, eventEngine=eventEngine) 27 | 28 | def _createPrototypeInstance(self) -> DoableObjectType: 29 | 30 | preferences: OglPreferences = self._oglPreferences 31 | 32 | pyutText: PyutText = PyutText(content=preferences.textValue) 33 | 34 | oglText: OglText = OglText(pyutText) 35 | oglText.textFontFamily = preferences.textFontFamily 36 | oglText.textSize = preferences.textFontSize 37 | oglText.isBold = preferences.textBold 38 | oglText.isItalicized = preferences.textItalicize 39 | 40 | return oglText 41 | 42 | def _placeShapeOnFrame(self): 43 | 44 | oglText: OglText = cast(OglText, self._shape) # get old 45 | pyutText: PyutText = cast(PyutText, oglText.pyutObject) 46 | # 47 | # Yet another reason to re-write miniogl. I don't understand the model 48 | # stuff that it is maintaining; However, I understand I have to recreate 49 | # the visuals so Shape._views is correct 50 | self._oglObjWidth, self._oglObjHeight = oglText.GetSize() 51 | self._shape = OglText(pyutText=pyutText, width=self._oglObjWidth, height=self._oglObjHeight) # create new 52 | 53 | self._eventEngine.sendEvent(EventType.EditText, pyutText=pyutText) 54 | 55 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbAddOglObjectToFrame) 56 | -------------------------------------------------------------------------------- /tests/PreferencesEditorDemo.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import cast 3 | 4 | from wx import App 5 | from wx import Button 6 | from wx import CommandEvent 7 | from wx import DEFAULT_FRAME_STYLE 8 | from wx import EVT_BUTTON 9 | from wx import ID_ANY 10 | from wx import PreferencesEditor 11 | from wx import StaticText 12 | from wx import StockPreferencesPage 13 | from wx import Window 14 | 15 | from wx.lib.sized_controls import SizedPanel 16 | from wx.lib.sized_controls import SizedFrame 17 | 18 | 19 | class DemoPreferencesPage(StockPreferencesPage): 20 | 21 | def __init__(self): 22 | super().__init__(kind=StockPreferencesPage.Kind_General) 23 | 24 | def CreateWindow(self, parent) -> Window: 25 | 26 | sizedPanel: SizedPanel = SizedPanel(parent) 27 | # noinspection PyUnusedLocal 28 | label: StaticText = StaticText(parent=sizedPanel, id=ID_ANY, label="Preferences Go Here") 29 | 30 | return sizedPanel 31 | 32 | def GetName(self) -> str: 33 | return 'Demo Preferences' 34 | 35 | 36 | class MainFrame(SizedFrame): 37 | def __init__(self): 38 | super().__init__(parent=None, id=ID_ANY, title="Show da' Anomaly", size=(300, 200), style=DEFAULT_FRAME_STYLE) 39 | 40 | self._preferencesEditor: PreferencesEditor = cast(PreferencesEditor, None) 41 | sizedPanel: SizedPanel = self.GetContentsPane() 42 | showMeButton: Button = Button(parent=sizedPanel, label='Press Me') 43 | 44 | self.Bind(EVT_BUTTON, self._onShowMe, showMeButton) 45 | self.Fit() 46 | 47 | # noinspection PyUnusedLocal 48 | def _onShowMe(self, event: CommandEvent): 49 | print('I was pressed') 50 | preferencesEditor: PreferencesEditor = PreferencesEditor() 51 | preferencesEditor.AddPage(DemoPreferencesPage()) 52 | 53 | preferencesEditor.Show(self) 54 | 55 | print('Hasta La Vista') 56 | self._preferencesEditor = preferencesEditor 57 | 58 | 59 | class PreferencesEditorDemo(App): 60 | 61 | def OnInit(self): 62 | 63 | self._frame: MainFrame = MainFrame() 64 | 65 | self._frame.Show() 66 | return True 67 | 68 | 69 | testApp: App = PreferencesEditorDemo(redirect=False) 70 | testApp.MainLoop() 71 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/Wrappers.py: -------------------------------------------------------------------------------- 1 | from wx import CANCEL 2 | from wx import CENTER 3 | from wx import OK 4 | 5 | from wx import TextEntryDialog 6 | from wx import Window 7 | 8 | 9 | class DlgEditUseCase(TextEntryDialog): 10 | """ 11 | Syntactic sugar around a text entry dialog specifically for 12 | editing a use case name 13 | Usage: 14 | 15 | with DlgEditUseCase(umlFrame, useCaseName=pyutUseCase.name) as dlg: 16 | if dlg.ShowModal() == ID_OK: 17 | pyutUseCase.name = dlg.GetValue() 18 | """ 19 | def __init__(self, parent: Window, useCaseName: str): 20 | super().__init__(parent, message="Use Case Name", caption="Edit Use Case Name", value=useCaseName, style=OK | CANCEL | CENTER) 21 | 22 | 23 | class DlgEditActor(TextEntryDialog): 24 | """ 25 | Syntactic sugar around a text entry dialog specifically for 26 | editing an actor's name 27 | Usage: 28 | 29 | with DlgEditActor(umlFrame, useCaseName=pyutActor.name) as dlg: 30 | if dlg.ShowModal() == ID_OK: 31 | pyutActor.name = dlg.GetValue() 32 | """ 33 | def __init__(self, parent: Window, actorName: str): 34 | super().__init__(parent, message="Actor Name", caption="Edit Actor Name", value=actorName, style=OK | CANCEL | CENTER) 35 | 36 | 37 | class DlgEditDiagramTitle(TextEntryDialog): 38 | """ 39 | Syntactic sugar around a text entry dialog specifically for 40 | editing a diagram's name 41 | Usage: 42 | 43 | with DlgEditDiagramTitle(umlFrame, diagramTitle=diagram.title) as dlg: 44 | if dlg.ShowModal() == ID_OK: 45 | diagram.title = dlg.GetValue() 46 | """ 47 | def __init__(self, parent: Window, diagramTitle: str): 48 | super().__init__(parent, message='Diagram Title', caption='Edit Diagram Title', value=diagramTitle, style=OK | CANCEL | CENTER) 49 | 50 | 51 | class DlgEditSDInstanceName(TextEntryDialog): 52 | def __init__(self, parent: Window, instanceName: str): 53 | super().__init__(parent, "Instance", "Enter instance name", instanceName, OK | CANCEL | CENTER) 54 | 55 | 56 | class DlgEditSDMessage(TextEntryDialog): 57 | def __init__(self, parent: Window, messageName: str): 58 | super().__init__(parent, 'Message', 'Enter message name', messageName, OK | CANCEL | CENTER) 59 | -------------------------------------------------------------------------------- /tests/pyut/ui/eventengine/TestInspector.py: -------------------------------------------------------------------------------- 1 | 2 | from unittest import TestSuite 3 | from unittest import main as unitTestMain 4 | 5 | from codeallybasic.UnitTestBase import UnitTestBase 6 | 7 | from pyut.ui.eventengine.inspector.Inspector import Inspector 8 | 9 | 10 | class Class1: 11 | def __init__(self): 12 | self._calledBy: str = '' 13 | 14 | def method1(self): 15 | print('method1 called') 16 | self._calledBy = Inspector.getCallerName() 17 | 18 | @property 19 | def calledBy(self) -> str: 20 | return self._calledBy 21 | 22 | 23 | class Class2: 24 | def __init__(self): 25 | self._class1: Class1 = Class1() 26 | 27 | def method2(self): 28 | self._class1.method1() 29 | 30 | @property 31 | def class1(self) -> Class1: 32 | return self._class1 33 | 34 | 35 | class TestInspector(UnitTestBase): 36 | """ 37 | Auto generated by the one and only: 38 | Gato Malo – Humberto A. Sanchez II 39 | Generated: 16 December 2024 40 | """ 41 | 42 | @classmethod 43 | def setUpClass(cls): 44 | super().setUpClass() 45 | 46 | def setUp(self): 47 | super().setUp() 48 | 49 | def tearDown(self): 50 | super().tearDown() 51 | 52 | def testBasicInspection(self): 53 | class2: Class2 = Class2() 54 | 55 | class2.method2() 56 | 57 | # noinspection SpellCheckingInspection 58 | expectedCaller: str = 'tests.pyut.ui.eventengine.TestInspector.Class2.method2' 59 | self.assertEqual(expectedCaller, class2.class1.calledBy, 'Incorrect') 60 | 61 | def testJustClassMethodName(self): 62 | 63 | fullyQualifiedName: str = 'pyut.ui.main.PyutApplicationFrame.PyutApplicationFrame._initialize' 64 | expectedShortName: str = 'PyutApplicationFrame._initialize' 65 | 66 | actualShortName: str = Inspector.justClassMethodName(fullyQualifiedName) 67 | 68 | self.assertEqual(expectedShortName, actualShortName, 'Not correctly shortened') 69 | 70 | 71 | def suite() -> TestSuite: 72 | import unittest 73 | 74 | testSuite: TestSuite = TestSuite() 75 | 76 | testSuite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(testCaseClass=TestInspector)) 77 | 78 | return testSuite 79 | 80 | 81 | if __name__ == '__main__': 82 | unitTestMain() 83 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/CommandDeleteOglLink.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import cast 3 | 4 | from logging import Logger 5 | from logging import getLogger 6 | 7 | from wx import Point 8 | from wx import Yield as wxYield 9 | 10 | from pyutmodelv2.enumerations.PyutLinkType import PyutLinkType 11 | 12 | from ogl.OglLink import OglLink 13 | from ogl.OglAssociation import OglAssociation 14 | 15 | from pyut.ui.wxcommands.BaseWxLinkCommand import BaseWxLinkCommand 16 | from pyut.ui.eventengine.EventType import EventType 17 | 18 | from pyut.ui.eventengine.IEventEngine import IEventEngine 19 | 20 | 21 | class CommandDeleteOglLink(BaseWxLinkCommand): 22 | 23 | def __init__(self, oglLink: OglLink, eventEngine: IEventEngine): 24 | 25 | self._linkType: PyutLinkType = oglLink.pyutObject.linkType 26 | 27 | super().__init__(partialName='Delete', linkType=self._linkType, eventEngine=eventEngine) 28 | 29 | self._delLinkLogger: Logger = getLogger(__name__) 30 | 31 | self._srcOglObject = oglLink.sourceShape 32 | self._dstOglObject = oglLink.destinationShape 33 | 34 | sourceAnchorPoint = oglLink.sourceAnchor 35 | destinationAnchorPoint = oglLink.destinationAnchor 36 | 37 | srcX, srcY = sourceAnchorPoint.GetPosition() 38 | dstX, dstY = destinationAnchorPoint.GetPosition() 39 | self._srcPoint = Point(x=srcX, y=srcY) 40 | self._dstPoint = Point(x=dstX, y=dstY) 41 | 42 | self._controlPoints = oglLink.GetControlPoints() # in case we have bends 43 | self._spline = oglLink.spline 44 | 45 | self._link = oglLink # Save the link to delete 46 | if isinstance(self._link, OglAssociation): 47 | oglAssociation: OglAssociation = cast(OglAssociation, self._link) 48 | 49 | self._pyutLink = oglAssociation.pyutObject 50 | 51 | def Do(self) -> bool: 52 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbDoDeleteLink) 53 | wxYield() 54 | 55 | return True 56 | 57 | def Undo(self) -> bool: 58 | 59 | self._link = self._createLink() 60 | 61 | self._delLinkLogger.info(f'Undo Delete: {self._link.__repr__()}') 62 | 63 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbPlaceLink) 64 | wxYield() 65 | 66 | return True 67 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/DlgEditField.py: -------------------------------------------------------------------------------- 1 | 2 | from wx import CommandEvent 3 | from wx import Window 4 | 5 | from pyutmodelv2.PyutField import PyutField 6 | 7 | from pyutmodelv2.PyutType import PyutType 8 | 9 | from pyutmodelv2.enumerations.PyutVisibility import PyutVisibility 10 | 11 | from pyut.ui.dialogs.BaseEditParamFieldDialog import BaseEditParamFieldDialog 12 | 13 | 14 | class DlgEditField(BaseEditParamFieldDialog): 15 | 16 | def __init__(self, parent: Window, fieldToEdit: PyutField): 17 | super().__init__(parent, title='Edit Field', layoutField=True) 18 | """ 19 | The Dialog to edit PyutFields 20 | Args: 21 | parent: 22 | fieldToEdit: The parameter that is being edited 23 | """ 24 | self._fieldToEdit: PyutField = fieldToEdit 25 | 26 | self._name.SetValue(self._fieldToEdit.name) 27 | self._type.SetValue(str(self._fieldToEdit.type)) 28 | self._defaultValue.SetValue(self._convertNone(self._fieldToEdit.defaultValue)) 29 | self._rdbVisibility.SetStringSelection(str(self._fieldToEdit.visibility)) 30 | 31 | self._name.SetFocus() 32 | # a little trick to make sure that you can't resize the dialog to 33 | # less screen space than the controls need 34 | self.Fit() 35 | self.SetMinSize(self.GetSize()) 36 | 37 | # noinspection PyUnusedLocal 38 | def _onOk (self, event: CommandEvent): 39 | """ 40 | Add additional behavior to super class method 41 | Args: 42 | event: Associated event 43 | """ 44 | nameValue: str = self._name.GetValue().strip() 45 | if nameValue == '': 46 | self._indicateEmptyTextCtrl(self._name) 47 | return # will not end modal dialog 48 | 49 | self._fieldToEdit.name = nameValue 50 | 51 | self._fieldToEdit.type = PyutType(self._type.GetValue().strip()) 52 | visStr: str = self._rdbVisibility.GetStringSelection() 53 | vis: PyutVisibility = PyutVisibility.toEnum(visStr) 54 | self._fieldToEdit.visibility = vis 55 | 56 | if self._defaultValue.GetValue().strip() != "": 57 | self._fieldToEdit.defaultValue = self._defaultValue.GetValue().strip() 58 | else: 59 | self._fieldToEdit.defaultValue = '' 60 | 61 | super()._onOk(event) 62 | -------------------------------------------------------------------------------- /pyut/errorcontroller/GraphicErrorView.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | from wx import CENTRE 6 | from wx import ICON_ERROR 7 | from wx import ICON_EXCLAMATION 8 | from wx import ICON_INFORMATION 9 | from wx import MessageDialog 10 | from wx import OK 11 | 12 | from pyut.errorcontroller.IErrorView import IErrorView 13 | 14 | 15 | class GraphicErrorView(IErrorView): 16 | """ 17 | This class is an error view which will display errors as 18 | wx message dialogs. 19 | """ 20 | def __init__(self): 21 | 22 | super().__init__() 23 | self.logger: Logger = getLogger(__name__) 24 | 25 | def displayFatalError(self, msg: str, title=None, parent=None): 26 | 27 | from pyut.errorcontroller.ErrorManager import ErrorManager # Avoid cyclical dependency 28 | 29 | if title is None: 30 | title = 'An error occurred...' 31 | 32 | errMsg: str = msg + "\n\n" 33 | errorInfo: str = ErrorManager.getErrorInfo() 34 | if errorInfo is not None: 35 | errMsg = f'{errMsg}{ErrorManager.getErrorInfo()}' 36 | try: 37 | dlg = MessageDialog(parent, errMsg, title, OK | ICON_ERROR | CENTRE) 38 | dlg.ShowModal() 39 | dlg.Destroy() 40 | except (ValueError, Exception) as e: 41 | self.logger.error(f'newFatalError: {e}') 42 | 43 | def displayWarning(self, msg: str, title=None, parent=None): 44 | 45 | if title is None: 46 | title = 'WARNING...' 47 | try: 48 | dlg = MessageDialog(parent, msg, title, OK | ICON_EXCLAMATION | CENTRE) 49 | dlg.ShowModal() 50 | dlg.Destroy() 51 | except (ValueError, Exception) as e: 52 | self.logger.error(f'newWarning: {e}') 53 | 54 | def newInformation(self, msg: str, title=None, parent=None): 55 | 56 | if title is None: 57 | title = 'INFORMATION...' 58 | try: 59 | dlg = MessageDialog(parent, msg, title, OK | ICON_INFORMATION | CENTRE) 60 | dlg.ShowModal() 61 | dlg.Destroy() 62 | 63 | except (ValueError, Exception) as e: 64 | self.logger.error(f'newInformation: {e}') 65 | 66 | def displayInformation(self, msg: str, title=None, parent=None): 67 | self.newInformation(msg=msg, title=title, parent=parent) 68 | -------------------------------------------------------------------------------- /pyut/ui/tools/ToolboxOwner.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import cast 3 | 4 | from logging import Logger 5 | from logging import getLogger 6 | 7 | from wx import Window 8 | 9 | from pyut.ui.tools.Tool import Category 10 | from pyut.ui.tools.Tool import Tool 11 | from pyut.ui.tools.Toolbox import Toolbox 12 | 13 | from pyut.ui.tools.ToolboxTypes import CategoryNames 14 | from pyut.ui.tools.ToolboxTypes import ToolCategories 15 | from pyut.ui.tools.ToolboxTypes import Tools 16 | 17 | 18 | class ToolboxOwner: 19 | 20 | """ 21 | ToolboxOwner : a toolbox owner 22 | """ 23 | 24 | def __init__(self, parent: Window): 25 | """ 26 | 27 | Args: 28 | parent: The parent window 29 | """ 30 | self._toolCategories: ToolCategories = cast(ToolCategories, {}) 31 | self._parent: Window = parent 32 | 33 | self.logger: Logger = getLogger(__name__) 34 | 35 | def displayToolbox(self, category: Category): 36 | """ 37 | Display a toolbox 38 | TODO: Don't redisplay toolbox if we have already done so 39 | 40 | Args: 41 | category: Category of tools to display 42 | """ 43 | toolbox = Toolbox(self._parent, self) 44 | toolbox.setCategory(category) 45 | toolbox.Show(True) 46 | 47 | def registerTool(self, tool: Tool): 48 | """ 49 | Add a tool to toolboxes 50 | 51 | Args: 52 | tool: The tool to add 53 | """ 54 | 55 | if tool.category not in self._toolCategories: 56 | self._toolCategories[tool.category] = Tools([tool]) 57 | self.logger.debug(f'Creating tool category: {tool.category}') 58 | else: 59 | self._toolCategories[tool.category].append(tool) 60 | 61 | def getCategoryTools(self, category: Category) -> Tools: 62 | """ 63 | 64 | Args: 65 | category: the category of tools to get 66 | 67 | Returns: all tools for a specified category 68 | """ 69 | toolsOfCategory: Tools = self._toolCategories[category] 70 | return toolsOfCategory 71 | 72 | def getCategories(self) -> CategoryNames: 73 | """ 74 | Return all tool category names 75 | 76 | Returns: The category names 77 | """ 78 | categoryNames: CategoryNames = cast(CategoryNames, list(self._toolCategories.keys())) 79 | return categoryNames 80 | -------------------------------------------------------------------------------- /pyut/ui/tools/SharedIdentifiers.py: -------------------------------------------------------------------------------- 1 | 2 | from pyut.PyutUtils import PyutUtils 3 | 4 | from pyut.ui.Action import Action 5 | 6 | 7 | class SharedIdentifiers: 8 | 9 | [ 10 | ID_MENU_FILE_NEW_PROJECT, 11 | ID_MENU_FILE_IMPORT, ID_MENU_FILE_EXPORT, 12 | ID_MENU_FILE, ID_MENU_FILE_DIAGRAM_PROPERTIES, 13 | ID_MENU_FILE_PRINT_SETUP, ID_MENU_FILE_PRINT_PREVIEW, ID_MENU_FILE_PRINT, ID_MENU_FILE_MANAGE_FILE_HISTORY, 14 | ID_MENU_FILE_PROJECT_CLOSE, 15 | ID_MENU_FILE_NEW_CLASS_DIAGRAM, ID_MENU_FILE_NEW_SEQUENCE_DIAGRAM, 16 | ID_MENU_FILE_NEW_USECASE_DIAGRAM, 17 | ID_MENU_FILE_INSERT_PROJECT, 18 | ID_MENU_FILE_REMOVE_DIAGRAM, 19 | 20 | ID_MENU_EDIT_ADD_PYUT_DIAGRAM, ID_MENU_EDIT_ADD_OGL_DIAGRAM, 21 | ID_MENU_GRAPHIC_ERROR_VIEW, ID_MENU_TEXT_ERROR_VIEW, ID_MENU_RAISE_ERROR_VIEW, 22 | ID_MENU_EDIT_SHOW_TOOLBAR, 23 | 24 | ID_MENU_HELP_VERSION, ID_MENU_HELP_WEB, ID_MENU_HELP_LOGGING_CONTROL, ID_MENU_HELP_DEBUG_EVENT_ENGINE, 25 | 26 | ID_SD_INSTANCE, ID_SD_MESSAGE, 27 | ID_ARROW, ID_CLASS, 28 | ID_NOTE, ID_ACTOR, 29 | ID_USECASE, ID_REL_NOTE, ID_TEXT, 30 | ID_RELATIONSHIP_INHERITANCE, ID_RELATIONSHIP_REALIZATION, ID_RELATIONSHIP_COMPOSITION, ID_RELATIONSHIP_AGGREGATION, ID_RELATIONSHIP_ASSOCIATION, 31 | ID_ZOOM_IN, ID_ZOOM_OUT, ID_ZOOM_VALUE, 32 | ] = PyutUtils.assignID(42) 33 | 34 | ACTIONS = { 35 | ID_ARROW: Action.SELECTOR, 36 | ID_CLASS: Action.NEW_CLASS, 37 | ID_NOTE: Action.NEW_NOTE, 38 | ID_RELATIONSHIP_INHERITANCE: Action.NEW_INHERIT_LINK, 39 | ID_RELATIONSHIP_REALIZATION: Action.NEW_IMPLEMENT_LINK, 40 | 41 | ID_RELATIONSHIP_COMPOSITION: Action.NEW_COMPOSITION_LINK, 42 | ID_RELATIONSHIP_AGGREGATION: Action.NEW_AGGREGATION_LINK, 43 | ID_RELATIONSHIP_ASSOCIATION: Action.NEW_ASSOCIATION_LINK, 44 | ID_REL_NOTE: Action.NEW_NOTE_LINK, 45 | ID_ACTOR: Action.NEW_ACTOR, 46 | ID_TEXT: Action.NEW_TEXT, 47 | ID_USECASE: Action.NEW_USECASE, 48 | ID_SD_INSTANCE: Action.NEW_SD_INSTANCE, 49 | ID_SD_MESSAGE: Action.NEW_SD_MESSAGE, 50 | ID_ZOOM_IN: Action.ZOOM_IN, 51 | ID_ZOOM_OUT: Action.ZOOM_OUT, 52 | } 53 | -------------------------------------------------------------------------------- /pyut/general/Version.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Dict 3 | from typing import NewType 4 | 5 | from logging import Logger 6 | from logging import getLogger 7 | 8 | from platform import platform as osPlatform 9 | 10 | from sys import version as pythonVersion 11 | 12 | from wx import __version__ as wxVersion 13 | 14 | from codeallybasic.SingletonV3 import SingletonV3 15 | 16 | from pyutmodelv2 import __version__ as dataModelVersion 17 | 18 | from ogl import __version__ as oglVersion 19 | 20 | # noinspection PyPackageRequirements 21 | from untanglepyut import __version__ as untanglePyutVersion 22 | from oglio import __version__ as oglioVersion 23 | from pyutplugins import __version__ as pyutPluginsVersion 24 | 25 | from pyut import __version__ 26 | 27 | PackageName = NewType('PackageName', str) 28 | PackageVersion = NewType('PackageVersion', str) 29 | 30 | PackageVersionsMap = NewType('PackageVersionsMap', Dict[PackageName, PackageVersion]) 31 | 32 | 33 | class Version(metaclass=SingletonV3): 34 | 35 | __appName__: str = 'Pyut' 36 | 37 | __longVersion__: str = "Python UML Diagrammer" 38 | __website__: str = 'https://github.com/hasii2011/pyut/wiki' 39 | 40 | def __init__(self): 41 | 42 | self.logger: Logger = getLogger(__name__) 43 | 44 | @property 45 | def platform(self) -> str: 46 | return osPlatform(terse=True) 47 | 48 | @property 49 | def applicationName(self) -> str: 50 | return Version.__appName__ 51 | 52 | @property 53 | def applicationVersion(self) -> str: 54 | return __version__ 55 | 56 | @property 57 | def applicationLongVersion(self) -> str: 58 | return self.__longVersion__ 59 | 60 | @property 61 | def applicationWebSite(self) -> str: 62 | return self.__website__ 63 | 64 | @property 65 | def pythonVersion(self) -> str: 66 | return pythonVersion.split(" ")[0] 67 | 68 | @property 69 | def wxPythonVersion(self) -> str: 70 | return wxVersion 71 | 72 | @property 73 | def pyutModelVersion(self) -> str: 74 | return dataModelVersion 75 | 76 | @property 77 | def oglVersion(self) -> str: 78 | return oglVersion 79 | 80 | @property 81 | def untanglePyutVersion(self) -> str: 82 | return untanglePyutVersion 83 | 84 | @property 85 | def oglioVersion(self) -> str: 86 | return oglioVersion 87 | 88 | @property 89 | def pyutPluginsVersion(self) -> str: 90 | return pyutPluginsVersion 91 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/CommandDeleteOglClass.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | from typing import TYPE_CHECKING 5 | from typing import cast 6 | 7 | from wx import Yield as wxYield 8 | 9 | from pyutmodelv2.PyutClass import PyutClass 10 | 11 | from ogl.OglClass import OglClass 12 | 13 | from pyut.ui.wxcommands.BaseWxDeleteCommand import BaseWxDeleteCommand 14 | from pyut.ui.eventengine.EventType import EventType 15 | 16 | from pyut.ui.eventengine.IEventEngine import IEventEngine 17 | 18 | if TYPE_CHECKING: 19 | from pyut.ui.umlframes.UmlDiagramsFrame import UmlDiagramsFrame 20 | 21 | 22 | class CommandDeleteOglClass(BaseWxDeleteCommand): 23 | 24 | def __init__(self, oglClass: OglClass, eventEngine: IEventEngine): 25 | 26 | super().__init__(name='Delete Ogl Class', doableObject=oglClass, eventEngine=eventEngine) 27 | 28 | self.logger: Logger = getLogger(__name__) 29 | 30 | self._pyutClass: PyutClass = oglClass.pyutObject 31 | 32 | def Do(self) -> bool: 33 | """ 34 | Do special delete behavior for a class 35 | """ 36 | 37 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbOglClassDelete) 38 | 39 | return True 40 | 41 | def Undo(self) -> bool: 42 | 43 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbOglClassDeleteUndo) 44 | wxYield() 45 | return True 46 | 47 | def _cbOglClassDelete(self, frame: 'UmlDiagramsFrame'): 48 | 49 | from pyut.ui.umlframes.UmlDiagramsFrame import UmlDiagramsFrame 50 | 51 | umlFrame: UmlDiagramsFrame = frame 52 | 53 | self._removeOglObjectFromFrame(umlFrame=umlFrame, oglObject=self._objectToDelete, pyutClass=self._pyutClass) 54 | 55 | def _cbOglClassDeleteUndo(self, frame: 'UmlDiagramsFrame'): 56 | 57 | from pyut.ui.umlframes.UmlDiagramsFrame import UmlDiagramsFrame 58 | 59 | umlFrame: UmlDiagramsFrame = frame 60 | 61 | oglClass: OglClass = cast(OglClass, self._objectToDelete) # get old 62 | 63 | self._oglObjWidth, self._oglObjHeight = oglClass.GetSize() 64 | 65 | self._objectToDelete = OglClass(self._pyutClass, w=self._oglObjWidth, h=self._oglObjHeight) # create new 66 | 67 | self._addOglClassToFrame(umlFrame=umlFrame, oglClass=self._objectToDelete, x=self._oglObjX, y=self._oglObjY) 68 | 69 | self.logger.info(f'Undo delete of {self._objectToDelete}') 70 | umlFrame.Refresh() 71 | -------------------------------------------------------------------------------- /pyut/ui/menuhandlers/PyutFileDropTarget.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import List 3 | from typing import NewType 4 | 5 | from logging import INFO 6 | from logging import Logger 7 | from logging import getLogger 8 | 9 | from wx import ICON_ERROR 10 | from wx import OK 11 | 12 | from wx import FileDropTarget 13 | from wx import MessageDialog 14 | 15 | from wx import Yield as wxYield 16 | 17 | from pyut.PyutConstants import PyutConstants 18 | 19 | from pyut.ui.eventengine.EventType import EventType 20 | from pyut.ui.eventengine.IEventEngine import IEventEngine 21 | 22 | FileNames = NewType('FileNames', List[str]) 23 | 24 | 25 | class PyutFileDropTarget(FileDropTarget): 26 | 27 | def __init__(self, eventEngine: IEventEngine): 28 | 29 | super().__init__() 30 | 31 | self.logger: Logger = getLogger(__name__) 32 | self.logger.setLevel(INFO) 33 | 34 | self._eventEngine: IEventEngine = eventEngine 35 | 36 | def OnDropFiles(self, x: int, y: int, filenames: FileNames) -> bool: 37 | """ 38 | 39 | Args: 40 | x: abscissa of dropped files 41 | y: ordinate of dropped files 42 | filenames: List of strings which are the full path names of the dropped files 43 | """ 44 | 45 | badFileNameList: FileNames = FileNames([]) 46 | fileNameList: FileNames = FileNames([]) 47 | 48 | for fileName in filenames: 49 | self.logger.info(f'You dropped: {fileName}') 50 | if fileName.endswith(PyutConstants.PYUT_EXTENSION) or fileName.endswith(PyutConstants.XML_EXTENSION): 51 | fileNameList.append(fileName) 52 | else: 53 | badFileNameList.append(fileName) 54 | 55 | self._loadFiles(fileNameList) 56 | 57 | if len(badFileNameList) > 0: 58 | message: str = 'Only .put and .xml files are supported' 59 | caption: str = 'Unsupported File' 60 | booBoo: MessageDialog = MessageDialog(parent=None, message=message, caption=caption, style=OK | ICON_ERROR) 61 | booBoo.ShowModal() 62 | 63 | return True 64 | 65 | def _loadFiles(self, filenames: FileNames): 66 | 67 | for filename in filenames: 68 | wxYield() 69 | self._eventEngine.sendEvent(EventType.OpenProject, projectFilename=filename) 70 | 71 | return True 72 | 73 | def _displayError(self, message: str): 74 | 75 | booBoo: MessageDialog = MessageDialog(parent=None, message=message, caption='Bad Drop File', style=OK | ICON_ERROR) 76 | booBoo.ShowModal() 77 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | 2 | from setuptools import setup 3 | from setuptools import find_packages 4 | 5 | import pathlib 6 | 7 | from pyut import __version__ 8 | 9 | # The directory containing this file 10 | HERE = pathlib.Path(__file__).parent 11 | 12 | APP = ['pyut/Pyut.py'] 13 | DATA_FILES = [('pyut/resources', ['pyut/resources/loggingConfiguration.json']), 14 | ('pyut/resources', ['pyut/resources/Kilroy-Pyut.txt']), 15 | ('pyut/resources', ['pyut/resources/Help.txt']), 16 | ('pyut/resources', ['pyut/resources/tips.txt']), 17 | ('pyut/resources/img', ['pyut/resources/img/pyut.ico']), 18 | ] 19 | OPTIONS = {} 20 | 21 | # The text of the README file 22 | README = (HERE / "README.md").read_text() 23 | LICENSE = (HERE / 'LICENSE').read_text() 24 | 25 | setup( 26 | name='Pyut', 27 | version=__version__, 28 | app=APP, 29 | data_files=DATA_FILES, 30 | packages=find_packages(include=['pyut.*']), 31 | include_package_data=True, 32 | zip_safe=False, 33 | 34 | url='https://github.com/hasii2011/pyut', 35 | author='Humberto A. Sanchez II', 36 | author_email='Humberto.A.Sanchez.II@gmail.com', 37 | maintainer='Humberto A. Sanchez II', 38 | maintainer_email='humberto.a.sanchez.ii@gmail.com', 39 | description='The Python UML Tool', 40 | long_description='A UML Diagrammer with plugin support and reverse engineering capabilities.', 41 | options=dict(py2app=dict( 42 | plist=dict( 43 | NSRequiresAquaSystemAppearance='False', 44 | CFBundleGetInfoString='Edits Pyut UML Files', 45 | CFBundleIdentifier='pyut', 46 | CFBundleShortVersionString=__version__, 47 | CFBundleDocumentTypes=[ 48 | {'CFBundleTypeName': 'pyut'}, 49 | {'CFBundleTypeRole': 'Editor'}, 50 | {'CFBundleTypeExtensions': ['put', 'xml']} 51 | ], 52 | LSMinimumSystemVersion='12', 53 | LSEnvironment=dict( 54 | APP_MODE='True', 55 | PYTHONOPTIMIZE='1', 56 | ), 57 | LSMultipleInstancesProhibited='True', 58 | ) 59 | ), 60 | ), 61 | setup_requires=['py2app'], 62 | install_requires=[ 63 | 'codeallybasic>=1.10.0', 64 | 'codeallyadvanced>=1.4.2', 65 | 'pyutmodelv2>=2.2.3', 66 | 'ogl>=3.6.7', 67 | 'oglio>=2.4.0', 68 | 'pyutplugins>=3.2.6', 69 | 'semantic-version==2.10.0', 70 | 'PyGithub==2.6.1', 71 | 'wxPython==4.2.2', 72 | 'chardet==5.2.0' 73 | ] 74 | ) 75 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/BaseWxDeleteCommand.py: -------------------------------------------------------------------------------- 1 | from logging import Logger 2 | from logging import getLogger 3 | from typing import TYPE_CHECKING 4 | 5 | from wx import Yield as wxYield 6 | 7 | from pyut.ui.wxcommands.BaseWxCommand import BaseWxCommand 8 | from pyut.ui.wxcommands.Types import DoableObjectType 9 | 10 | from pyut.ui.eventengine.EventType import EventType 11 | from pyut.ui.eventengine.IEventEngine import IEventEngine 12 | 13 | if TYPE_CHECKING: 14 | from pyut.ui.umlframes.UmlDiagramsFrame import UmlDiagramsFrame 15 | 16 | 17 | class BaseWxDeleteCommand(BaseWxCommand): 18 | 19 | def __init__(self, name: str, doableObject: DoableObjectType, eventEngine: IEventEngine): 20 | 21 | self._name: str = name 22 | 23 | self._bWxDeleteLogger: Logger = getLogger(__name__) 24 | self._objectToDelete: DoableObjectType = doableObject 25 | self._eventEngine: IEventEngine = eventEngine 26 | 27 | super().__init__(canUndo=True, name=self._name) 28 | 29 | w, h = self._objectToDelete.GetSize() 30 | x, y = self._objectToDelete.GetPosition() 31 | self._oglObjWidth: int = w 32 | self._oglObjHeight: int = h 33 | self._oglObjX: int = x 34 | self._oglObjY: int = y 35 | 36 | def GetName(self) -> str: 37 | """ 38 | Returns the command name. 39 | """ 40 | return self._name 41 | 42 | def CanUndo(self) -> bool: 43 | """ 44 | Returns true if the command can be undone, false otherwise. 45 | """ 46 | return True 47 | 48 | def Do(self) -> bool: 49 | """ 50 | Override this member method to execute the appropriate action when called. 51 | """ 52 | 53 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbGetActiveUmlFrameForDelete) 54 | 55 | wxYield() 56 | return True 57 | 58 | def _cbGetActiveUmlFrameForDelete(self, frame: 'UmlDiagramsFrame'): 59 | 60 | from pyut.ui.umlframes.UmlDiagramsFrame import UmlDiagramsFrame 61 | 62 | self._bWxDeleteLogger.info(f'{self._objectToDelete} deleted') 63 | self._objectToDelete.Detach() 64 | 65 | umlFrame: UmlDiagramsFrame = frame 66 | umlFrame.Refresh() 67 | 68 | def _cbGetActiveUmlFrameForUndoDelete(self, frame: 'UmlDiagramsFrame'): 69 | 70 | from pyut.ui.umlframes.UmlDiagramsFrame import UmlDiagramsFrame 71 | 72 | umlFrame: UmlDiagramsFrame = frame 73 | umlFrame.addShape(self._objectToDelete, x=self._oglObjX, y=self._oglObjY) 74 | umlFrame.Refresh() 75 | -------------------------------------------------------------------------------- /pyut/ui/ToolBoxHandler.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import List 3 | from typing import cast 4 | 5 | from logging import Logger 6 | from logging import getLogger 7 | 8 | from wx import Frame 9 | from wx import ToolBar 10 | 11 | from wx import NewIdRef as wxNewIdRef 12 | 13 | 14 | from codeallybasic.SingletonV3 import SingletonV3 15 | 16 | from pyut.ui.tools.Tool import Category 17 | from pyut.ui.tools.Tool import Tool 18 | from pyut.ui.tools.ToolboxOwner import ToolboxOwner 19 | from pyut.ui.tools.ToolboxTypes import CategoryNames 20 | 21 | 22 | PARAMETER_FRAME: str = 'frame' 23 | 24 | 25 | class ToolBoxHandler(metaclass=SingletonV3): 26 | 27 | def __init__(self, **kwargs): 28 | 29 | self.logger: Logger = getLogger(__name__) 30 | 31 | assert PARAMETER_FRAME in kwargs, 'We need a frame to initialize ourselves' 32 | frame: Frame = kwargs[PARAMETER_FRAME] 33 | 34 | self._toolboxOwner: ToolboxOwner = ToolboxOwner(parent=frame) 35 | 36 | self._toolBar: ToolBar = cast(ToolBar, None) 37 | 38 | self.logger.debug('Executed ToolBoxHandler constructor') 39 | 40 | def _setToolBar(self, tb: ToolBar): 41 | """ 42 | Register the toolbar. 43 | 44 | Args: 45 | tb: The toolbar 46 | """ 47 | self._toolBar = tb 48 | 49 | def _setToolBarTools(self, tools: List[wxNewIdRef]): 50 | """ 51 | Register the toolbar tools. 52 | 53 | Args: 54 | tools: a list of the tool IDs 55 | """ 56 | self._tools = tools 57 | 58 | @property 59 | def toolBoxCategoryNames(self) -> CategoryNames: 60 | """ 61 | Return all toolbox category names 62 | 63 | Returns: The category names 64 | """ 65 | return self._toolboxOwner.getCategories() 66 | 67 | # noinspection PyTypeChecker 68 | toolBar = property(fget=None, fset=_setToolBar) 69 | # noinspection PyTypeChecker 70 | toolBarTools = property(fget=None, fset=_setToolBarTools) 71 | 72 | def addTool(self, tool: Tool): 73 | """ 74 | Add a tool to a toolbox 75 | 76 | Args: 77 | tool: The tool to add 78 | 79 | """ 80 | self._toolboxOwner.registerTool(tool) 81 | 82 | def displayToolbox(self, category: Category): 83 | """ 84 | Display a toolbox 85 | 86 | Args: 87 | category: The tool category to display 88 | """ 89 | self._toolboxOwner.displayToolbox(category) 90 | 91 | def __repr__(self) -> str: 92 | return f'<{self.__class__.__name__} at {hex(id(self))}>' 93 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/tips/TipHandler.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import NewType 3 | from typing import TextIO 4 | from typing import List 5 | from typing import cast 6 | 7 | from logging import Logger 8 | from logging import getLogger 9 | 10 | from os import linesep as osLineSep 11 | 12 | from pyut.preferences.PyutPreferences import PyutPreferences 13 | 14 | TipLinesType = NewType('TipLinesType', List[str]) 15 | 16 | 17 | class TipHandler: 18 | """ 19 | And I expect a 20% gratuity regardless of the kind of service I give you, if any 20 | """ 21 | def __init__(self, fqFileName: str): 22 | """ 23 | 24 | Args: 25 | fqFileName: Fully qualified filename 26 | """ 27 | self.logger: Logger = getLogger(__name__) 28 | self._prefs: PyutPreferences = PyutPreferences() 29 | 30 | tipLines: TipLinesType = self._cacheTips(fileName=fqFileName) 31 | tipCount: int = self._computeTipCount(tipLines=tipLines) 32 | 33 | self._tipLines: TipLinesType = tipLines 34 | self._tipCount: int = tipCount 35 | 36 | self._currentTipNumber: int = self._safelyRetrieveCurrentTipNumber() 37 | 38 | @property 39 | def currentTipNumber(self) -> int: 40 | return self._currentTipNumber 41 | 42 | def getCurrentTipText(self) -> str: 43 | 44 | tipText: str = self._tipLines[self._currentTipNumber] 45 | 46 | self.logger.debug(f'{tipText=}') 47 | 48 | return tipText 49 | 50 | def incrementTipNumber(self, byValue: int): 51 | """ 52 | Increment/Decrement. Returns non-zero tip number 53 | 54 | Args: 55 | byValue: Use negative number to decrement 56 | """ 57 | tipNumber = (self._currentTipNumber + byValue) % self._tipCount 58 | if tipNumber < 0: 59 | tipNumber = self._tipCount 60 | 61 | self._currentTipNumber = tipNumber 62 | self.logger.info(f'{self._currentTipNumber=}') 63 | 64 | def _cacheTips(self, fileName: str) -> TipLinesType: 65 | 66 | file: TextIO = open(fileName) 67 | tipLines: TipLinesType = cast(TipLinesType, file.read().split(f'{osLineSep}')) 68 | file.close() 69 | 70 | return tipLines 71 | 72 | def _computeTipCount(self, tipLines: TipLinesType) -> int: 73 | return len(tipLines) - 1 # because we use it as a 0-based index 74 | 75 | def _safelyRetrieveCurrentTipNumber(self) -> int: 76 | 77 | currentTipNumber: int = self._prefs.currentTip 78 | if currentTipNumber is None: 79 | return 0 80 | else: 81 | return currentTipNumber 82 | -------------------------------------------------------------------------------- /tests/pyut/ui/tools/TestToolbox.py: -------------------------------------------------------------------------------- 1 | 2 | from unittest import TestSuite 3 | from unittest import main as unitTestMain 4 | 5 | from wx import Size 6 | 7 | from pyut.preferences.PyutPreferences import PyutPreferences 8 | 9 | from pyut.general.datatypes.ToolBarIconSize import ToolBarIconSize 10 | 11 | from pyut.ui.tools.Toolbox import Toolbox 12 | 13 | from codeallybasic.UnitTestBase import UnitTestBase 14 | 15 | 16 | class TestToolbox(UnitTestBase): 17 | """ 18 | """ 19 | @classmethod 20 | def setUpClass(cls): 21 | UnitTestBase.setUpClass() 22 | 23 | def setUp(self): 24 | super().setUp() 25 | self._preferences: PyutPreferences = PyutPreferences() 26 | 27 | def tearDown(self): 28 | super().tearDown() 29 | 30 | def testComputeToolboxNumberRows(self): 31 | 32 | actualRowCount: int = Toolbox.computeToolboxNumberRows(toolCount=21, numColumns=3) 33 | expectedRowCount: int = 7 34 | 35 | self.assertEqual(expectedRowCount, actualRowCount, 'Incorrect row count') 36 | 37 | def testComputeToolboxNumberRowsForceCeiling(self): 38 | 39 | actualRowCount: int = Toolbox.computeToolboxNumberRows(toolCount=35, numColumns=6) 40 | expectedRowCount: int = 6 41 | 42 | self.assertEqual(expectedRowCount, actualRowCount, 'Incorrect row count') 43 | 44 | def testComputeSizeBasedOnRowColumnsSmallIcons(self): 45 | 46 | iconSize: int = int(ToolBarIconSize.SIZE_16.value) 47 | actualSize: Size = Toolbox.computeSizeBasedOnRowColumns(numColumns=3, numRows=6, iconSize=iconSize) 48 | expectedSize: Size = Size(54, 108) 49 | self.logger.debug(f'Small Icons Window Size: {actualSize}') 50 | 51 | self.assertEqual(expectedSize, actualSize, 'Size computation changed for small icons') 52 | 53 | def testComputeSizeBasedOnRowColumnsLargeIcons(self): 54 | 55 | iconSize: int = int(ToolBarIconSize.SIZE_32.value) 56 | actualSize: Size = Toolbox.computeSizeBasedOnRowColumns(numColumns=3, numRows=6, iconSize=iconSize) 57 | expectedSize: Size = Size(102, 204) 58 | self.logger.debug(f'Large Icons Window Size: {actualSize}') 59 | 60 | self.assertEqual(expectedSize, actualSize, 'Size computation changed for large icons') 61 | 62 | 63 | def suite() -> TestSuite: 64 | """You need to change the name of the test class here also.""" 65 | import unittest 66 | 67 | testSuite: TestSuite = TestSuite() 68 | 69 | testSuite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(testCaseClass=TestToolbox)) 70 | 71 | return testSuite 72 | 73 | 74 | if __name__ == '__main__': 75 | unitTestMain() 76 | -------------------------------------------------------------------------------- /pyut/ui/menuhandlers/EditMenuHandler.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | 6 | from wx import CommandEvent 7 | 8 | from wx import Menu 9 | 10 | 11 | from pyut.ui.menuhandlers.BaseMenuHandler import BaseMenuHandler 12 | 13 | from pyut.ui.eventengine.EventType import EventType 14 | from pyut.ui.eventengine.IEventEngine import IEventEngine 15 | 16 | 17 | class EditMenuHandler(BaseMenuHandler): 18 | 19 | def __init__(self, editMenu: Menu, eventEngine: IEventEngine | None = None): 20 | 21 | super().__init__(menu=editMenu, eventEngine=eventEngine) 22 | 23 | self.logger: Logger = getLogger(__name__) 24 | 25 | # noinspection PyUnusedLocal 26 | def onUndo(self, event: CommandEvent): 27 | """ 28 | 29 | Args: 30 | event: 31 | """ 32 | self._eventEngine.sendEvent(EventType.Undo) 33 | 34 | # noinspection PyUnusedLocal 35 | def onRedo(self, event: CommandEvent): 36 | """ 37 | 38 | Args: 39 | event: 40 | """ 41 | self._eventEngine.sendEvent(EventType.Redo) 42 | 43 | # noinspection PyUnusedLocal 44 | def onCut(self, event: CommandEvent): 45 | """ 46 | May be invoked directly from a menu item or from another component by posting the appropriate event 47 | Args: 48 | event: 49 | """ 50 | self._eventEngine.sendEvent(EventType.CutShapes) 51 | 52 | # noinspection PyUnusedLocal 53 | def onCopy(self, event: CommandEvent): 54 | """ 55 | Args: 56 | event: 57 | """ 58 | self._eventEngine.sendEvent(EventType.CopyShapes) 59 | 60 | # noinspection PyUnusedLocal 61 | def onPaste(self, event: CommandEvent): 62 | """ 63 | 64 | Args: 65 | event: 66 | """ 67 | self._eventEngine.sendEvent(EventType.PasteShapes) 68 | 69 | # noinspection PyUnusedLocal 70 | def onSelectAll(self, event: CommandEvent): 71 | """ 72 | Args: 73 | event: 74 | """ 75 | self._eventEngine.sendEvent(EventType.SelectAllShapes) 76 | 77 | # noinspection PyUnusedLocal 78 | def onAddPyut(self, event: CommandEvent): 79 | """ 80 | Add Pyut UML Diagram. 81 | 82 | Args: 83 | event: 84 | """ 85 | self._eventEngine.sendEvent(EventType.AddPyutDiagram) 86 | 87 | # noinspection PyUnusedLocal 88 | def onAddOgl(self, event: CommandEvent): 89 | """ 90 | Add Pyut-Ogl UML Diagram. 91 | 92 | Args: 93 | event: 94 | """ 95 | self._eventEngine.sendEvent(EventType.AddOglDiagram) 96 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/DlgEditMethodModifiers.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import List 3 | from typing import cast 4 | 5 | from logging import Logger 6 | from logging import getLogger 7 | 8 | from copy import deepcopy 9 | 10 | from wx import ID_ANY 11 | 12 | from wx import CommandEvent 13 | 14 | from wx.adv import EL_ALLOW_DELETE 15 | from wx.adv import EL_ALLOW_EDIT 16 | from wx.adv import EL_ALLOW_NEW 17 | from wx.adv import EL_DEFAULT_STYLE 18 | from wx.adv import EditableListBox 19 | 20 | from wx.lib.sized_controls import SizedPanel 21 | 22 | from pyutmodelv2.PyutMethod import PyutModifiers 23 | from pyutmodelv2.PyutModifier import PyutModifier 24 | 25 | from pyut.ui.dialogs.BaseEditDialog import BaseEditDialog 26 | 27 | 28 | class DlgEditMethodModifiers(BaseEditDialog): 29 | 30 | def __init__(self, parent, pyutModifiers: PyutModifiers): 31 | 32 | super().__init__(parent, title='Edit Method Modifiers') 33 | 34 | self.logger: Logger = getLogger(__name__) 35 | self._pyutModifiers: PyutModifiers = pyutModifiers 36 | self._pyutModifiersCopy: PyutModifiers = deepcopy(pyutModifiers) 37 | 38 | self._elb: EditableListBox = cast(EditableListBox, None) 39 | sizedPanel: SizedPanel = self.GetContentsPane() 40 | 41 | self._layoutEditableListBox(sizedPanel) 42 | self._layoutStandardOkCancelButtonSizer() 43 | 44 | @property 45 | def pyutModifiers(self) -> PyutModifiers: 46 | return self._stringToPyutModifiers() 47 | 48 | def _layoutEditableListBox(self, parent: SizedPanel): 49 | style: int = EL_DEFAULT_STYLE | EL_ALLOW_NEW | EL_ALLOW_EDIT | EL_ALLOW_DELETE 50 | self._elb = EditableListBox(parent, ID_ANY, "Modifiers", (-1, -1), (-1, -1), style=style) 51 | 52 | self._elb.SetStrings(self._pyutModifiersToStrings()) 53 | 54 | def _onOk(self, event: CommandEvent): 55 | """ 56 | """ 57 | 58 | super()._onOk(event) 59 | 60 | def _pyutModifiersToStrings(self) -> List[str]: 61 | """ 62 | Converts the modifiers copy to a list of string 63 | Returns: 64 | """ 65 | 66 | stringList: List[str] = [] 67 | for modifier in self._pyutModifiersCopy: 68 | pyutModifier: PyutModifier = cast(PyutModifier, modifier) 69 | stringList.append(pyutModifier.name) 70 | 71 | return stringList 72 | 73 | def _stringToPyutModifiers(self) -> PyutModifiers: 74 | pyutModifiers: PyutModifiers = PyutModifiers([]) 75 | strList: List[str] = self._elb.GetStrings() 76 | for modifierString in strList: 77 | pyutModifier: PyutModifier = PyutModifier(name=modifierString) 78 | pyutModifiers.append(pyutModifier) 79 | 80 | return pyutModifiers 81 | -------------------------------------------------------------------------------- /tests/pyut/general/TestLineSplitter.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import List 3 | 4 | from unittest import TestSuite 5 | from unittest import main as unitTestMain 6 | 7 | from unittest.mock import Mock 8 | 9 | from tests.ProjectTestBase import ProjectTestBase 10 | 11 | from pyut.general.LineSplitter import LineSplitter 12 | 13 | 14 | class TestLineSplitter(ProjectTestBase): 15 | """ 16 | """ 17 | 18 | def setUp(self): 19 | super().setUp() 20 | self.lineSplitter: LineSplitter = LineSplitter() 21 | 22 | def tearDown(self): 23 | super().tearDown() 24 | 25 | def testNoSplit(self): 26 | 27 | shortLine: str = 'Short line' 28 | mockedDC: Mock = Mock() 29 | mockedDC.GetTextExtent = self.mockGetTextExtent 30 | textWidth: int = 120 31 | newLines: List[str] = self.lineSplitter.split(text=shortLine, dc=mockedDC, textWidth=textWidth) 32 | 33 | expectedLength: int = 1 34 | actualLength: int = len(newLines) 35 | self.assertEqual(expectedLength, actualLength, 'Split incorrectly') 36 | 37 | def testBasicSplit(self): 38 | longLine: str = '12345678 12345678 12345678' 39 | mockedDC: Mock = Mock() 40 | mockedDC.GetTextExtent = self.mockGetTextExtent 41 | textWidth: int = 45 42 | 43 | newLines: List[str] = self.lineSplitter.split(text=longLine, dc=mockedDC, textWidth=textWidth) 44 | 45 | expectedLength: int = 3 46 | actualLength: int = len(newLines) 47 | self.assertEqual(expectedLength, actualLength, 'Split incorrectly') 48 | 49 | def testFancySplit(self): 50 | 51 | longLine: str = 'Where oh where do you want to split me along party lines or maybe not' 52 | mockedDC: Mock = Mock() 53 | mockedDC.GetTextExtent = self.mockGetTextExtent 54 | textWidth: int = 50 55 | 56 | newLines: List[str] = self.lineSplitter.split(text=longLine, dc=mockedDC, textWidth=textWidth) 57 | 58 | expectedLength: int = 8 59 | actualLength: int = len(newLines) 60 | self.assertEqual(expectedLength, actualLength, 'Split incorrectly') 61 | 62 | def mockGetTextExtent(self, textToMeasure: str): 63 | 64 | noSpaces: str = textToMeasure.strip(' ') 65 | height: int = 10 66 | width: int = noSpaces.__len__() * 5 67 | 68 | width += 2 # account for space at end that was stripped 69 | 70 | return width, height 71 | 72 | 73 | def suite() -> TestSuite: 74 | import unittest 75 | 76 | testSuite: TestSuite = TestSuite() 77 | 78 | testSuite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(testCaseClass=TestLineSplitter)) 79 | 80 | return testSuite 81 | 82 | 83 | if __name__ == '__main__': 84 | unitTestMain() 85 | -------------------------------------------------------------------------------- /tests/pyut/dialogs/tips/TestTipHandler.py: -------------------------------------------------------------------------------- 1 | 2 | from unittest import TestSuite 3 | from unittest import main as unitTestMain 4 | 5 | from codeallybasic.ResourceManager import ResourceManager 6 | from codeallybasic.UnitTestBase import UnitTestBase 7 | 8 | from pyut.PyutConstants import PyutConstants 9 | 10 | from pyut.ui.dialogs.tips.TipHandler import TipHandler 11 | 12 | from tests.ProjectTestBase import ProjectTestBase 13 | 14 | 15 | class TestTipHandler(ProjectTestBase): 16 | """ 17 | """ 18 | @classmethod 19 | def setUpClass(cls): 20 | UnitTestBase.setUpClass() 21 | 22 | def setUp(self): 23 | super().setUp() 24 | self._tipsFileName: str = ResourceManager.retrieveResourcePath(bareFileName=f'{PyutConstants.TIPS_FILENAME}', 25 | packageName=f'{PyutConstants.BASE_RESOURCES_PACKAGE}', 26 | resourcePath=f'{PyutConstants.BASE_RESOURCE_PATH}') 27 | 28 | def tearDown(self): 29 | super().tearDown() 30 | 31 | def testInitialization(self): 32 | 33 | tipHandler: TipHandler = TipHandler(fqFileName=self._tipsFileName) 34 | 35 | self.assertTrue(len(tipHandler._tipLines) > 0) 36 | 37 | def testComputeTipCount(self): 38 | """Another test""" 39 | pass 40 | 41 | tipHandler: TipHandler = TipHandler(fqFileName=self._tipsFileName) 42 | self.assertTrue(tipHandler._tipCount > 0) 43 | 44 | def testSafelyRetrieveCurrentTipNumber(self): 45 | 46 | tipHandler: TipHandler = TipHandler(fqFileName=self._tipsFileName) 47 | 48 | self.assertIsNotNone(tipHandler._currentTipNumber) 49 | 50 | self.assertTrue(tipHandler._currentTipNumber >= 0) 51 | 52 | def testGetCurrentTipText(self): 53 | 54 | tipHandler: TipHandler = TipHandler(fqFileName=self._tipsFileName) 55 | 56 | expectedTip: str = tipHandler._tipLines[tipHandler._currentTipNumber] 57 | actualTip: str = tipHandler.getCurrentTipText() 58 | 59 | self.assertEqual(expectedTip, actualTip, 'Uh, our white box test did not work') 60 | 61 | def testIncrementTipNumber(self): 62 | 63 | tipHandler: TipHandler = TipHandler(fqFileName=self._tipsFileName) 64 | 65 | prevTipNumber: int = tipHandler._currentTipNumber 66 | tipHandler.incrementTipNumber(1) 67 | 68 | self.assertEqual(prevTipNumber + 1, tipHandler._currentTipNumber, 'We did not increment by 1') 69 | 70 | 71 | def suite() -> TestSuite: 72 | 73 | import unittest 74 | 75 | testSuite: TestSuite = TestSuite() 76 | 77 | testSuite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(testCaseClass=TestTipHandler)) 78 | 79 | return testSuite 80 | 81 | 82 | if __name__ == '__main__': 83 | unitTestMain() 84 | -------------------------------------------------------------------------------- /pyut/resources/img/DefaultPreferences.py: -------------------------------------------------------------------------------- 1 | #---------------------------------------------------------------------- 2 | # This file was generated by /Users/humberto.a.sanchez.ii/PycharmProjects/PyUt/venv-pyenv-3.9.0/bin/img2py 3 | # 4 | from wx.lib.embeddedimage import PyEmbeddedImage 5 | 6 | embeddedImage = PyEmbeddedImage( 7 | b'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9i' 8 | b'ZSBJbWFnZVJlYWR5ccllPAAAA9dpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tl' 9 | b'dCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1l' 10 | b'dGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUu' 11 | b'MC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpS' 12 | b'REYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgt' 13 | b'bnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJo' 14 | b'dHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6' 15 | b'Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRv' 16 | b'YmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9u' 17 | b'cy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpP' 18 | b'cmlnaW5hbERvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo4ZTMyMGZhYy0xMDU3' 19 | b'LTExZGItYTlkNC1mZTk0NmM0OWUxNTciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RTFB' 20 | b'NUY3REJGM0I0MTFFNkEzNDlGNjRCNEQ1RDgxRDEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5p' 21 | b'aWQ6RTFBNUY3REFGM0I0MTFFNkEzNDlGNjRCNEQ1RDgxRDEiIHhtcDpDcmVhdG9yVG9vbD0i' 22 | b'QWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVm' 23 | b'Omluc3RhbmNlSUQ9InhtcC5paWQ6MjhGQjk3NTU5N0YzRTYxMTk1QkZFNzg5RjBFRkJDN0Ei' 24 | b'IHN0UmVmOmRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo4ZTMyMGZhYy0xMDU3' 25 | b'LTExZGItYTlkNC1mZTk0NmM0OWUxNTciLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJE' 26 | b'Rj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz59cfg6AAACV0lEQVR42oyTS0wT' 27 | b'QRzGv5ndZdtSWmhLLLU0hFRSg2AIJ3zEejKePWji3eDNqweIB2IiXky8cEJiokb0KokHEkPU' 28 | b'pBdfREzrgzaRNlLY0t1uy27bHWervLQQdvPfncl8+5tvNt+fMMZ8lUplql6vewAGPm8Uf0Av' 29 | b'l5HPr73o7++/LwgCml2Ei0OKoiRNw3AzG2BZsOxiVmOcTmcgStLYyMipCRv69snjO92hkBU6' 30 | b'feaWJMuGDegqFAqLHODfBrA/ED5FoaAgm82hzeO5QQXqWp6dvf09maxcn3kYDnR2KmJTX2xn' 31 | b'aIMcDgc0rXSvqJpMPzEE19HwnP2xvU6bH+wvhzvxer0IR3rh0ecx1DNHVlc+VzfK+t0taXMH' 32 | b'u4wIkhPFzEcE2xfgd+o46Tk2Pnx5KrGloQcBCL9r1Ro2fz5CJEbw5Z2/FB28MN0VPIJDABio' 33 | b'5ML613n0RlPI/jDhjIzWYudH2W7VvgBCRFSKCkT9GXxBisxyDL7oObLzhw4C8D2I6ISSeo6+' 34 | b'gXV8+wS4eq5CEP+XNwVQsQVaLoV2+WWDtqaeRXv3IKyacThA3aIopp+ib3ATyfcu+GJXeCCM' 35 | b'vQHZD0AoRaW0gexaEYk3EszWS2j1h3hCq/8efzsHRJblFjs0dhNRKkLHL1gmgypdw8DwRb5x' 36 | b'FYLDyRMp2/raHsDS0lI9n88nebd12ABBENnih0SbuRmhEeqnrxdeMbs3CCEwDVOffjBzvFQq' 37 | b'5eLx+Mrk5KQhut3uvKZpcQ5o+FNVld0cm/CrqtZRr5mBJr5beYUDgcAqfxu/BRgAJ9ARQuBm' 38 | b'0B0AAAAASUVORK5CYII=') 39 | 40 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/DlgEditStereotype.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import List 3 | 4 | from logging import Logger 5 | from logging import getLogger 6 | 7 | from wx import CANCEL 8 | from wx import EVT_BUTTON 9 | from wx import EVT_CLOSE 10 | from wx import ID_CANCEL 11 | from wx import ID_OK 12 | from wx import OK 13 | 14 | from wx import CommandEvent 15 | from wx import ListBox 16 | from wx import StaticText 17 | from wx import Window 18 | 19 | from wx.lib.sized_controls import SizedPanel 20 | 21 | from pyutmodelv2.enumerations.PyutStereotype import PyutStereotype 22 | 23 | from pyut.ui.dialogs.BaseEditDialog import BaseEditDialog 24 | 25 | 26 | class DlgEditStereotype(BaseEditDialog): 27 | """ 28 | Usage: 29 | with DlgEditStereotype(parent=parent, eventEngine=eventEngine, pyutStereotype=pyutModel.stereotype) as dlg: 30 | if dlg.ShowModal() == OK: 31 | self._pyutModel.stereotype = dlg.value 32 | """ 33 | 34 | def __init__(self, parent: Window, pyutStereotype: PyutStereotype): 35 | 36 | super().__init__(parent=parent, title='Select Stereotype') 37 | self.logger: Logger = getLogger(__name__) 38 | 39 | panel: SizedPanel = self.GetContentsPane() 40 | 41 | panel.SetSizerType('vertical') 42 | 43 | classStereoTypes: List[str] = [enum.value for enum in PyutStereotype] 44 | self._label: StaticText = StaticText(panel, label='Stereotypes') 45 | self._stereoTypeSelector: ListBox = ListBox(panel, choices=classStereoTypes) 46 | self._stereoTypeSelector.SetSizerProps(proportion=1, expand=True) 47 | 48 | self.SetButtonSizer(self.CreateStdDialogButtonSizer(OK | CANCEL)) 49 | 50 | self._setSelected(pyutStereotype=pyutStereotype) 51 | self.Fit() 52 | self.SetMinSize(self.GetSize()) 53 | 54 | self.Bind(EVT_BUTTON, self._onOk, id=ID_OK) 55 | self.Bind(EVT_BUTTON, self._onClose, id=ID_CANCEL) 56 | self.Bind(EVT_CLOSE, self._onClose) 57 | 58 | @property 59 | def value(self) -> PyutStereotype: 60 | """ 61 | Query this if the dialog ended with Ok. 62 | I know, Standard wxPython uses GetValue, Too bad, I am providing 63 | additional functionality, aka type conversion 64 | 65 | Returns: The currently selected enumeration 66 | """ 67 | selection: str = self._stereoTypeSelector.GetString(self._stereoTypeSelector.GetSelection()) 68 | 69 | pyutStereotype: PyutStereotype = PyutStereotype.toEnum(selection) 70 | return pyutStereotype 71 | 72 | def _setSelected(self, pyutStereotype: PyutStereotype): 73 | x: int = self._stereoTypeSelector.FindString(pyutStereotype.value) 74 | self._stereoTypeSelector.SetSelection(x) 75 | 76 | def _onOk(self, event: CommandEvent): 77 | super()._onOk(event) 78 | 79 | def _onClose(self, event: CommandEvent): 80 | super()._onClose(event) 81 | -------------------------------------------------------------------------------- /archive/help/node2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | Introduction 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | next 34 | 35 | 36 | up 37 | 38 | 39 | previous 40 | 41 | 42 | contents 43 | 44 | 45 | index 46 | 47 |
48 | Next: PyUt installation 49 | Up: PyUt 1.4 help 50 | Previous: Contents 51 |   Contents 52 |   Index 53 |
54 |
55 | 56 | 57 |

58 | Introduction 59 |

60 | This document presents PyUt (which means Python UML tool) and is 61 | organized as follow : the first part will help you to install PyUt. 62 | Next section is a little introduction to UML 1.4 class and use cases diagrams. 63 | In the third section, you will be introduced to the user interface and it 64 | will be followed by a sample in the last section. 65 | 66 |
67 |
68 |
69 | Dutoit Cedric 70 | 2003-06-29 71 |
72 | 73 | 74 | -------------------------------------------------------------------------------- /pyut/general/GitHubAdapter.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import List 3 | from typing import cast 4 | 5 | from logging import Logger 6 | from logging import getLogger 7 | 8 | from collections import Counter 9 | 10 | from semantic_version import Version as SemanticVersion 11 | 12 | from github import Github 13 | from github.GitRelease import GitRelease 14 | from github.PaginatedList import PaginatedList 15 | from github.Repository import Repository 16 | 17 | # 18 | # Import the module because it appears that PyGithub dynamically loads it. So 19 | # while the code works when executed in the IDE, it does not work when I build 20 | # Pyut as a standalone applications. The noinspection comments I include because 21 | # I despise warnings in the PyCharm IDE. ;-) 22 | # 23 | # noinspection PyPackageRequirements 24 | # noinspection PyUnresolvedReferences 25 | from cffi import api 26 | 27 | 28 | class GitHubAdapter: 29 | 30 | ALL_ISSUES_INDICATOR: str = 'All' 31 | OPEN_MILESTONE_INDICATOR: str = 'Open' 32 | OPEN_ISSUE_INDICATOR: str = 'open' 33 | 34 | PYUT_REPOSITORY_SLUG: str = 'hasii2011/PyUt' 35 | MY_LIST: List[str] = ['g', 'h', 'p', '_', 'B', 36 | 'i', 'M', 'm', 'Z', 'N', 37 | 's', '1', 'U', 'A', 'o', 38 | 's', 'o', 'V', '6', 'a', 39 | 'c', 'r', 'U', '1', 'o', 40 | 'B', 'H', 'e', 'x', 'I', 41 | 'r', 'S', 'x', 'H', '2', 42 | '8', 'K', 'z', 'Z', 'W'] 43 | 44 | def __init__(self): 45 | 46 | self.logger: Logger = getLogger(__name__) 47 | self._github: Github = Github(''.join(GitHubAdapter.MY_LIST)) 48 | 49 | def getLatestVersionNumber(self) -> SemanticVersion: 50 | 51 | repo: Repository = self._github.get_repo(GitHubAdapter.PYUT_REPOSITORY_SLUG) 52 | self.logger.info(f'{repo.full_name=}') 53 | 54 | releases: PaginatedList = repo.get_releases() 55 | 56 | latestReleaseVersion: SemanticVersion = SemanticVersion('0.0.0') 57 | for release in releases: 58 | gitRelease: GitRelease = cast(GitRelease, release) 59 | 60 | releaseNumber: str = gitRelease.tag_name 61 | numPeriods: int = self._countPeriods(releaseNumber) 62 | if numPeriods < 2: 63 | releaseNumber = f'{releaseNumber}.0' 64 | 65 | releaseVersion: SemanticVersion = SemanticVersion(releaseNumber) 66 | self.logger.debug(f'{releaseVersion=}') 67 | if latestReleaseVersion < releaseVersion: 68 | latestReleaseVersion = releaseVersion 69 | 70 | return latestReleaseVersion 71 | 72 | def cleanUp(self): 73 | del self._github 74 | 75 | def _countPeriods(self, releaseNumber: str) -> int: 76 | 77 | cnt = Counter(list(releaseNumber)) 78 | return cnt['.'] 79 | -------------------------------------------------------------------------------- /pyut/ui/PyutDocument.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import cast 3 | 4 | from logging import Logger 5 | from logging import getLogger 6 | 7 | from wx import ICON_ERROR 8 | from wx import OK 9 | 10 | from wx import TreeItemId 11 | from wx import MessageDialog 12 | 13 | from pyut.enums.DiagramType import DiagramType 14 | 15 | from pyut.ui.IPyutDocument import IPyutDocument 16 | from pyut.ui.IPyutProject import UmlFrameType 17 | 18 | from pyut.ui.eventengine.IEventEngine import IEventEngine 19 | 20 | 21 | class PyutDocument(IPyutDocument): 22 | """ 23 | Document : Contains a document : frames, properties, ... 24 | """ 25 | def __init__(self, diagramFrame: UmlFrameType, docType: DiagramType, eventEngine: IEventEngine): 26 | 27 | """ 28 | 29 | Args: 30 | diagramFrame: 31 | docType: The enumeration value for the diagram type 32 | eventEngine: The engine used to communicate with the Pyut UI 33 | """ 34 | self._diagramFrame: UmlFrameType = diagramFrame 35 | 36 | super().__init__() 37 | self.logger: Logger = getLogger(__name__) 38 | 39 | self._diagramType: DiagramType = docType # This document's diagram type 40 | self._eventEngine: IEventEngine = eventEngine 41 | self._treeRoot: TreeItemId = cast(TreeItemId, None) # The document entry in the tree 42 | self._treeRootParent: TreeItemId = cast(TreeItemId, None) # Project entry 43 | self._title: str = cast(str, None) 44 | 45 | @property 46 | def title(self) -> str: 47 | return self._title 48 | 49 | @title.setter 50 | def title(self, theNewValue: str): 51 | self._title = theNewValue 52 | 53 | @property 54 | def treeRoot(self) -> TreeItemId: 55 | """ 56 | Returns: The tree root ItemId for this document's node 57 | """ 58 | return self._treeRoot 59 | 60 | @treeRoot.setter 61 | def treeRoot(self, value: TreeItemId): 62 | self._treeRoot = value 63 | 64 | @property 65 | def diagramFrame(self) -> UmlFrameType: 66 | """ 67 | Return the document's frame 68 | 69 | Returns: this document's uml frame 70 | """ 71 | return self._diagramFrame 72 | 73 | @property 74 | def diagramType(self) -> DiagramType: 75 | """ 76 | Returns: The document type 77 | """ 78 | return self._diagramType 79 | 80 | def updateTreeText(self): 81 | assert False, 'Do not use this method' 82 | 83 | def removeFromTree(self): 84 | assert False, 'Do not use this method' 85 | 86 | def _displayError(self, message: str): 87 | booBoo: MessageDialog = MessageDialog(parent=None, message=message, caption='Error', style=OK | ICON_ERROR) 88 | booBoo.ShowModal() 89 | 90 | def __str__(self) -> str: 91 | return f'[{self.title=} {self._diagramType=}]' 92 | 93 | def __repr__(self): 94 | return self.__str__() 95 | -------------------------------------------------------------------------------- /archive/help/footnode.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | Footnotes 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
... 2.21
33 |
http://www.python.org 35 | 36 |
.
 37 | .
 38 | .
 39 | .
 40 | .
 41 | .
 42 | .
 43 | .
 44 | .
 45 | .
 46 | .
 47 | .
 48 | .
 49 | .
 50 | .
 51 | .
 52 | .
 53 | .
 54 | .
 55 | .
 56 | .
 57 | .
 58 | .
 59 | .
 60 | .
 61 | .
 62 | .
 63 | .
 64 | .
 65 | .
 66 | 
67 |
68 |
... 2.3.42
70 |
http://www.wxpython.org 72 | 73 |
.
 74 | .
 75 | .
 76 | .
 77 | .
 78 | .
 79 | .
 80 | .
 81 | .
 82 | .
 83 | .
 84 | .
 85 | .
 86 | .
 87 | .
 88 | .
 89 | .
 90 | .
 91 | .
 92 | .
 93 | .
 94 | .
 95 | .
 96 | .
 97 | .
 98 | .
 99 | .
100 | .
101 | .
102 | .
103 | 
104 |
105 |
... SIZE="-1">$PYUTPATH/src3
107 |
Where '$PYUTPATH' is the installation directory 108 | 109 |
.
110 | .
111 | .
112 | .
113 | .
114 | .
115 | .
116 | .
117 | .
118 | .
119 | .
120 | .
121 | .
122 | .
123 | .
124 | .
125 | .
126 | .
127 | .
128 | .
129 | .
130 | .
131 | .
132 | .
133 | .
134 | .
135 | .
136 | .
137 | .
138 | .
139 | 
140 |
141 |
... site4
143 |
http://www.rational.com 145 | 146 |
.
147 | .
148 | .
149 | .
150 | .
151 | .
152 | .
153 | .
154 | .
155 | .
156 | .
157 | .
158 | .
159 | .
160 | .
161 | .
162 | .
163 | .
164 | .
165 | .
166 | .
167 | .
168 | .
169 | .
170 | .
171 | .
172 | .
173 | .
174 | .
175 | .
176 | 
177 |
178 |
179 | 180 | 181 | -------------------------------------------------------------------------------- /pyut/ui/IPyutProject.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import List 3 | 4 | from abc import ABC 5 | from abc import abstractmethod 6 | 7 | from wx import TreeItemId 8 | 9 | from pyut.ui.IPyutDocument import PyutDocuments 10 | from pyut.ui.Types import Frames 11 | from pyut.ui.Types import UmlFrameType 12 | 13 | 14 | class IPyutProject(ABC): 15 | """ 16 | Something to fool mypy 17 | """ 18 | def __init__(self): 19 | pass 20 | 21 | @property 22 | @abstractmethod 23 | def filename(self) -> str: 24 | """ 25 | Returns: The project's filename 26 | """ 27 | pass 28 | 29 | @filename.setter 30 | @abstractmethod 31 | def filename(self, filename: str): 32 | """ 33 | Set the project's filename 34 | 35 | Args: 36 | filename: 37 | """ 38 | pass 39 | 40 | @property 41 | @abstractmethod 42 | def projectName(self) -> str: 43 | """ 44 | Truncates to just the file name and less the suffix. 45 | 46 | Returns: Nice short hane 47 | """ 48 | pass 49 | 50 | @property 51 | @abstractmethod 52 | def codePath(self) -> str: 53 | pass 54 | 55 | @codePath.setter 56 | @abstractmethod 57 | def codePath(self, newValue: str): 58 | pass 59 | 60 | @property 61 | @abstractmethod 62 | def modified(self) -> bool: 63 | """ 64 | Returns: 'True' if it has been else 'False' 65 | """ 66 | pass 67 | 68 | @modified.setter 69 | @abstractmethod 70 | def modified(self, value: bool = True): 71 | """ 72 | Set that the project has been modified 73 | Args: 74 | value: 'True' if it has been else 'False' 75 | """ 76 | pass 77 | 78 | @property 79 | @abstractmethod 80 | def projectTreeRoot(self) -> TreeItemId: 81 | """ 82 | A piece of UI information needed to communicate with the UI component 83 | 84 | Returns: The opaque item where this project's documents are display on the UI Tree 85 | """ 86 | pass 87 | 88 | @projectTreeRoot.setter 89 | @abstractmethod 90 | def projectTreeRoot(self, newValue: TreeItemId): 91 | pass 92 | 93 | @property 94 | @abstractmethod 95 | def frames(self) -> Frames: 96 | pass 97 | 98 | @property 99 | @abstractmethod 100 | def documents(self) -> PyutDocuments: 101 | """ 102 | Return the documents (Not a copy) 103 | TODO: Is this a bad idea 104 | 105 | Returns: A list of documents 106 | """ 107 | pass 108 | 109 | @abstractmethod 110 | def getFrames(self) -> List[UmlFrameType]: 111 | """ 112 | Get all the project's frames 113 | 114 | Returns: 115 | List of frames 116 | """ 117 | pass 118 | 119 | @abstractmethod 120 | def selectFirstDocument(self): 121 | pass 122 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/DlgEditCode.py: -------------------------------------------------------------------------------- 1 | 2 | from os import linesep as osLineSep 3 | 4 | from wx import BOTH 5 | from wx import CANCEL 6 | from wx import CAPTION 7 | from wx import CLOSE_BOX 8 | from wx import CommandEvent 9 | from wx import DIALOG_EX_METAL 10 | from wx import EVT_BUTTON 11 | from wx import EVT_CLOSE 12 | from wx import EVT_TEXT 13 | from wx import ID_CANCEL 14 | from wx import ID_OK 15 | from wx import OK 16 | from wx import Size 17 | 18 | from wx import TE_MULTILINE 19 | from wx import TextCtrl 20 | 21 | from wx.lib.sized_controls import SizedDialog 22 | 23 | from wx import NewIdRef as wxNewIdRef 24 | from wx.lib.sized_controls import SizedPanel 25 | 26 | from pyutmodelv2.PyutMethod import SourceCode 27 | 28 | TXT_CODE: int = wxNewIdRef() 29 | 30 | 31 | class DlgEditCode(SizedDialog): 32 | """ 33 | Dialog for the class comment edition. 34 | """ 35 | 36 | def __init__(self, parent, wxID, sourceCode: SourceCode): 37 | """ 38 | We'll modify pyutMethod on OK 39 | Args: 40 | parent: 41 | wxID: 42 | sourceCode: 43 | """ 44 | self._sourceCode: SourceCode = sourceCode 45 | self._displayableSourceCode: str = f'{osLineSep}'.join(sourceCode) 46 | 47 | super().__init__(parent, wxID, 'Method Code', style=CAPTION | CLOSE_BOX | DIALOG_EX_METAL) 48 | 49 | self.Center(BOTH) 50 | panel: SizedPanel = self.GetContentsPane() 51 | 52 | panel.SetSizerType('vertical') 53 | 54 | self._txtCtrl: TextCtrl = TextCtrl(panel, TXT_CODE, self._displayableSourceCode, style=TE_MULTILINE) 55 | 56 | self._txtCtrl.SetSizerProps(expand=True) 57 | print(f'{self._txtCtrl.GetSize()=}') 58 | 59 | txtCtrlSize: Size = self._txtCtrl.GetSize() 60 | newSize: Size = Size() 61 | newSize.SetWidth(txtCtrlSize.GetWidth() * 4) 62 | newSize.SetHeight(txtCtrlSize.GetHeight()) 63 | self._txtCtrl.SetMinSize(newSize) 64 | 65 | self.SetButtonSizer(self.CreateStdDialogButtonSizer(OK | CANCEL)) 66 | 67 | self.Fit() 68 | self.SetMinSize(self.GetSize()) 69 | 70 | self.Bind(EVT_TEXT, self.__onSourceCodeChange, id=TXT_CODE) 71 | self.Bind(EVT_BUTTON, self.__onCmdOk, id=ID_OK) 72 | self.Bind(EVT_BUTTON, self.__onClose, id=ID_CANCEL) 73 | self.Bind(EVT_CLOSE, self.__onClose) 74 | 75 | @property 76 | def sourceCode(self) -> SourceCode: 77 | return self._sourceCode 78 | 79 | def __onSourceCodeChange(self, event: CommandEvent): 80 | self._displayableSourceCode = event.GetString() 81 | 82 | # noinspection PyUnusedLocal 83 | def __onCmdOk(self, event: CommandEvent): 84 | """ 85 | """ 86 | self._sourceCode = SourceCode(self._displayableSourceCode.split(osLineSep)) 87 | self.SetReturnCode(OK) # TODO Fix this; should be OK 88 | self.EndModal(OK) 89 | 90 | # noinspection PyUnusedLocal 91 | def __onClose(self, event: CommandEvent): 92 | """ 93 | """ 94 | self.SetReturnCode(CANCEL) # This is correct 95 | self.EndModal(ID_CANCEL) 96 | -------------------------------------------------------------------------------- /pyut/ui/eventengine/inspector/Inspector.py: -------------------------------------------------------------------------------- 1 | from types import FrameType 2 | from types import ModuleType 3 | from typing import List 4 | 5 | from logging import Logger 6 | from logging import getLogger 7 | 8 | from inspect import FrameInfo 9 | 10 | from inspect import getmodule 11 | from inspect import stack 12 | 13 | CLASS_IDENTIFIER: str = 'self' 14 | MODULE_IDENTIFIER: str = '' 15 | 16 | 17 | class Inspector: 18 | """ 19 | Adapted from 20 | https://code.activestate.com/recipes/578352-get-full-caller-name-packagemodulefunction/ 21 | 22 | TODO: I am unsatisfied with the type hinting I managed to put in 23 | """ 24 | def __init__(self): 25 | self.logger: Logger = getLogger(__name__) 26 | 27 | @classmethod 28 | def getCallerName(cls, skip: int = 2) -> str: 29 | """ 30 | Get a name of a caller in the format module.class.method 31 | 32 | Args: 33 | skip: specifies how many levels of stack to skip while getting caller 34 | name. skip=1 means "who calls me", skip=2 "who calls my caller" etc. 35 | 36 | Returns: An empty string is returned if skipped levels exceed stack height 37 | """ 38 | stackList: List[FrameInfo] = stack() 39 | start: int = 0 + skip 40 | 41 | if len(stackList) < start + 1: 42 | return '' 43 | else: 44 | parentFrame: FrameType = stackList[start][0] 45 | name: List[str] = [] 46 | module: ModuleType = getmodule(parentFrame) # type: ignore 47 | 48 | # `module` can be None when frame is executed directly in console 49 | if module is not None: 50 | name.append(module.__name__) 51 | # 52 | # detect className 53 | # 54 | # noinspection PyUnresolvedReferences 55 | if CLASS_IDENTIFIER in parentFrame.f_locals: 56 | # 57 | # I don't know any way to detect call from the object method 58 | # There seems to be no way to detect static method call - it will be just a function call 59 | # 60 | 61 | # noinspection PyUnresolvedReferences 62 | clazz = parentFrame.f_locals[CLASS_IDENTIFIER] 63 | # fullyQualifiedClassName: str = parentFrame.f_locals[CLASS_IDENTIFIER].__class__.__name__ 64 | fullyQualifiedClassName: str = clazz.__class__.__name__ 65 | name.append(fullyQualifiedClassName) 66 | 67 | # noinspection PyUnresolvedReferences 68 | codeName: str = parentFrame.f_code.co_name 69 | if codeName != MODULE_IDENTIFIER: # top level usually 70 | name.append(codeName) # function or a method 71 | 72 | return ".".join(name) 73 | 74 | @classmethod 75 | def justClassMethodName(cls, fullyQualifiedName: str) -> str: 76 | 77 | parts: List[str] = fullyQualifiedName.split('.') 78 | 79 | partLen: int = len(parts) 80 | 81 | shortName: str = f'{parts[partLen-2]}.{parts[partLen-1]}' 82 | 83 | return shortName 84 | -------------------------------------------------------------------------------- /pyut/ui/umlframes/UmlClassDiagramFrameMenuHandler.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import cast 3 | 4 | from logging import Logger 5 | from logging import getLogger 6 | 7 | from ogl.OglLink import OglLink 8 | from wx import CommandEvent 9 | from wx import EVT_MENU 10 | from wx import Menu 11 | from wx import MouseEvent 12 | from wx import NewIdRef as wxNewIdRef 13 | 14 | from miniogl.DiagramFrame import DiagramFrame 15 | 16 | from ogl.OglClass import OglClass 17 | 18 | from pyut.ui.umlframes.UmlFrame import UmlFrame 19 | from pyut.ui.umlframes.UmlFrame import UmlObjects 20 | 21 | 22 | class UmlClassDiagramFrameMenuHandler: 23 | def __init__(self, frame: DiagramFrame): 24 | 25 | self.logger: Logger = getLogger(__name__) 26 | 27 | self._frame: DiagramFrame = frame 28 | self._contextMenu: Menu = cast(Menu, None) 29 | 30 | self._autoSizeID: int = wxNewIdRef() 31 | self.arrangeLinksID: int = wxNewIdRef() 32 | 33 | self._createContextMenu() 34 | 35 | def popupMenu(self, event: MouseEvent): 36 | 37 | x: int = event.GetX() 38 | y: int = event.GetY() 39 | self.logger.debug(f'UmlClassDiagramFrameMenuHandler - x,y: {x},{y}') 40 | 41 | self._frame.PopupMenu(self._contextMenu, x, y) 42 | 43 | def _createContextMenu(self): 44 | 45 | menu: Menu = Menu() 46 | 47 | menu.Append(self._autoSizeID, 'Auto Size Classes', 'Auto size all class objects on diagram') 48 | menu.Append(self.arrangeLinksID, 'Arrange Links', 'Auto arrange links') 49 | 50 | # Callbacks 51 | menu.Bind(EVT_MENU, self._onMenuClick, id=self._autoSizeID) 52 | menu.Bind(EVT_MENU, self._onMenuClick, id=self.arrangeLinksID) 53 | 54 | self._contextMenu = menu 55 | 56 | def _onMenuClick(self, event: CommandEvent): 57 | """ 58 | Callback for the popup menu on the class 59 | 60 | Args: 61 | event: 62 | """ 63 | eventId: int = event.GetId() 64 | 65 | match eventId: 66 | case self._autoSizeID: 67 | self._autoSize() 68 | case self.arrangeLinksID: 69 | # self._arrangeLinks() 70 | pass 71 | case _: 72 | self.logger.error('Unhandled Menu ID') 73 | 74 | def _autoSize(self): 75 | 76 | umlFrame: UmlFrame = cast(UmlFrame, self._frame) 77 | umlObjects: UmlObjects = umlFrame.umlObjects 78 | 79 | for umlObject in umlObjects: 80 | if isinstance(umlObject, OglClass): 81 | oglClass: OglClass = cast(OglClass, umlObject) 82 | 83 | oglClass.autoResize() 84 | 85 | def _arrangeLinks(self): 86 | 87 | umlFrame: UmlFrame = cast(UmlFrame, self._frame) 88 | umlObjects: UmlObjects = umlFrame.umlObjects 89 | 90 | for oglObject in umlObjects: 91 | if isinstance(oglObject, OglLink): 92 | oglLink: OglLink = cast(OglLink, oglObject) 93 | self.logger.info(f"Optimizing: {oglLink}") 94 | oglLink.optimizeLine() 95 | else: 96 | self.logger.debug(f"No line optimizing for: {oglObject}") 97 | -------------------------------------------------------------------------------- /pyut/ui/wxcommands/CommandCreateLollipopInterface.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Tuple 3 | from typing import cast 4 | 5 | from logging import Logger 6 | from logging import getLogger 7 | 8 | from codeallyadvanced.ui.AttachmentSide import AttachmentSide 9 | 10 | from pyutmodelv2.PyutModelTypes import ClassName 11 | 12 | from pyutmodelv2.PyutInterface import PyutInterface 13 | 14 | from miniogl.SelectAnchorPoint import SelectAnchorPoint 15 | 16 | from ogl.OglClass import OglClass 17 | from ogl.OglInterface2 import OglInterface2 18 | 19 | from pyut.ui.wxcommands.Types import DoableObjectType 20 | from pyut.ui.eventengine.EventType import EventType 21 | from pyut.ui.eventengine.IEventEngine import IEventEngine 22 | 23 | from pyut.ui.wxcommands.BaseWxCreateCommand import BaseWxCreateCommand 24 | 25 | 26 | class CommandCreateLollipopInterface(BaseWxCreateCommand): 27 | 28 | def __init__(self, implementor: OglClass, attachmentAnchor: SelectAnchorPoint, eventEngine: IEventEngine): 29 | 30 | self.logger: Logger = getLogger(__name__) 31 | 32 | self._implementor: OglClass = implementor 33 | self._attachmentAnchor: SelectAnchorPoint = attachmentAnchor 34 | 35 | # x,y will be reset correctly prior to adding the lollipop 36 | super().__init__(canUndo=True, name='Create Interface', eventEngine=eventEngine, x=0, y=0) 37 | 38 | def _createPrototypeInstance(self) -> DoableObjectType: 39 | 40 | pyutInterface: PyutInterface = PyutInterface() 41 | pyutInterface.addImplementor(ClassName(self._implementor.pyutObject.name)) 42 | 43 | self._pyutInterface: PyutInterface = pyutInterface 44 | self._oglInterface: OglInterface2 = OglInterface2(pyutInterface, self._attachmentAnchor) 45 | 46 | self.logger.debug(f'Created Prototype Instance: {pyutInterface.name=} {pyutInterface.id=}') 47 | return self._oglInterface 48 | 49 | def _placeShapeOnFrame(self): 50 | 51 | attachmentAnchor: SelectAnchorPoint = self._attachmentAnchor 52 | 53 | self._removeUnneededAnchorPoints(self._implementor, attachmentAnchor) 54 | 55 | self._eventEngine.sendEvent(EventType.EditInterface, oglInterface2=self._oglInterface, implementor=self._implementor) 56 | 57 | anchorPosition: Tuple[int, int] = attachmentAnchor.GetPosition() 58 | self.logger.info(f'anchorPosition: {anchorPosition}') 59 | 60 | # set up the eventHandler 61 | self._oglObjX = anchorPosition[0] 62 | self._oglObjY = anchorPosition[1] 63 | self._shape = self._oglInterface 64 | 65 | self._eventEngine.sendEvent(EventType.ActiveUmlFrame, callback=self._cbAddOglObjectToFrame) 66 | 67 | return True 68 | 69 | def Undo(self) -> bool: 70 | return True 71 | 72 | def _removeUnneededAnchorPoints(self, implementor: OglClass, attachmentAnchor: SelectAnchorPoint): 73 | 74 | attachmentSide: AttachmentSide = attachmentAnchor.attachmentPoint 75 | for iAnchor in implementor.anchors: 76 | if isinstance(iAnchor, SelectAnchorPoint): 77 | anchor: SelectAnchorPoint = cast(SelectAnchorPoint, iAnchor) 78 | if anchor.attachmentPoint != attachmentSide: 79 | anchor.protected = False 80 | anchor.Detach() 81 | -------------------------------------------------------------------------------- /archive/help/node7.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | About this document ... 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | next 31 | 32 | up 33 | 34 | previous 35 | 36 | contents 37 | 38 | index 39 |
40 | Up: PyUt 1.4 help 41 | Previous: Guided sample 42 |   Contents 43 |   Index 44 |
45 |
46 | 47 | 48 |

49 | About this document ... 50 |

51 |

52 | This document was generated using the 53 | LaTeX2HTML translator Version 2002 (1.62) 54 |

55 | Copyright © 1993, 1994, 1995, 1996, 56 | Nikos Drakos, 57 | Computer Based Learning Unit, University of Leeds. 58 |
59 | Copyright © 1997, 1998, 1999, 60 | Ross Moore, 61 | Mathematics Department, Macquarie University, Sydney. 62 |

63 | The command line arguments were:
64 | latex2html 65 | -dir final -index index.html -split 4 -link 2 -title 'PyUt 1.3 help' -mkdir -antialias_text -noexternal_images -noparbox_images 66 | -local_icons main.tex 67 |

68 | The translation was initiated by Dutoit Cedric on 2003-06-29 69 |
70 |


71 |
72 | Dutoit Cedric 73 | 2003-06-29 74 |
75 | 76 | 77 | -------------------------------------------------------------------------------- /pyut/ui/umlframes/UmlSequenceDiagramsFrame.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | from pyut.ui.umlframes.UmlDiagramsFrame import UmlDiagramsFrame 6 | 7 | from pyutmodelv2.PyutSDInstance import PyutSDInstance 8 | from pyutmodelv2.PyutSDMessage import PyutSDMessage 9 | 10 | from ogl.sd.OglSDInstance import OglSDInstance 11 | from ogl.sd.OglSDMessage import OglSDMessage 12 | 13 | from pyut.ui.eventengine.IEventEngine import IEventEngine 14 | 15 | 16 | class UmlSequenceDiagramsFrame(UmlDiagramsFrame): 17 | """ 18 | UmlSequenceDiagramsFrame : a UML sequence diagram frame. 19 | 20 | This class is the instance of one UML sequence diagram structure. 21 | It derives its functionality from UmlDiagramsFrame, but 22 | it knows the structure of a sequence diagram, 23 | It can load sequence diagram data 24 | 25 | Note on data 26 | - sdInstances is a set of class diagram instances, 27 | composed by label and lifeline 28 | """ 29 | cdfDebugId: int = 0x00FFF # UML Sequence Diagrams Frame Debug ID 30 | 31 | def __init__(self, parent, eventEngine: IEventEngine | None = None): 32 | """ 33 | 34 | Args: 35 | parent: The parent window 36 | eventEngine: Pyut event engine 37 | """ 38 | super().__init__(parent, eventEngine=eventEngine) # type: ignore 39 | 40 | self._seqLogger: Logger = getLogger(__name__) 41 | self._cdfDebugId: int = UmlSequenceDiagramsFrame.cdfDebugId 42 | 43 | UmlSequenceDiagramsFrame.cdfDebugId += 1 44 | 45 | # self.clearDiagram() 46 | # self._cdInstances = [] # type: ignore 47 | 48 | # noinspection PyUnusedLocal 49 | def createNewSDInstance(self, x, y) -> OglSDInstance: 50 | """ 51 | Create a new sequence diagram instance 52 | """ 53 | # Create and add instance 54 | pyutSDInstance: PyutSDInstance = PyutSDInstance() 55 | oglSDInstance: OglSDInstance = OglSDInstance(pyutSDInstance=pyutSDInstance) 56 | self.addShape(oglSDInstance, x, oglSDInstance.GetPosition()[1]) 57 | 58 | self._seqLogger.info(f'Created {oglSDInstance}') 59 | return oglSDInstance 60 | 61 | def createNewLink(self, src, dst, srcPos=None, dstPos=None): 62 | """ 63 | Adds an OglSDMessage link between src and dst. 64 | 65 | Args: 66 | src: source of the link 67 | dst: destination of the link 68 | srcPos: position on source 69 | dstPos: position on destination 70 | 71 | Returns: the created OglSDMessage link 72 | """ 73 | srcTime = src.ConvertCoordToRelative(0, srcPos[1])[1] 74 | dstTime = dst.ConvertCoordToRelative(0, dstPos[1])[1] 75 | pyutLink = PyutSDMessage("msg test", src.getPyutObject(), srcTime, dst.getPyutObject(), dstTime) 76 | 77 | oglLink = OglSDMessage(src, pyutLink, dst) 78 | # pyutLink.setOglObject(oglLink) 79 | 80 | src.addLink(oglLink) 81 | dst.addLink(oglLink) 82 | self._diagram.AddShape(oglLink) 83 | 84 | self.Refresh() 85 | 86 | self._seqLogger.info(f'Created {oglLink}') 87 | 88 | return oglLink 89 | 90 | def __repr__(self) -> str: 91 | 92 | debugId: str = f'0x{self._cdfDebugId:06X}' 93 | return f'UmlSequenceDiagramsFrame:[{debugId=}]' 94 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/BaseEditParamFieldDialog.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import cast 3 | 4 | from logging import Logger 5 | from logging import getLogger 6 | 7 | from wx import EVT_TEXT 8 | from wx import ID_ANY 9 | from wx import RA_SPECIFY_ROWS 10 | 11 | from wx import Colour 12 | from wx import CommandEvent 13 | from wx import DefaultSize 14 | from wx import Point 15 | from wx import RadioBox 16 | from wx import StaticText 17 | from wx import TextCtrl 18 | from wx import Window 19 | 20 | from wx.lib.sized_controls import SizedPanel 21 | 22 | from pyut.ui.dialogs.BaseEditDialog import BaseEditDialog 23 | 24 | 25 | class BaseEditParamFieldDialog(BaseEditDialog): 26 | """ 27 | Common class for laying out controls to edit either a field or a parameter 28 | """ 29 | basePFDLogger: Logger = getLogger(__name__) 30 | 31 | def __init__(self, parent: Window, title, layoutField: bool = True): 32 | """ 33 | 34 | Args: 35 | parent: 36 | title: The dialog title appropriate for this invocation of the editor 37 | layoutField: If 'True' includes the visibility radio buttons for a field 38 | """ 39 | 40 | super().__init__(parent, title=title) 41 | 42 | self._rdbVisibility: RadioBox = cast(RadioBox, None) 43 | self._name: TextCtrl = cast(TextCtrl, None) 44 | self._type: TextCtrl = cast(TextCtrl, None) 45 | self._defaultValue: TextCtrl = cast(TextCtrl, None) 46 | 47 | sizedPanel: SizedPanel = self.GetContentsPane() 48 | 49 | self._layoutEditControls(sizedPanel, layoutField) 50 | self._layoutStandardOkCancelButtonSizer() 51 | 52 | self._normalNameBackgroundColour: Colour = self._name.GetBackgroundColour() 53 | 54 | self.basePFDLogger.warning(f'{self._normalNameBackgroundColour=}') 55 | 56 | self.Bind(EVT_TEXT, self._onNameChange, self._name) 57 | self.basePFDLogger.info(f'Name change event is registered') 58 | 59 | def _layoutEditControls(self, parent: SizedPanel, layoutField: bool): 60 | 61 | controlsPanel: SizedPanel = SizedPanel(parent) 62 | controlsPanel.SetSizerType('horizontal') 63 | 64 | if layoutField is True: 65 | self._rdbVisibility = RadioBox(controlsPanel, ID_ANY, "", Point(35, 30), DefaultSize, ["+", "-", "#"], style=RA_SPECIFY_ROWS) 66 | 67 | gridPanel: SizedPanel = SizedPanel(parent=controlsPanel) 68 | gridPanel.SetSizerType("grid", {"cols": 3}) # 3-column grid layout 69 | 70 | StaticText(gridPanel, label="Name").SetSizerProps(proportion=1) 71 | StaticText(gridPanel, label="Type").SetSizerProps(proportion=1) 72 | StaticText(gridPanel, label="Default Value").SetSizerProps(proportion=1) 73 | 74 | self._name = TextCtrl(gridPanel, value="", size=(140, -1)) # 75 | self._type = TextCtrl(gridPanel, value="", size=(100, -1)) # 76 | self._defaultValue = TextCtrl(gridPanel, value="", size=(80, -1)) # 77 | 78 | # noinspection PyUnusedLocal 79 | def _onNameChange(self, event: CommandEvent): 80 | updatedName: str = self._name.GetValue().strip() 81 | self.basePFDLogger.warning(f'{updatedName=}') 82 | if self._name.GetValue().strip() == '': 83 | self._indicateEmptyTextCtrl(name=self._name) 84 | else: 85 | self._indicateNonEmptyTextCtrl(name=self._name, normalBackgroundColor=self._normalNameBackgroundColour) 86 | -------------------------------------------------------------------------------- /pyut/ui/menuhandlers/HelpMenuHandler.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | 5 | from wx import CommandEvent 6 | from wx import Menu 7 | from wx import BeginBusyCursor as wxBeginBusyCursor 8 | from wx import EndBusyCursor as wxEndBusyCursor 9 | from wx import Yield as wxYield 10 | 11 | from semantic_version import Version as SemanticVersion 12 | 13 | from pyut import __version__ as pyutVersion 14 | 15 | from pyut.ui.dialogs.DlgAbout import DlgAbout 16 | from pyut.ui.dialogs.logcontrol.DlgLogControl import DlgLogControl 17 | from pyut.ui.eventengine.inspector.DlgEventEngineDialog import DlgEventEngineDialog 18 | 19 | from pyut.ui.menuhandlers.BaseMenuHandler import BaseMenuHandler 20 | 21 | from pyut.PyutUtils import PyutUtils 22 | 23 | from pyut.ui.eventengine.IEventEngine import IEventEngine 24 | 25 | 26 | class HelpMenuHandler(BaseMenuHandler): 27 | 28 | PYUT_WIKI: str = 'https://github.com/hasii2011/PyUt/wiki/' 29 | 30 | def __init__(self, helpMenu: Menu, eventEngine: IEventEngine | None = None): 31 | 32 | super().__init__(menu=helpMenu, eventEngine=eventEngine) 33 | 34 | self.logger: Logger = getLogger(__name__) 35 | 36 | # noinspection PyUnusedLocal 37 | def onAbout(self, event: CommandEvent): 38 | """ 39 | Show the Pyut about dialog 40 | 41 | Args: 42 | event: 43 | """ 44 | with DlgAbout(self._parent) as dlg: 45 | dlg.ShowModal() 46 | 47 | # noinspection PyUnusedLocal 48 | def onHelpVersion(self, event: CommandEvent): 49 | """ 50 | Check for newer version. 51 | Args: 52 | event: 53 | """ 54 | from pyut.general.GitHubAdapter import GitHubAdapter 55 | 56 | wxBeginBusyCursor() 57 | githubAdapter: GitHubAdapter = GitHubAdapter() 58 | latestVersion: SemanticVersion = githubAdapter.getLatestVersionNumber() 59 | 60 | myVersion: SemanticVersion = SemanticVersion(pyutVersion) 61 | # latestVersion.major = 9 Manual test 62 | if myVersion < latestVersion: 63 | msg = f"PyUt version {str(latestVersion)} is available on https://github.com/hasii2011/PyUt/releases" 64 | else: 65 | msg = "No newer version yet !" 66 | 67 | wxEndBusyCursor() 68 | wxYield() 69 | PyutUtils.displayInformation(msg, "Check for newer version", self._parent) 70 | 71 | # noinspection PyUnusedLocal 72 | def onHelpWeb(self, event: CommandEvent): 73 | """ 74 | 75 | Args: 76 | event: 77 | """ 78 | PyutUtils.displayInformation(f"Please point your browser to {HelpMenuHandler.PYUT_WIKI}", "The new Pyut Wiki", self._parent) 79 | 80 | # noinspection PyUnusedLocal 81 | def onDebug(self, event: CommandEvent): 82 | """ 83 | Open a dialog to access the Pyut loggers 84 | 85 | Args: 86 | event: 87 | """ 88 | with DlgLogControl(self._parent) as dlg: 89 | dlg.ShowModal() 90 | 91 | # noinspection PyUnusedLocal 92 | def onDebugEventEngine(self, event: CommandEvent): 93 | """ 94 | Open a dialog to access the Pyut event engine 95 | Args: 96 | event: 97 | """ 98 | with DlgEventEngineDialog(self._parent, eventEngine=self._eventEngine) as dlg: 99 | dlg.ShowModal() 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://github.com/hasii2011/code-ally-basic/blob/master/developer/agpl-license-web-badge-version-2-256x48.png "AGPL") 2 | 3 | [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/Naereen/StrapDown.js/graphs/commit-activity) 4 | [![CircleCI](https://dl.circleci.com/status-badge/img/gh/hasii2011/pyut/tree/master.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/hasii2011/PyUt/tree/master) 5 | 6 | Gitmoji 7 | 8 | 9 | [![CircleCI](https://dl.circleci.com/insights-snapshot/gh/hasii2011/pyut/master/main/badge.svg?window=30d)](https://app.circleci.com/insights/github/hasii2011/PyUt/workflows/main/overview?branch=master&reporting-window=last-30-days&insights-snapshot=true) 10 | 11 | [![forthebadge made-with-python](http://ForTheBadge.com/images/badges/made-with-python.svg)](https://www.python.org/) 12 | 13 | 14 | 𝓟 𝓨 𝓤 𝓣 stands for Python UML Tool. Actually, Pyut is only a class diagram editor 15 | 16 | 17 | This is a Python 3 version of a source forge project. This is the [original website](http://pyut.sourceforge.net/whatis.html) 18 | 19 | See [documentation](https://github.com/hasii2011/PyUt/wiki) 20 | 21 | _Some Updates_ 22 | 23 | * Improved UML sequence diagram creation 24 | * Mermaid Support 25 | * Better support for importing Python code 26 | * Better support for orthogonal lines 27 | * New Orthogonal layout engine 28 | * An improved PDF and image generation capability (image is alpha) 29 | * Additional display options for UML diagrams 30 | * More internal parameters externalized through preferences 31 | * Updated to Python 3.11.5 and wxPython 4.2 32 | * Improved L & F for mac OS X 33 | * Source code completely re-organized for maintainability 34 | * Apple code signed for easy of use on OS X 35 | 36 | 37 | 38 | 39 | --------- 40 | I asked the original developers if it was Ok to fork this project. One developer replied: 41 | 42 | 43 | >> Hello, 44 | >> 45 | >> This project is no longer maintained. 46 | >> I think that you can fork this project and upgrade it depending on (sic) your needs, but please keep it free ;-) 47 | >> 48 | >> Good luck and keep us updated if you do the fork 49 | >> 50 | >> Cédric D 51 | 52 | 53 | This is the fork. I let Cedric know. 54 | 55 | 56 | ## Developer Notes 57 | This project uses [buildlackey](https://github.com/hasii2011/buildlackey) for day-to-day development builds 58 | 59 | ___ 60 | 61 | Updated and modified by Humberto A. Sanchez II (C) 2025 62 | 63 | --- 64 | 65 | 66 | 67 | ![Humberto's Modified Logo](https://raw.githubusercontent.com/wiki/hasii2011/gittodoistclone/images/SillyGitHub.png) 68 | 69 | I am concerned about GitHub's Copilot project 70 | 71 | 72 | 73 | I urge you to read about the 74 | [Give up GitHub](https://GiveUpGitHub.org) campaign from 75 | [the Software Freedom Conservancy](https://sfconservancy.org). 76 | 77 | While I do not advocate for all the issues listed there, I do not like that 78 | a company like Microsoft may profit from open source projects. 79 | 80 | I continue to use GitHub because it offers the services I need for free. But I continue 81 | to monitor their terms of service. 82 | 83 | Any use of this project's code by GitHub Copilot, past or present, is done 84 | without my permission. I do not consent to GitHub's use of this project's 85 | code in Copilot. 86 | -------------------------------------------------------------------------------- /tests/pyut/errorcontroller/TestErrorManager.py: -------------------------------------------------------------------------------- 1 | 2 | from unittest import TestCase 3 | from unittest import main as unitTestMain 4 | from unittest import TestSuite 5 | 6 | from os import linesep as osLineSep 7 | 8 | from pathlib import Path 9 | 10 | from pyut.PyutConstants import PyutConstants 11 | from pyut.errorcontroller.ErrorViewType import ErrorViewType 12 | from pyut.errorcontroller.IErrorView import IErrorView 13 | from pyut.errorcontroller.TextErrorView import TextErrorView 14 | from pyut.preferences.PyutPreferences import PyutPreferences 15 | 16 | from pyut.errorcontroller.ErrorManager import ErrorManager 17 | 18 | EXPECTED_TEST_OUTPUT: str = ( 19 | f'ERROR: ErrorManager: --------------------------------------------------------------------{osLineSep}' 20 | f'ERROR: ErrorManager: A Test Log Entry Title - This is only a test error message{osLineSep}' 21 | ) 22 | 23 | TEST_LOG_FILE_NAME: str = '/tmp/testErrorManager.log' 24 | TEST_LOG_FILE_PATH: Path = Path(TEST_LOG_FILE_NAME) 25 | 26 | 27 | class TestErrorManager(TestCase): 28 | """ 29 | Subclass from basic TestCase so we can set up our own logging 30 | """ 31 | 32 | def setUp(self): 33 | 34 | super().setUp() 35 | preferences: PyutPreferences = PyutPreferences() 36 | self._saveErrorViewType: ErrorViewType = preferences.errorViewType 37 | 38 | def tearDown(self): 39 | 40 | super().tearDown() 41 | preferences: PyutPreferences = PyutPreferences() 42 | preferences.errorViewType = self._saveErrorViewType 43 | 44 | def testAddToLogFile(self): 45 | 46 | self._setupAddToLogFileTest() 47 | ErrorManager.addToLogFile(title='A Test Log Entry Title', msg='This is only a test error message') 48 | 49 | actualTestOutput: str = TEST_LOG_FILE_PATH.read_text() 50 | 51 | self.assertEqual(EXPECTED_TEST_OUTPUT, actualTestOutput, 'Our test did not generated good stuff') 52 | 53 | def testChangingViewType(self): 54 | errorManger: ErrorManager = ErrorManager() 55 | 56 | errorManger.errorViewType = ErrorViewType.RAISE_ERROR_VIEW 57 | 58 | self.assertEqual(ErrorViewType.RAISE_ERROR_VIEW, errorManger.errorViewType, 'Incorrect error type') 59 | 60 | def testCorrectViewSet(self): 61 | 62 | errorManger: ErrorManager = ErrorManager() 63 | 64 | errorManger.errorViewType = ErrorViewType.TEXT_ERROR_VIEW 65 | 66 | view: IErrorView = errorManger._errorView 67 | 68 | self.assertTrue(isinstance(view, TextErrorView)) 69 | 70 | def _setupAddToLogFileTest(self): 71 | from logging import getLogger 72 | from logging import FileHandler 73 | from logging import Formatter 74 | from logging import Logger 75 | from logging import DEBUG 76 | 77 | # noinspection SpellCheckingInspection 78 | logger: Logger = getLogger(PyutConstants.MAIN_LOGGING_NAME) 79 | fh: FileHandler = FileHandler(TEST_LOG_FILE_NAME, mode='w') 80 | formatter: Formatter = Formatter('%(levelname)s: %(module)s: %(message)s') 81 | 82 | logger.setLevel(DEBUG) 83 | 84 | fh.setLevel(DEBUG) 85 | 86 | fh.setFormatter(formatter) 87 | 88 | logger.addHandler(fh) 89 | 90 | 91 | def suite() -> TestSuite: 92 | 93 | import unittest 94 | 95 | testSuite: TestSuite = TestSuite() 96 | 97 | testSuite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(testCaseClass=TestErrorManager)) 98 | 99 | return testSuite 100 | 101 | 102 | if __name__ == '__main__': 103 | unitTestMain() 104 | -------------------------------------------------------------------------------- /pyut/ui/dialogs/DlgEditProjectHistory.py: -------------------------------------------------------------------------------- 1 | 2 | from logging import Logger 3 | from logging import getLogger 4 | from pathlib import Path 5 | from typing import List 6 | from typing import Tuple 7 | from typing import cast 8 | 9 | from wx import CheckListBox 10 | from wx import CommandEvent 11 | from wx import FileHistory 12 | from wx import ID_ANY 13 | from wx import Size 14 | from wx.lib.sized_controls import SizedPanel 15 | from wx.lib.sized_controls import SizedStaticBox 16 | 17 | from pyut.preferences.PyutPreferences import PyutPreferences 18 | 19 | from pyut.ui.dialogs.BaseEditDialog import BaseEditDialog 20 | from pyut.ui.dialogs.BaseEditDialog import CustomDialogButton 21 | from pyut.ui.dialogs.BaseEditDialog import CustomDialogButtons 22 | 23 | 24 | class DlgEditProjectHistory(BaseEditDialog): 25 | def __init__(self, parent, fileHistory: FileHistory, title='Edit Recently Opened'): 26 | 27 | super().__init__(parent, title=title) 28 | 29 | self.logger: Logger = getLogger(__name__) 30 | 31 | self._fileHistory: FileHistory = fileHistory 32 | sizedPanel: SizedPanel = self.GetContentsPane() 33 | 34 | self._recentProjects: CheckListBox = cast(CheckListBox, None) 35 | 36 | self._layoutSelectionControls(parent=sizedPanel) 37 | 38 | customDialogButton: CustomDialogButton = CustomDialogButton() 39 | customDialogButton.label = 'Clear &All' 40 | customDialogButton.callback = self._onClearAll 41 | self._layoutCustomDialogButtonContainer(parent=sizedPanel, customButtons=CustomDialogButtons([customDialogButton])) 42 | 43 | self.Fit() 44 | self.SetMinSize(self.GetSize()) 45 | 46 | def _layoutSelectionControls(self, parent: SizedPanel): 47 | 48 | selectionPanel: SizedStaticBox = SizedStaticBox(parent, label='Select Recent Projects to Forget') 49 | selectionPanel.SetSizerType('horizontal') 50 | selectionPanel.SetSizerProps(expand=True, proportion=1) 51 | 52 | files: List[str] = [] 53 | fhCount: int = self._fileHistory.GetCount() 54 | 55 | showProjectExtension: bool = PyutPreferences().displayProjectExtension 56 | for i in range(fhCount): 57 | fName: str = self._fileHistory.GetHistoryFile(i) 58 | path: Path = Path(fName) 59 | if showProjectExtension is True: 60 | shortName: str = path.name 61 | else: 62 | shortName = path.stem 63 | files.append(shortName) 64 | 65 | self._recentProjects = CheckListBox(parent=selectionPanel, id=ID_ANY, choices=files, size=Size(width=250, height=200)) 66 | 67 | # noinspection PyUnusedLocal 68 | def _onClearAll(self, event: CommandEvent): 69 | nEntries: int = self._fileHistory.GetCount() 70 | self.logger.info(f'Recent Project History -- clearing {nEntries} entries') 71 | while nEntries > 0: 72 | self._fileHistory.RemoveFileFromHistory(0) 73 | nEntries -= 1 74 | 75 | super()._onOk(event) 76 | 77 | def _onOk(self, event: CommandEvent): 78 | 79 | idxOfItemsToRemove: Tuple[int] = self._recentProjects.GetCheckedItems() 80 | 81 | iteration: int = 0 82 | # Since the list is one less on each list traversal, 83 | # we have to adjust the removal index on each iteration 84 | # through the list 85 | for idx in idxOfItemsToRemove: 86 | idxToRemove: int = idx - iteration 87 | self.logger.info(f'Removing file history entry {self._fileHistory.GetHistoryFile(idxToRemove)}') 88 | self._fileHistory.RemoveFileFromHistory(idxToRemove) 89 | iteration += 1 90 | super()._onOk(event) 91 | -------------------------------------------------------------------------------- /archive/CREDITS: -------------------------------------------------------------------------------- 1 | PyUt Member(s) PyUt 1.8 (2020) 2 | Humberto A. Sanchez II Humberto.A.Sanchez.II@gmail.com 3 | 4 | wxpython update, Lots plugins made to work again; Internal maintainability changes 5 | 6 | PyUt Member(s) PyUt 1.5, 1.6, 1.7 (2019) 7 | ===== 8 | Humberto A. Sanchez II Humberto.A.Sanchez.II@gmail.com 9 | Python 3 update; Fork to comply with PEP-8, packaging, typing, and latest wxPython API 10 | 11 | 12 | 13 | Here is the PyUt members list for PyUt 1.4 (feb 2006 - ?) 14 | Name Mail Role/Position 15 | ==== ==== ============= 16 | T.Gagnebin thierry.gagnebin@eivd.ch Project owner for EIVD 17 | C.Dutoit dutoitc@shimbawa.ch Project manager 18 | P. Dabrowsky (private) Patch submitter 19 | (tag RELEASE-1-4) 20 | 21 | 22 | 23 | Here is the PyUt members list for PyUt 1.3 (November 2002 - jan 2006) 24 | 25 | Name Mail Role/Position 26 | ==== ==== ============= 27 | T.Gagnebin thierry.gagnebin@eivd.ch Project owner for EIVD 28 | L.Burgbacher lb@alawa.ch Developer 29 | C.Dutoit dutoitc@hotmail.com Developer 30 | 31 | F.Domingues fernandojd@hotmail.com Portuguese translator 32 | 33 | 34 | (tag RELEASE-1-3-INIT) 35 | ------------------------------------------------------------------------------ 36 | ------------------------------------------------------------------------------ 37 | ------------------------------------------------------------------------------ 38 | 39 | Here is the PyUt members list for PyUt 1.2 (July 2002 - november 2002) 40 | 41 | Name Mail Role/Position 42 | ==== ==== ============= 43 | T.Gagnebin thierry.gagnebin@eivd.ch Project owner for EIVD 44 | L.Burgbacher lb@alawa.ch Developer 45 | C.Dutoit dutoitc@hotmail.com Developer, release manager 46 | 47 | S.Drees on demand German translator 48 | 49 | 50 | 51 | 52 | ------------------------------------------------------------------------------ 53 | ------------------------------------------------------------------------------ 54 | ------------------------------------------------------------------------------ 55 | 56 | Here is the PyUt members list for PyUt 1.1 (March 2002 - July 2002) 57 | 58 | Name Mail Role/Position 59 | ==== ==== ============= 60 | C.Dutoit dutoitc@hotmail.com Project manager, developer 61 | D.Roux droux@eivd.ch Co-Project manager, CVS, Configuration, 62 | Releases, developer 63 | P.Waelti pwaelti@eivd.ch Documentation, Quality, developer 64 | L.Burgbacher lb@alawa.ch Tests, developer 65 | N.Dubois ndubois@eivd.ch Internationalization, developer 66 | N.Hamadi nhamadi@eivd.ch feedback, developer 67 | 68 | 69 | ------------------------------------------------------------------------------ 70 | ------------------------------------------------------------------------------ 71 | ------------------------------------------------------------------------------ 72 | 73 | Here is the PyUt members list for PyUt 1.0 (October 2001 - March 2002) 74 | Name Mail Role/Position 75 | ==== ==== ============= 76 | L.Burgbacher lb@alawa.ch Project manager, .exe packager, developer 77 | P.Waelti pwaelti@eivd.ch Co-Project manager, developer 78 | N.Dubois ndubois@eivd.ch Developer 79 | C.Dutoit dutoitc@hotmail.com Doc writer, .rpm/.tar.gz packager 80 | N.Hamadi nhamadi@eivd.ch Tester, Developer 81 | D.Roux droux@eivd.ch Developer 82 | 83 | -------------------------------------------------------------------------------- /pyut/ui/PluginProjectCreator.py: -------------------------------------------------------------------------------- 1 | from typing import cast 2 | 3 | from logging import Logger 4 | from logging import getLogger 5 | 6 | from ogl.OglInterface2 import OglInterface2 7 | from ogl.OglClass import OglClass 8 | from ogl.OglLink import OglLink 9 | from ogl.OglNote import OglNote 10 | from ogl.OglText import OglText 11 | from ogl.OglActor import OglActor 12 | from ogl.OglUseCase import OglUseCase 13 | from ogl.sd.OglSDInstance import OglSDInstance 14 | from ogl.sd.OglSDMessage import OglSDMessage 15 | from oglio import OglVersion 16 | 17 | from pyutplugins.ExternalTypes import PluginDocument 18 | from pyutplugins.ExternalTypes import PluginDocumentTitle 19 | from pyutplugins.ExternalTypes import PluginDocumentType 20 | from pyutplugins.ExternalTypes import PluginProject 21 | 22 | from pyut.ui.IPyutDocument import IPyutDocument 23 | from pyut.ui.IPyutProject import IPyutProject 24 | from pyut.ui.Types import UmlFrameType 25 | 26 | 27 | class PluginProjectCreator: 28 | def __init__(self): 29 | self.logger: Logger = getLogger(__name__) 30 | 31 | def toPluginProject(self, pyutProject: IPyutProject) -> PluginProject: 32 | pass 33 | 34 | pluginProject: PluginProject = PluginProject() 35 | 36 | pluginProject.projectName = pyutProject.projectName 37 | pluginProject.fileName = pyutProject.filename 38 | pluginProject.codePath = pyutProject.codePath 39 | pluginProject.version = OglVersion.version 40 | for document in pyutProject.documents: 41 | pyutDocument: IPyutDocument = cast(IPyutDocument, document) 42 | pluginDocument: PluginDocument = PluginDocument() 43 | 44 | pluginDocument.documentType = PluginDocumentType.toEnum(pyutDocument.diagramType.name) 45 | pluginDocument.documentTitle = PluginDocumentTitle(pyutDocument.title) 46 | 47 | diagramFrame: UmlFrameType = pyutDocument.diagramFrame 48 | scrollPosX, scrollPosY = diagramFrame.GetViewStart() 49 | xUnit, yUnit = diagramFrame.GetScrollPixelsPerUnit() 50 | 51 | pluginDocument.scrollPositionX = scrollPosX 52 | pluginDocument.scrollPositionY = scrollPosY 53 | pluginDocument.pixelsPerUnitX = xUnit 54 | pluginDocument.pixelsPerUnitY = yUnit 55 | 56 | for umlObject in diagramFrame.umlObjects: 57 | match umlObject: 58 | case OglInterface2(): 59 | oglInterface2: OglInterface2 = cast(OglInterface2, umlObject) 60 | pluginDocument.oglLinks.append(oglInterface2) 61 | case OglSDInstance(): 62 | oglSDInstance: OglSDInstance = cast(OglSDInstance, umlObject) 63 | pluginDocument.oglSDInstances[oglSDInstance.pyutSDInstance.id] = oglSDInstance 64 | case OglSDMessage(): 65 | oglSDMessage: OglSDMessage = cast(OglSDMessage, umlObject) 66 | pluginDocument.oglSDMessages[oglSDMessage.pyutObject.id] = oglSDMessage 67 | case OglClass(): 68 | pluginDocument.oglClasses.append(umlObject) 69 | case OglLink(): 70 | pluginDocument.oglLinks.append(umlObject) 71 | case OglNote(): 72 | pluginDocument.oglNotes.append(umlObject) 73 | case OglText(): 74 | pluginDocument.oglTexts.append(umlObject) 75 | case OglActor(): 76 | pluginDocument.oglActors.append(umlObject) 77 | case OglUseCase(): 78 | pluginDocument.oglUseCases.append(umlObject) 79 | case _: 80 | self.logger.error(f'Unknown umlObject: {umlObject=}') 81 | 82 | pluginProject.pluginDocuments[pluginDocument.documentTitle] = pluginDocument 83 | 84 | return pluginProject 85 | --------------------------------------------------------------------------------