From fdf41f383aeb1a2bdeb5ea7a7c378eeb69022c2b Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sat, 27 Apr 2019 14:49:54 +0200 Subject: [PATCH] Moved main menu to its own class, and reshuffled some of the logic in opening and saving projects --- nw/gui/mainmenu.py | 198 ++++++++++++++++++++++-------------------- nw/gui/winmain.py | 133 ++++++++++++++-------------- nw/project/project.py | 10 ++- 3 files changed, 175 insertions(+), 166 deletions(-) diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 250b6e43..806eb7d5 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -13,36 +13,31 @@ import logging import nw -from PyQt5.QtWidgets import QMenu -# from os import path -# from PyQt5.QtWidgets import qApp, QWidget, QMainWindow, QVBoxLayout, QFrame, QSplitter, QAction, QToolBar, QFileDialog, QStackedWidget -# from PyQt5.QtCore import Qt, QSize, pyqtSlot -# from PyQt5.QtGui import QIcon +from PyQt5.QtWidgets import qApp, QMenuBar, QAction +from PyQt5.QtGui import QIcon -# from nw.gui.doctree import GuiDocTree -# from nw.gui.doctreectx import GuiDocTreeCtx -# from nw.gui.doceditor import GuiDocEditor -# from nw.gui.docdetails import GuiDocDetails -# from nw.gui.projecteditor import GuiProjectEditor -# from nw.gui.statusbar import GuiMainStatus -# from nw.project.project import NWProject -# from nw.project.document import NWDoc -# from nw.project.item import NWItem -# from nw.enum import nwItemType, nwItemClass, nwDocAction, nwItemAction +from nw.enum import nwItemClass, nwDocAction logger = logging.getLogger(__name__) -class GuiMainMenu(QMenu): +class GuiMainMenu(QMenuBar): def __init__(self, theParent, theProject): - QMenu.__init__(self, theParent) + QMenuBar.__init__(self, theParent) logger.debug("Initialising Main Menu ...") - self.mainConf = nw.CONFIG - self.theProject = NWProject() - self.theDocument = NWDoc(self.theProject, self) + self.mainConf = nw.CONFIG + self.theParent = theParent + self.theProject = theProject - self._buildMenu() + self._buildProjectMenu() + self._buildItemMenu() + self._buildEditMenu() + self._buildFormatMenu() + self._buildHelpMenu() + + # Function Pointers + self._docAction = self.theParent.docEditor.docAction logger.debug("Main Menu initialisation complete") @@ -50,103 +45,116 @@ class GuiMainMenu(QMenu): def openRecentProject(self, menuItem, recentItem): logger.verbose("User requested opening recent project #%d" % recentItem) - self.theProject.openProject(self.mainConf.recentList[recentItem]) - self.treeView.buildTree() - self._setWindowTitle(self.theProject.projName) + self.theParent.openProject(self.mainConf.recentList[recentItem]) return True ## - # GUI Builders + # Menu Action ## - def _buildMenu(self): + def _menuExit(self): + self.theParent.closeMain() + qApp.quit() + return True + + def _showAbout(self): + self.docTabs.createTab(None,nw.DOCTYPE_ABOUT) + return True + + ## + # Menu Builders + ## + + def _buildProjectMenu(self): # Project - projMenu = self.addMenu("&Project") + self.projMenu = self.addMenu("&Project") # Project > New Project menuItem = QAction(QIcon.fromTheme("folder-new"), "New Project", self) menuItem.setStatusTip("Create New Project") - menuItem.triggered.connect(self.newProject) - projMenu.addAction(menuItem) + menuItem.triggered.connect(self.theParent.newProject) + self.projMenu.addAction(menuItem) # Project > Open Project menuItem = QAction(QIcon.fromTheme("folder-open"), "Open Project", self) menuItem.setStatusTip("Open Project") menuItem.setShortcut("Ctrl+Shift+O") - menuItem.triggered.connect(self.openProject) - projMenu.addAction(menuItem) + menuItem.triggered.connect(lambda : self.theParent.openProject(None)) + self.projMenu.addAction(menuItem) # Project > Save Project menuItem = QAction(QIcon.fromTheme("document-save"), "Save Project", self) menuItem.setStatusTip("Save Project") menuItem.setShortcut("Ctrl+Shift+S") - menuItem.triggered.connect(self.saveProject) - projMenu.addAction(menuItem) + menuItem.triggered.connect(self.theParent.saveProject) + self.projMenu.addAction(menuItem) # Project > Recent Projects - recentMenu = projMenu.addMenu(QIcon.fromTheme("document-open-recent"),"Recent Projects") + recentMenu = self.projMenu.addMenu(QIcon.fromTheme("document-open-recent"),"Recent Projects") for n in range(len(self.mainConf.recentList)): recentProject = self.mainConf.recentList[n] if recentProject == "": continue - menuItem = QAction(QIcon.fromTheme("folder-open"), "%d: %s" % (n,recentProject), projMenu) + menuItem = QAction(QIcon.fromTheme("folder-open"), "%d: %s" % (n,recentProject), self.projMenu) menuItem.triggered.connect(lambda menuItem, n=n : self.openRecentProject(menuItem, n)) recentMenu.addAction(menuItem) # Project > Separator - projMenu.addSeparator() + self.projMenu.addSeparator() # Project > Project Settings menuItem = QAction(QIcon.fromTheme("document-properties"), "Project Settings", self) menuItem.setStatusTip("Project Settings") - menuItem.triggered.connect(self.editProject) - projMenu.addAction(menuItem) + menuItem.triggered.connect(self.theParent.editProjectDialog) + self.projMenu.addAction(menuItem) # Project > Separator - projMenu.addSeparator() + self.projMenu.addSeparator() # Project > Exit menuItem = QAction(QIcon.fromTheme("application-exit"), "Exit", self) menuItem.setStatusTip("Exit %s" % nw.__package__) menuItem.setShortcut("Ctrl+Q") menuItem.triggered.connect(self._menuExit) - projMenu.addAction(menuItem) + self.projMenu.addAction(menuItem) - ############################################################################################ + return + + def _buildItemMenu(self): # Item - itemMenu = self.addMenu("&Item") + self.itemMenu = self.addMenu("&Item") # Item > New menuItem = QAction(QIcon.fromTheme("document-new"), "&New Document", self) menuItem.setStatusTip("Create New Document") menuItem.setShortcut("Ctrl+N") - itemMenu.addAction(menuItem) + self.itemMenu.addAction(menuItem) # Item > Open menuItem = QAction(QIcon.fromTheme("document-open"), "&Open Document", self) menuItem.setStatusTip("Open Selected Document") menuItem.setShortcut("Ctrl+O") - itemMenu.addAction(menuItem) + self.itemMenu.addAction(menuItem) # Item > Save menuItem = QAction(QIcon.fromTheme("document-save"), "&Save Document", self) menuItem.setStatusTip("Save Current Document") menuItem.setShortcut("Ctrl+S") - menuItem.triggered.connect(self.saveDocument) - itemMenu.addAction(menuItem) + menuItem.triggered.connect(self.theParent.saveDocument) + self.itemMenu.addAction(menuItem) # Item > Separator - itemMenu.addSeparator() + self.itemMenu.addSeparator() # Item > New Folder menuItem = QAction(QIcon.fromTheme("folder-new"), "New Folder", self) menuItem.setStatusTip("New Folder") menuItem.setShortcut("Ctrl+Shift+N") - itemMenu.addAction(menuItem) + self.itemMenu.addAction(menuItem) # Item > New Root - rootMenu = itemMenu.addMenu(QIcon.fromTheme("folder-new"), "Create Root Group") + rootMenu = self.itemMenu.addMenu(QIcon.fromTheme("folder-new"), "Create Root Group") self.rootItems = {} self.rootItems[nwItemClass.NOVEL] = QAction(QIcon.fromTheme("folder-new"), "Novel Root", rootMenu) self.rootItems[nwItemClass.PLOT] = QAction(QIcon.fromTheme("folder-new"), "Plot Root", rootMenu) @@ -158,148 +166,146 @@ class GuiMainMenu(QMenu): rootMenu.addActions(self.rootItems.values()) # Item > Separator - itemMenu.addSeparator() + self.itemMenu.addSeparator() # Item > Delete Item menuItem = QAction(QIcon.fromTheme("folder-delete"), "Delete Item", self) menuItem.setStatusTip("Delete Selected Item") menuItem.setShortcut("Del") - itemMenu.addAction(menuItem) + self.itemMenu.addAction(menuItem) - ############################################################################################ + return + + def _buildEditMenu(self): # Edit - editMenu = self.addMenu("&Edit") + self.editMenu = self.addMenu("&Edit") # Edit > Undo menuItem = QAction(QIcon.fromTheme("edit-undo"), "Undo", self) menuItem.setStatusTip("Undo Last Change") menuItem.setShortcut("Ctrl+Z") - menuItem.triggered.connect(lambda: self.docEditor.docAction(nwDocAction.UNDO)) - editMenu.addAction(menuItem) + menuItem.triggered.connect(lambda: self._docAction(nwDocAction.UNDO)) + self.editMenu.addAction(menuItem) # Edit > Redo menuItem = QAction(QIcon.fromTheme("edit-redo"), "Redo", self) menuItem.setStatusTip("Redo Last Change") menuItem.setShortcut("Ctrl+Y") - menuItem.triggered.connect(lambda: self.docEditor.docAction(nwDocAction.REDO)) - editMenu.addAction(menuItem) + menuItem.triggered.connect(lambda: self._docAction(nwDocAction.REDO)) + self.editMenu.addAction(menuItem) # Edit > Separator - editMenu.addSeparator() + self.editMenu.addSeparator() # Edit > Cut menuItem = QAction(QIcon.fromTheme("edit-cut"), "Cut", self) menuItem.setStatusTip("Cut Selected Text") menuItem.setShortcut("Ctrl+X") - menuItem.triggered.connect(lambda: self.docEditor.docAction(nwDocAction.CUT)) - editMenu.addAction(menuItem) + menuItem.triggered.connect(lambda: self._docAction(nwDocAction.CUT)) + self.editMenu.addAction(menuItem) # Edit > Copy menuItem = QAction(QIcon.fromTheme("edit-copy"), "Copy", self) menuItem.setStatusTip("Copy Selected Text") menuItem.setShortcut("Ctrl+C") - menuItem.triggered.connect(lambda: self.docEditor.docAction(nwDocAction.COPY)) - editMenu.addAction(menuItem) + menuItem.triggered.connect(lambda: self._docAction(nwDocAction.COPY)) + self.editMenu.addAction(menuItem) # Edit > Paste menuItem = QAction(QIcon.fromTheme("edit-paste"), "Paste", self) menuItem.setStatusTip("Paste Text from Clipboard") menuItem.setShortcut("Ctrl+V") - menuItem.triggered.connect(lambda: self.docEditor.docAction(nwDocAction.PASTE)) - editMenu.addAction(menuItem) + menuItem.triggered.connect(lambda: self._docAction(nwDocAction.PASTE)) + self.editMenu.addAction(menuItem) # Edit > Separator - editMenu.addSeparator() + self.editMenu.addSeparator() # Edit > Select All menuItem = QAction(QIcon.fromTheme("edit-select-all"), "Select All", self) menuItem.setStatusTip("Select All Text in Document") menuItem.setShortcut("Ctrl+A") - menuItem.triggered.connect(lambda: self.docEditor.docAction(nwDocAction.SEL_ALL)) - editMenu.addAction(menuItem) + menuItem.triggered.connect(lambda: self._docAction(nwDocAction.SEL_ALL)) + self.editMenu.addAction(menuItem) # Edit > Select Paragraph menuItem = QAction(QIcon.fromTheme("edit-select-all"), "Select Paragraph", self) menuItem.setStatusTip("Select All Text in Paragraph") menuItem.setShortcut("Ctrl+Shift+A") - menuItem.triggered.connect(lambda: self.docEditor.docAction(nwDocAction.SEL_PARA)) - editMenu.addAction(menuItem) + menuItem.triggered.connect(lambda: self._docAction(nwDocAction.SEL_PARA)) + self.editMenu.addAction(menuItem) # Edit > Separator - editMenu.addSeparator() + self.editMenu.addSeparator() # Edit > Settings menuItem = QAction(QIcon.fromTheme("applications-system"), "Program Setting", self) menuItem.setStatusTip("Change %s Settings" % nw.__package__) - editMenu.addAction(menuItem) + self.editMenu.addAction(menuItem) - ############################################################################################ + return + + def _buildFormatMenu(self): # Format - fmtMenu = self.addMenu("&Format") + self.fmtMenu = self.addMenu("&Format") # Format > Bold Text menuItem = QAction(QIcon.fromTheme("format-text-bold"), "Bold Text", self) menuItem.setStatusTip("Make Selected Text Bold") menuItem.setShortcut("Ctrl+B") - menuItem.triggered.connect(lambda: self.docEditor.docAction(nwDocAction.BOLD)) - fmtMenu.addAction(menuItem) + menuItem.triggered.connect(lambda: self._docAction(nwDocAction.BOLD)) + self.fmtMenu.addAction(menuItem) # Format > Italic Text menuItem = QAction(QIcon.fromTheme("format-text-italic"), "Italic Text", self) menuItem.setStatusTip("Make Selected Text Italic") menuItem.setShortcut("Ctrl+I") - menuItem.triggered.connect(lambda: self.docEditor.docAction(nwDocAction.ITALIC)) - fmtMenu.addAction(menuItem) + menuItem.triggered.connect(lambda: self._docAction(nwDocAction.ITALIC)) + self.fmtMenu.addAction(menuItem) # Format > Underline Text menuItem = QAction(QIcon.fromTheme("format-text-underline"), "Underline Text", self) menuItem.setStatusTip("Underline Selected Text") menuItem.setShortcut("Ctrl+U") - menuItem.triggered.connect(lambda: self.docEditor.docAction(nwDocAction.U_LINE)) - fmtMenu.addAction(menuItem) + menuItem.triggered.connect(lambda: self._docAction(nwDocAction.U_LINE)) + self.fmtMenu.addAction(menuItem) # Edit > Separator - fmtMenu.addSeparator() + self.fmtMenu.addSeparator() # Format > Double Quotes menuItem = QAction(QIcon.fromTheme("insert-text"), "Wrap Double Quotes", self) menuItem.setStatusTip("Wrap Selected Text in Double Quotes") menuItem.setShortcut("Ctrl+D") - menuItem.triggered.connect(lambda: self.docEditor.docAction(nwDocAction.D_QUOTE)) - fmtMenu.addAction(menuItem) + menuItem.triggered.connect(lambda: self._docAction(nwDocAction.D_QUOTE)) + self.fmtMenu.addAction(menuItem) # Format > Single Quotes menuItem = QAction(QIcon.fromTheme("insert-text"), "Wrap Single Quotes", self) menuItem.setStatusTip("Wrap Selected Text in Single Quotes") menuItem.setShortcut("Ctrl+Shift+D") - menuItem.triggered.connect(lambda: self.docEditor.docAction(nwDocAction.S_QUOTE)) - fmtMenu.addAction(menuItem) + menuItem.triggered.connect(lambda: self._docAction(nwDocAction.S_QUOTE)) + self.fmtMenu.addAction(menuItem) - ############################################################################################ + return + + def _buildHelpMenu(self): # Help - helpMenu = self.addMenu("&Help") + self.helpMenu = self.addMenu("&Help") # Help > About menuItem = QAction(QIcon.fromTheme("help-about"), "About %s" % nw.__package__, self) menuItem.setStatusTip("About %s" % nw.__package__) menuItem.triggered.connect(self._showAbout) - helpMenu.addAction(menuItem) + self.helpMenu.addAction(menuItem) # Help > About Qt5 menuItem = QAction(QIcon.fromTheme("help-about"), "About Qt5", self) menuItem.setStatusTip("About Qt5") - helpMenu.addAction(menuItem) - - if not self.mainConf.debugGUI: - return - - ############################################################################################ - - # Debug GUI - debugMenu = self.addMenu("&Debug") + self.helpMenu.addAction(menuItem) return diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py index 35c4ae35..d1e9be29 100644 --- a/nw/gui/winmain.py +++ b/nw/gui/winmain.py @@ -22,6 +22,7 @@ from nw.gui.doctree import GuiDocTree from nw.gui.doctreectx import GuiDocTreeCtx from nw.gui.doceditor import GuiDocEditor from nw.gui.docdetails import GuiDocDetails +from nw.gui.mainmenu import GuiMainMenu from nw.gui.projecteditor import GuiProjectEditor from nw.gui.statusbar import GuiMainStatus from nw.project.project import NWProject @@ -49,6 +50,8 @@ class GuiMain(QMainWindow): self.docEditor = GuiDocEditor(self) self.docDetails = GuiDocDetails(self.theProject) self.treeView = GuiDocTree(self.theProject) + self.mainMenu = GuiMainMenu(self, self.theProject) + self.statusBar = GuiMainStatus() # Assemble Main Window self.stackPane = QStackedWidget() @@ -70,16 +73,15 @@ class GuiMain(QMainWindow): self.setCentralWidget(self.splitMain) - # Build Menus - self._buildMenu() + # Build GUI Elements self.treeView.setContextMenuPolicy(Qt.CustomContextMenu) self.treeView.customContextMenuRequested.connect(self._openDocTreeContextMenu) self.treeView.itemSelectionChanged.connect(self._treeSingleClick) self.treeView.itemDoubleClicked.connect(self._treeDoubleClick) self.treeView.buildTree() - # Build Status Bar - self.statusBar = GuiMainStatus() + # Set Main Window Elements + self.setMenuBar(self.mainMenu) self.setStatusBar(self.statusBar) self.statusBar.showMessage("Ready") @@ -106,47 +108,24 @@ class GuiMain(QMainWindow): self.treeView.buildTree() return - def openProject(self): - dlgOpt = QFileDialog.Options() - dlgOpt |= QFileDialog.DontUseNativeDialog - projPath, _ = QFileDialog.getOpenFileName( - self,"Open novelWriter Project","","novelWriter Project File (nwProject.nwx);;All Files (*)", options=dlgOpt - ) - if projPath: - self.theProject.openProject(projPath) - self.treeView.buildTree() - self._setWindowTitle(self.theProject.projName) - else: + def openProject(self, projFile=None): + if projFile is None: + projFile = self.openProjectDialog() + if projFile is None: return False + self.theProject.openProject(projFile) + self.treeView.buildTree() + self._setWindowTitle(self.theProject.projName) return True def saveProject(self): if self.theProject.projPath is None: - dlgOpt = QFileDialog.Options() - dlgOpt |= QFileDialog.DontUseNativeDialog - projPath, _ = QFileDialog.getSaveFileName( - self,"Save novelWriter Project","","novelWriter Project File (nwProject.nwx);;All Files (*)", options=dlgOpt - ) - if projPath: - self.theProject.setProjectPath(projPath) - else: - return False + projPath = self.saveProjectDialog() + self.theProject.setProjectPath(projPath) self.treeView.saveTreeOrder() self.theProject.saveProject() return True - def editProject(self): - dlgProj = GuiProjectEditor(self, self.theProject) - dlgProj.exec_() - return True - - def openRecentProject(self, menuItem, recentItem): - logger.verbose("User requested opening recent project #%d" % recentItem) - self.theProject.openProject(self.mainConf.recentList[recentItem]) - self.treeView.buildTree() - self._setWindowTitle(self.theProject.projName) - return True - ## # Document Actions ## @@ -166,27 +145,39 @@ class GuiMain(QMainWindow): return ## - # Internal Functions + # Main Dialogs ## - def _treeSingleClick(self): - sHandle = self.treeView.getSelectedHandle() - if sHandle is not None: - self.docDetails.buildViewBox(sHandle) - return + def openProjectDialog(self): + dlgOpt = QFileDialog.Options() + dlgOpt |= QFileDialog.DontUseNativeDialog + projFile, _ = QFileDialog.getOpenFileName( + self,"Open novelWriter Project","","novelWriter Project File (nwProject.nwx);;All Files (*)", options=dlgOpt + ) + if projFile: + return projFile + return None - def _treeDoubleClick(self, tItem, colNo): - tHandle = tItem.text(3) - logger.verbose("User double clicked tree item with handle %s" % tHandle) - nwItem = self.theProject.getItem(tHandle) - if nwItem.itemType == nwItemType.FILE: - logger.verbose("Requested item %s is a file" % tHandle) - self.openDocument(tHandle) - else: - logger.verbose("Requested item %s is a folder" % tHandle) - return + def saveProjectDialog(self): + dlgOpt = QFileDialog.Options() + dlgOpt |= QFileDialog.DontUseNativeDialog + projPath, _ = QFileDialog.getSaveFileName( + self,"Save novelWriter Project","","novelWriter Project File (nwProject.nwx);;All Files (*)", options=dlgOpt + ) + if projPath: + return projPath + return None - def _closeMain(self): + def editProjectDialog(self): + dlgProj = GuiProjectEditor(self, self.theProject) + dlgProj.exec_() + return True + + ## + # Main Window Actions + ## + + def closeMain(self): logger.info("Exiting %s" % nw.__package__) self.mainConf.setWinSize(self.width(), self.height()) self.mainConf.setTreeColWidths(self.treeView.getColumnSizes()) @@ -194,6 +185,10 @@ class GuiMain(QMainWindow): self.mainConf.saveConfig() return + ## + # Internal Functions + ## + def _setWindowTitle(self, projName=None): winTitle = "%s [%s]" % (nw.__package__, nw.__version__) if projName is not None: @@ -201,19 +196,6 @@ class GuiMain(QMainWindow): self.setWindowTitle(winTitle) return True - ## - # Menu Action - ## - - def _menuExit(self): - self._closeMain() - qApp.quit() - return True - - def _showAbout(self): - self.docTabs.createTab(None,nw.DOCTYPE_ABOUT) - return True - ## # DocTree Context Menu ## @@ -271,7 +253,7 @@ class GuiMain(QMainWindow): return def closeEvent(self, theEvent): - self._closeMain() + self.closeMain() QMainWindow.closeEvent(self,theEvent) return @@ -279,6 +261,23 @@ class GuiMain(QMainWindow): # Signal Handlers ## + def _treeSingleClick(self): + sHandle = self.treeView.getSelectedHandle() + if sHandle is not None: + self.docDetails.buildViewBox(sHandle) + return + + def _treeDoubleClick(self, tItem, colNo): + tHandle = tItem.text(3) + logger.verbose("User double clicked tree item with handle %s" % tHandle) + nwItem = self.theProject.getItem(tHandle) + if nwItem.itemType == nwItemType.FILE: + logger.verbose("Requested item %s is a file" % tHandle) + self.openDocument(tHandle) + else: + logger.verbose("Requested item %s is a folder" % tHandle) + return + def _splitMainMove(self, pWidth, pHeight): """Alert dependent GUI elements that the main pane splitter has been moved. """ diff --git a/nw/project/project.py b/nw/project/project.py index 95449462..2038126e 100644 --- a/nw/project/project.py +++ b/nw/project/project.py @@ -163,7 +163,7 @@ class NWProject(): for xValue in xItem: nwItem.setFromTag(xValue.tag,xValue.text) self._appendItem(tHandle,pHandle,nwItem) - + self._makeStatusIcons() self.mainConf.setRecent(self.projPath) @@ -176,8 +176,12 @@ class NWProject(): return False if not path.isdir(self.projPath): - logger.info("Created folder %s" % self.projPath) - mkdir(self.projPath) + try: + mkdir(self.projPath) + logger.info("Created folder %s" % self.projPath) + except: + logger.error("Could not create folder %s" % self.projPath) + return False logger.debug("Saving project: %s" % self.projPath)