コード例 #1
0
ファイル: MainUI.py プロジェクト: curiousTauseef/PyUt
class MainUI:
    """
    The main portion of the User Interface.
    Used by the main application frame (AppFrame) to host all UML frames,
    the notebook and the project tree.

    Handles the the project files, projects, documents and
    their relationship to the various UI Tree elements and the
    notebook tabs in the UI

    All actions called from AppFrame are executed on the current frame
    """
    MAX_NOTEBOOK_PAGE_NAME_LENGTH: int = 12

    def __init__(self, parent, mediator):
        """

        Args:
            parent:     An AppFrame
            mediator:   Our one and only mediator
        """
        self.logger: Logger = getLogger(__name__)

        from org.pyut.ui.AppFrame import AppFrame   # Prevent recursion import problem
        from org.pyut.general.Mediator import Mediator
        self.__parent:  AppFrame = parent
        self._mediator: Mediator = mediator

        self._projects:       List[PyutProject] = []
        self._currentProject: PyutProject       = cast(PyutProject, None)
        self._currentFrame:   UmlDiagramsFrame  = cast(UmlDiagramsFrame, None)

        if not self._mediator.isInScriptMode():

            self.__splitter:          SplitterWindow = cast(SplitterWindow, None)
            self.__projectTree:       TreeCtrl       = cast(TreeCtrl, None)
            self.__projectTreeRoot:   TreeItemId     = cast(TreeItemId, None)
            self.__notebook:          Notebook       = cast(Notebook, None)
            self.__projectPopupMenu:  Menu = cast(Menu, None)
            self.__documentPopupMenu: Menu = cast(Menu, None)

            self._initializeUIElements()

    def registerUmlFrame(self, frame):
        """
        Register the current UML Frame

        Args:
            frame:
        """
        self._currentFrame = frame
        self._currentProject = self.getProjectFromFrame(frame)

    def showFrame(self, frame):
        self._frame = frame
        frame.Show()

    def getProjects(self):
        """

        Returns:
            Return all projects
        """
        return self._projects

    def isProjectLoaded(self, filename) -> bool:
        """

        Args:
            filename:

        Returns:
            `True` if the project is already loaded
        """
        for project in self._projects:
            if project.getFilename == filename:
                return True
        return False

    def isDefaultFilename(self, filename: str) -> bool:
        """

        Args:
            filename:

        Returns:
            `True` if the filename is the default filename
        """
        return filename == PyutConstants.DefaultFilename

    def openFile(self, filename, project=None) -> bool:
        """
        Open a file

        Args:
            filename:
            project:

        Returns:
            `True` if operation succeeded
        """
        # Exit if the file is already loaded
        if not self.isDefaultFilename(filename) and self.isProjectLoaded(filename):
            PyutUtils.displayError(_("The selected file is already loaded !"))
            return False

        # Create a new project ?
        if project is None:
            project = PyutProject(PyutConstants.DefaultFilename, self.__notebook, self.__projectTree, self.__projectTreeRoot)

        #  print ">>>FileHandling-openFile-3"
        # Load the project and add it
        try:
            if not project.loadFromFilename(filename):
                PyutUtils.displayError(_("The specified file can't be loaded !"))
                return False
            self._projects.append(project)
            #  self._ctrl.registerCurrentProject(project)
            self._currentProject = project
        except (ValueError, Exception) as e:
            PyutUtils.displayError(_(f"An error occurred while loading the project ! {e}"))
            return False

        try:
            if not self._mediator.isInScriptMode():
                for document in project.getDocuments():
                    diagramTitle: str = document.getTitle()
                    shortName:    str = self.shortenNotebookPageFileName(diagramTitle)
                    self.__notebook.AddPage(document.getFrame(), shortName)

                self.__notebookCurrentPage = self.__notebook.GetPageCount()-1
                self.__notebook.SetSelection(self.__notebookCurrentPage)

            if len(project.getDocuments()) > 0:
                self._currentFrame = project.getDocuments()[0].getFrame()

        except (ValueError, Exception) as e:
            PyutUtils.displayError(_(f"An error occurred while adding the project to the notebook {e}"))
            return False
        return True

    def insertFile(self, filename):
        """
        Insert a file in the current project

        Args:
            filename: filename of the project to insert

        """
        # Get current project
        project = self._currentProject

        # Save number of initial documents
        nbInitialDocuments = len(project.getDocuments())

        # Load data...
        if not project.insertProject(filename):
            PyutUtils.displayError(_("The specified file can't be loaded !"))
            return False

        # ...
        if not self._mediator.isInScriptMode():
            try:
                for document in project.getDocuments()[nbInitialDocuments:]:
                    self.__notebook.AddPage(document.getFrame(), document.getFullyQualifiedName())

                self.__notebookCurrentPage = self.__notebook.GetPageCount()-1
                self.__notebook.SetSelection(self.__notebookCurrentPage)
            except (ValueError, Exception) as e:
                PyutUtils.displayError(_(f"An error occurred while adding the project to the notebook {e}"))
                return False

        # Select first frame as current frame
        if len(project.getDocuments()) > nbInitialDocuments:
            self._frame = project.getDocuments()[nbInitialDocuments].getFrame()

    def saveFile(self) -> bool:
        """
        save to the current filename

        Returns:
            `True` if the save succeeds else `False`
        """
        currentProject = self._currentProject
        if currentProject is None:
            PyutUtils.displayError(_("No diagram to save !"), _("Error"))
            return False

        if currentProject.getFilename() is None or currentProject.getFilename() == PyutConstants.DefaultFilename:
            return self.saveFileAs()
        else:
            return currentProject.saveXmlPyut()

    def saveFileAs(self):
        """
        Ask for a filename and save the diagram data

        Returns:
            `True` if the save succeeds else `False`
        """
        if self._mediator.isInScriptMode():
            PyutUtils.displayError(_("Save File As is not accessible in script mode !"))
            return

        # Test if no diagram exists
        if self._mediator.getDiagram() is None:
            PyutUtils.displayError(_("No diagram to save !"), _("Error"))
            return

        # Ask for filename
        filenameOK = False
        # TODO revisit this to figure out how to get rid of Pycharm warning 'dlg referenced before assignment'
        # Bad thing is dlg can be either a FileDialog or a MessageDialog
        dlg: DialogType = cast(DialogType, None)
        while not filenameOK:
            dlg = FileDialog(self.__parent,
                             defaultDir=self.__parent.getCurrentDir(),
                             wildcard=_("Pyut file (*.put)|*.put"),
                             style=FD_SAVE | FD_OVERWRITE_PROMPT)

            # Return False if canceled
            if dlg.ShowModal() != ID_OK:
                dlg.Destroy()
                return False

            # Find if a specified filename is already opened
            filename = dlg.GetPath()

            if len([project for project in self._projects if project.getFilename() == filename]) > 0:
                dlg = MessageDialog(self.__parent,
                                    _("Error ! The filename '%s" + "' correspond to a project which is currently opened !" +
                                      " Please choose another filename !") % str(filename),
                                    _("Save change, filename error"), OK | ICON_ERROR)
                dlg.ShowModal()
                dlg.Destroy()
                return
            filenameOK = True

        project = self._currentProject
        project.setFilename(dlg.GetPath())
        project.saveXmlPyut()

        # Modify notebook text
        for i in range(self.__notebook.GetPageCount()):
            frame = self.__notebook.GetPage(i)
            document = [document for document in project.getDocuments() if document.getFrame() is frame]
            if len(document) > 0:
                document = document[0]
                if frame in project.getFrames():
                    diagramTitle: str = document.getTitle()
                    shortName:    str = self.shortenNotebookPageFileName(diagramTitle)

                    self.__notebook.SetPageText(i, shortName)
            else:
                self.logger.info("Not updating notebook in FileHandling")

        self.__parent.updateCurrentDir(dlg.GetPath())

        project.setModified(False)
        dlg.Destroy()
        return True

    def newProject(self):
        """
        Begin a new project
        """
        project = PyutProject(PyutConstants.DefaultFilename, self.__notebook, self.__projectTree, self.__projectTreeRoot)
        self._projects.append(project)
        self._currentProject = project
        self._currentFrame = None

    def newDocument(self, docType: DiagramType):
        """
        Begin a new document

        Args:
            docType:  Type of document
        """
        project = self._currentProject
        if project is None:
            self.newProject()
            project = self.getCurrentProject()
        frame = project.newDocument(docType).getFrame()
        self._currentFrame  = frame
        self._currentProject = project

        if not self._mediator.isInScriptMode():
            shortName: str = self.shortenNotebookPageFileName(project.getFilename())
            self.__notebook.AddPage(frame, shortName)
            wxYield()
            self.__notebookCurrentPage  = self.__notebook.GetPageCount() - 1
            self.logger.info(f'Current notebook page: {self.__notebookCurrentPage}')
            self.__notebook.SetSelection(self.__notebookCurrentPage)

    def getCurrentFrame(self):
        """

        Returns:
            Get the current frame
        """
        return self._currentFrame

    def getCurrentProject(self) -> PyutProject:
        """
        Get the current working project

        Returns:
            the current project or None if not found
        """
        return self._currentProject

    def getProjectFromFrame(self, frame: UmlDiagramsFrame) -> PyutProject:
        """
        Return the project that owns a given frame

        Args:
            frame:  the frame to get This project

        Returns:
            PyutProject or None if not found
        """
        for project in self._projects:
            if frame in project.getFrames():
                return project
        return cast(PyutProject, None)

    def getCurrentDocument(self) -> PyutDocument:
        """
        Get the current document.

        Returns:
            the current document or None if not found
        """
        project = self.getCurrentProject()
        if project is None:
            return cast(PyutDocument, None)
        for document in project.getDocuments():
            if document.getFrame() is self._currentFrame:
                return document
        return cast(PyutDocument, None)

    def onClose(self) -> bool:
        """
        Close all files

        Returns:
            True if everything is ok
        """
        # Display warning if we are in scripting mode
        if self._mediator.isInScriptMode():
            print("WARNING : in script mode, the non-saved projects are closed without warning")

        # Close projects and ask for unsaved but modified projects
        if not self._mediator.isInScriptMode():
            for project in self._projects:
                if project.getModified() is True:
                    frames = project.getFrames()
                    if len(frames) > 0:
                        frame = frames[0]
                        frame.SetFocus()
                        wxYield()
                        # if self._ctrl is not None:
                            # self._ctrl.registerUMLFrame(frame)
                        self.showFrame(frame)
                    dlg = MessageDialog(self.__parent,
                                        _("Your diagram has not been saved! Would you like to save it ?"),
                                        _("Save changes ?"), YES_NO | ICON_QUESTION)
                    if dlg.ShowModal() == ID_YES:
                        # save
                        if self.saveFile() is False:
                            return False
                    dlg.Destroy()

        # dereference all
        self.__parent = None
        self._mediator = None
        self.__splitter = None
        self.__projectTree = None
        self.__notebook.DeleteAllPages()
        self.__notebook = None
        self.__splitter = None
        self._projects = None
        self._currentProject = None
        self._currentFrame = None

    def setModified(self, theNewValue: bool = True):
        """
        Set the Modified flag of the currently opened diagram

        Args:
            theNewValue:

        """
        if self._currentProject is not None:
            self._currentProject.setModified(theNewValue)
        self._mediator.updateTitle()

    def closeCurrentProject(self):
        """
        Close the current project

        Returns:
            True if everything is ok
        """
        if self._currentProject is None and self._currentFrame is not None:
            self._currentProject = self.getProjectFromFrame(self._currentFrame)
        if self._currentProject is None:
            PyutUtils.displayError(_("No frame to close !"), _("Error..."))
            return False

        # Display warning if we are in scripting mode
        if self._mediator.isInScriptMode():
            self.logger.warning("WARNING : in script mode, the non-saved projects are closed without warning")

        # Close the file
        if self._currentProject.getModified() is True and not self._mediator.isInScriptMode():
            frame = self._currentProject.getFrames()[0]
            frame.SetFocus()
            self.showFrame(frame)

            dlg = MessageDialog(self.__parent, _("Your project has not been saved. " 
                                                 "Would you like to save it ?"), _("Save changes ?"), YES_NO | ICON_QUESTION)
            if dlg.ShowModal() == ID_YES:
                if self.saveFile() is False:
                    return False

        # Remove the frame in the notebook
        if not self._mediator.isInScriptMode():
            # Python 3 update
            pages = list(range(self.__notebook.GetPageCount()))
            pages.reverse()
            for i in pages:
                pageFrame = self.__notebook.GetPage(i)
                if pageFrame in self._currentProject.getFrames():
                    self.__notebook.DeletePage(i)

        self._currentProject.removeFromTree()
        self._projects.remove(self._currentProject)

        self._currentProject = None
        self._currentFrame = None

        return True

    def removeAllReferencesToUmlFrame(self, umlFrame):
        """
        Remove all my references to a given uml frame

        Args:
            umlFrame:

        """
        # Current frame ?
        if self._currentFrame is umlFrame:
            self._currentFrame = None

        # Exit if we are in scripting mode
        if self._mediator.isInScriptMode():
            return

        for i in range(self.__notebook.GetPageCount()):
            pageFrame = self.__notebook.GetPage(i)
            if pageFrame is umlFrame:
                self.__notebook.DeletePage(i)
                break

    def getProjectFromOglObjects(self, oglObjects) -> PyutProject:
        """
        Get a project that owns oglObjects

        Args:
            oglObjects: Objects to find their parents

        Returns:
            PyutProject if found, None else
        """
        for project in self._projects:
            for frame in project.getFrames():
                diagram = frame.getDiagram()
                shapes = diagram.GetShapes()
                for obj in oglObjects:
                    if obj in shapes:
                        self.logger.info(f'obj: {obj} is part of project: {project}')
                        return project

        self.logger.warning(f'The oglObjects: {oglObjects} appear to not belong to any project')
        return cast(PyutProject, None)

    def _initializeUIElements(self):
        """
        Instantiate all the UI elements
        """
        self.__splitter        = SplitterWindow(self.__parent, ID_ANY)
        self.__projectTree     = TreeCtrl(self.__splitter, ID_ANY, style=TR_HIDE_ROOT + TR_HAS_BUTTONS)
        self.__projectTreeRoot = self.__projectTree.AddRoot(_("Root"))

        #  self.__projectTree.SetPyData(self.__projectTreeRoot, None)
        # Expand root, since wx.TR_HIDE_ROOT is not supported under windows
        # Not supported for hidden tree since wx.Python 2.3.3.1 ?
        #  self.__projectTree.Expand(self.__projectTreeRoot)

        # diagram container
        self.__notebook = Notebook(self.__splitter, ID_ANY, style=CLIP_CHILDREN)

        # Set splitter
        self.__splitter.SetMinimumPaneSize(20)
        self.__splitter.SplitVertically(self.__projectTree, self.__notebook, 160)

        self.__notebookCurrentPage = -1

        # Callbacks
        self.__parent.Bind(EVT_NOTEBOOK_PAGE_CHANGED,      self.__onNotebookPageChanged)
        self.__parent.Bind(EVT_TREE_SEL_CHANGED,           self.__onProjectTreeSelChanged)
        self.__projectTree.Bind(EVT_TREE_ITEM_RIGHT_CLICK, self.__onProjectTreeRightClick)

    # noinspection PyUnusedLocal
    def __onNotebookPageChanged(self, event):
        """
        Callback for notebook page changed

        Args:
            event:
        """
        self.__notebookCurrentPage = self.__notebook.GetSelection()
        if self._mediator is not None:      # hasii maybe I got this right from the old pre PEP-8 code
            #  self._ctrl.registerUMLFrame(self._getCurrentFrame())
            self._currentFrame = self._getCurrentFrameFromNotebook()
            self.__parent.notifyTitleChanged()
        # self.__projectTree.SelectItem(getID(self.getCurrentFrame()))
        # TODO : how can I do getID ???

        # Register the current project
        self._currentProject = self.getProjectFromFrame(self._currentFrame)

    def __onProjectTreeSelChanged(self, event: TreeEvent):
        """
        Callback for notebook page changed

        Args:
            event:
        """
        itm: TreeItemId = event.GetItem()
        pyutData: TreeDataType = self.__projectTree.GetItemData(itm)
        self.logger.debug(f'Clicked on: `{pyutData}`')
        # Use our own base type
        if isinstance(pyutData, UmlDiagramsFrame):
            frame: UmlDiagramsFrame = pyutData
            self._currentFrame = frame
            self._currentProject = self.getProjectFromFrame(frame)

            # Select the frame in the notebook
            for i in range(self.__notebook.GetPageCount()):
                pageFrame = self.__notebook.GetPage(i)
                if pageFrame is frame:
                    self.__notebook.SetSelection(i)
                    return
        elif isinstance(pyutData, PyutProject):
            self._currentProject = pyutData

    def _getCurrentFrameFromNotebook(self):
        """
        Get the current frame in the notebook

        Returns:
        """
        # Return None if we are in scripting mode
        if self._mediator.isInScriptMode():
            return None

        noPage = self.__notebookCurrentPage
        if noPage == -1:
            return None
        frame = self.__notebook.GetPage(noPage)
        return frame

    def __onProjectTreeRightClick(self, treeEvent: TreeEvent):

        itemId: TreeItemId = treeEvent.GetItem()
        data = self.__projectTree.GetItemData(item=itemId)
        self.logger.info(f'Item Data: `{data}`')
        if isinstance(data, PyutProject):
            self.__popupProjectMenu()
        elif isinstance(data, UmlDiagramsFrame):
            self.__popupProjectDocumentMenu()

    def __popupProjectMenu(self):

        self._mediator.resetStatusText()

        if self.__projectPopupMenu is None:
            self.logger.info(f'Create the project popup menu')
            [closeProjectMenuID] = PyutUtils.assignID(1)
            popupMenu: Menu = Menu('Actions')
            popupMenu.AppendSeparator()
            popupMenu.Append(closeProjectMenuID, 'Close Project', 'Remove project from tree', ITEM_NORMAL)
            popupMenu.Bind(EVT_MENU, self.__onCloseProject, id=closeProjectMenuID)
            self.__projectPopupMenu = popupMenu

        self.logger.info(f'currentProject: `{self._currentProject}`')
        self.__parent.PopupMenu(self.__projectPopupMenu)

    def __popupProjectDocumentMenu(self):

        if self.__documentPopupMenu is None:

            self.logger.info(f'Create the document popup menu')

            [editDocumentNameMenuID, removeDocumentMenuID] = PyutUtils.assignID(2)

            popupMenu: Menu = Menu('Actions')
            popupMenu.AppendSeparator()
            popupMenu.Append(editDocumentNameMenuID, 'Edit Document Name', 'Change document name', ITEM_NORMAL)
            popupMenu.Append(removeDocumentMenuID,   'Remove Document',    'Delete it',            ITEM_NORMAL)

            popupMenu.Bind(EVT_MENU, self.__onEditDocumentName, id=editDocumentNameMenuID)
            popupMenu.Bind(EVT_MENU, self.__onRemoveDocument,   id=removeDocumentMenuID)

            self.__documentPopupMenu = popupMenu

        self.logger.info(f'Current Document: `{self.getCurrentDocument()}`')
        self.__parent.PopupMenu(self.__documentPopupMenu)

    # noinspection PyUnusedLocal
    def __onCloseProject(self, event: CommandEvent):
        self.closeCurrentProject()

    # noinspection PyUnusedLocal
    def __onEditDocumentName(self, event: CommandEvent):

        self.logger.info(f'self.__notebookCurrentPage: {self.__notebookCurrentPage} nb Selection: {self.__notebook.GetSelection()}')
        if self.__notebookCurrentPage == -1:
            self.__notebookCurrentPage = self.__notebook.GetSelection()    # must be default empty project

        currentDocument: PyutDocument = self.getCurrentDocument()
        dlgEditDocument: DlgEditDocument = DlgEditDocument(parent=self.getCurrentFrame(),
                                                           dialogIdentifier=ID_ANY,
                                                           document=currentDocument)
        dlgEditDocument.Destroy()

        self.__notebook.SetPageText(page=self.__notebookCurrentPage, text=currentDocument.title)
        currentDocument.updateTreeText()

    # noinspection PyUnusedLocal
    def __onRemoveDocument(self, event: CommandEvent):
        """
        Invoked from the popup menu in the tree

        Args:
            event:
        """
        project:         PyutProject  = self.getCurrentProject()
        currentDocument: PyutDocument = self.getCurrentDocument()
        project.removeDocument(currentDocument)

    def shortenNotebookPageFileName(self, filename: str) -> str:
        """
        Return a shorter filename to display; For file names longer
        than `MAX_NOTEBOOK_PAGE_NAME_LENGTH` this method takes the first
        four characters and the last eight as the shortened file name

        Args:
            filename:  The file name to display

        Returns:
            A better file name
        """
        justFileName: str = osPath.split(filename)[1]
        if len(justFileName) > MainUI.MAX_NOTEBOOK_PAGE_NAME_LENGTH:
            firstFour: str = justFileName[:4]
            lastEight: str = justFileName[-8:]
            return f'{firstFour}{lastEight}'
        else:
            return justFileName
コード例 #2
0
ファイル: WalkDirectory.py プロジェクト: DebyGe/3DWarehouse
 def wolkDirAppenTree(self, treeObj: wx.TreeCtrl, path):
     treeObj.DeleteAllItems()
     treeRoot = treeObj.AddRoot("Path")
     self.wolkDir(treeObj, treeRoot, path)
     treeObj.Expand(treeRoot)