From 02f85bd5d703b6a31ca110361c328af13b608409 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Fri, 31 Jul 2020 14:48:37 +0200 Subject: [PATCH 01/13] Set __package__ to nw and added variable __appname__ --- nw/__init__.py | 15 ++++++++------- nw/config.py | 4 ++-- nw/core/project.py | 6 +++--- nw/gui/about.py | 6 +++--- nw/gui/mainmenu.py | 6 +++--- nw/gui/preferences.py | 10 +++++----- nw/guimain.py | 12 ++++++------ 7 files changed, 30 insertions(+), 29 deletions(-) diff --git a/nw/__init__.py b/nw/__init__.py index a66a888b..b67a6f4f 100644 --- a/nw/__init__.py +++ b/nw/__init__.py @@ -36,7 +36,8 @@ from PyQt5.QtWidgets import QApplication, QErrorMessage from nw.config import Config -__package__ = "novelWriter" +__package__ = "nw" +__appname__ = "novelWriter" __author__ = "Veronica Berglyd Olsen" __copyright__ = "Copyright 2018–2020, Veronica Berglyd Olsen" __license__ = "GPLv3" @@ -132,7 +133,7 @@ def main(sysArgs=None): " --data= Alternative user data path.\n" " --testmode Do not display GUI. Used by the test suite.\n" ).format( - appname = __package__, + appname = __appname__, version = __version__, status = __status__, copyright = __copyright__, @@ -167,7 +168,7 @@ def main(sysArgs=None): print(helpMsg) sys.exit() elif inOpt in ("-v", "--version"): - print("%s %s Version %s [%s]" % (__package__,__status__,__version__,__date__)) + print("%s %s Version %s [%s]" % (__appname__,__status__,__version__,__date__)) sys.exit() elif inOpt == "--info": debugLevel = logging.INFO @@ -218,7 +219,7 @@ def main(sysArgs=None): logger.setLevel(debugLevel) logger.info("Starting %s %s (%s) %s" % ( - __package__, __version__, __hexversion__, __date__ + __appname__, __version__, __hexversion__, __date__ )) # Check Packages and Versions @@ -255,7 +256,7 @@ def main(sysArgs=None): "ERROR: %s cannot start due to the following issues:

" " - %s

Exiting." ) % ( - __package__, "
 - ".join(errorData) + __appname__, "
 - ".join(errorData) )) errApp.exec_() sys.exit(1) @@ -269,8 +270,8 @@ def main(sysArgs=None): nwGUI = GuiMain() return nwGUI else: - nwApp = QApplication([__package__,("-style=%s" % qtStyle)]) - nwApp.setApplicationName(__package__) + nwApp = QApplication([__appname__,("-style=%s" % qtStyle)]) + nwApp.setApplicationName(__appname__) nwApp.setApplicationVersion(__version__) nwApp.setWindowIcon(QIcon(CONFIG.appIcon)) nwApp.setOrganizationDomain("novelwriter.io") diff --git a/nw/config.py b/nw/config.py index cbd79d17..fb60fe11 100644 --- a/nw/config.py +++ b/nw/config.py @@ -52,8 +52,8 @@ class Config: def __init__(self): # Set Application Variables - self.appName = nw.__package__ - self.appHandle = nw.__package__.lower() + self.appName = nw.__appname__ + self.appHandle = nw.__appname__.lower() self.showGUI = True self.debugInfo = False self.cmdOpen = None diff --git a/nw/core/project.py b/nw/core/project.py index 496e8bc4..80ae358c 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -373,7 +373,7 @@ class NWProject(): "Note that after the upgrade, you cannot open the project with an older " "version of %s any more, so make sure you have a recent backup." ) % ( - nw.__package__, nw.__package__ + nw.__appname__, nw.__appname__ )) if msgRes != QMessageBox.Yes: return False @@ -384,7 +384,7 @@ class NWProject(): "The project cannot be opened by this version of {nw:s}. " "The file was saved with {nw:s} version {vers:s}." ).format( - nw = nw.__package__, + nw = nw.__appname__, vers = appVersion, ), nwAlert.ERROR) return False @@ -399,7 +399,7 @@ class NWProject(): "If you continue to open the project, some attributes and settings may not be " "preserved. Continue opening the project?" ) % ( - nw.__package__, appVersion, nw.__version__ + nw.__appname__, appVersion, nw.__version__ )) if msgRes != QMessageBox.Yes: return False diff --git a/nw/gui/about.py b/nw/gui/about.py index d432a4d9..749daeeb 100644 --- a/nw/gui/about.py +++ b/nw/gui/about.py @@ -54,13 +54,13 @@ class GuiAbout(QDialog): self.innerBox = QHBoxLayout() self.innerBox.setSpacing(self.mainConf.pxInt(16)) - self.setWindowTitle("About %s" % nw.__package__) + self.setWindowTitle("About %s" % nw.__appname__) self.setMinimumWidth(self.mainConf.pxInt(650)) self.setMinimumHeight(self.mainConf.pxInt(600)) iPx = self.mainConf.pxInt(96) self.guiDeco = self.theParent.theTheme.loadDecoration("nwicon", (iPx, iPx)) - self.lblName = QLabel("%s" % nw.__package__) + self.lblName = QLabel("%s" % nw.__appname__) self.lblVers = QLabel("v%s" % nw.__version__) self.lblDate = QLabel(datetime.strptime(nw.__date__, "%Y-%m-%d").strftime("%x")) @@ -132,7 +132,7 @@ class GuiAbout(QDialog): "

Credits

" "

{credits:s}

" ).format( - name = nw.__package__, + name = nw.__appname__, copyright = nw.__copyright__, website = nw.__url__, domain = nw.__domain__, diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 84a336eb..7e17b782 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -258,7 +258,7 @@ class GuiMainMenu(QMenuBar): # Project > Exit self.aExitNW = QAction("Exit", self) - self.aExitNW.setStatusTip("Exit %s" % nw.__package__) + self.aExitNW.setStatusTip("Exit %s" % nw.__appname__) self.aExitNW.setShortcut("Ctrl+Q") self.aExitNW.triggered.connect(self._menuExit) self.projMenu.addAction(self.aExitNW) @@ -804,8 +804,8 @@ class GuiMainMenu(QMenuBar): self.helpMenu = self.addMenu("&Help") # Help > About - self.aAboutNW = QAction("About %s" % nw.__package__, self) - self.aAboutNW.setStatusTip("About %s" % nw.__package__) + self.aAboutNW = QAction("About %s" % nw.__appname__, self) + self.aAboutNW.setStatusTip("About %s" % nw.__appname__) self.aAboutNW.triggered.connect(self._showAbout) self.helpMenu.addAction(self.aAboutNW) diff --git a/nw/gui/preferences.py b/nw/gui/preferences.py index 0882b089..187c664f 100644 --- a/nw/gui/preferences.py +++ b/nw/gui/preferences.py @@ -108,7 +108,7 @@ class GuiPreferences(PagedDialog): msgBox = QMessageBox() msgBox.information( self, "Preferences", - "Some changes will not be applied until %s has been restarted." % nw.__package__ + "Some changes will not be applied until %s has been restarted." % nw.__appname__ ) if validEntries: @@ -154,7 +154,7 @@ class GuiConfigEditGeneralTab(QWidget): self.mainForm.addRow( "Main GUI theme", self.selectTheme, - "Changing this requires restarting %s." % nw.__package__ + "Changing this requires restarting %s." % nw.__appname__ ) ## Select Icon Theme @@ -170,7 +170,7 @@ class GuiConfigEditGeneralTab(QWidget): self.mainForm.addRow( "Main icon theme", self.selectIcons, - "Changing this requires restarting %s." % nw.__package__ + "Changing this requires restarting %s." % nw.__appname__ ) ## Dark Icons @@ -193,7 +193,7 @@ class GuiConfigEditGeneralTab(QWidget): self.mainForm.addRow( "Font family", self.guiFont, - "Changing this requires restarting %s." % nw.__package__, + "Changing this requires restarting %s." % nw.__appname__, theButton = self.fontButton ) @@ -206,7 +206,7 @@ class GuiConfigEditGeneralTab(QWidget): self.mainForm.addRow( "Font size", self.guiFontSize, - "Changing this requires restarting %s." % nw.__package__, + "Changing this requires restarting %s." % nw.__appname__, theUnit = "pt" ) diff --git a/nw/guimain.py b/nw/guimain.py index f2a4cb3f..ec8064a1 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -45,8 +45,8 @@ from nw.gui import ( GuiOutline, GuiOutlineDetails, GuiPreferences, GuiProjectLoad, GuiTheme, GuiProjectSettings, GuiProjectTree, GuiWritingStats ) -from nw.core import NWProject, NWDoc, NWIndex -from nw.constants import nwFiles, nwItemType, nwAlert +from .core import NWProject, NWDoc, NWIndex +from .constants import nwFiles, nwItemType, nwAlert logger = logging.getLogger(__name__) @@ -220,7 +220,7 @@ class GuiMain(QMainWindow): else: self.manageProjects() - logger.debug("%s is ready ..." % nw.__package__) + logger.debug("%s is ready ..." % nw.__appname__) return @@ -390,7 +390,7 @@ class GuiMain(QMainWindow): "can safely be overridden. If, however, another instance of %s has " "the project open, overriding the lock may corrupt the project, and " "is not recommended.%s" - ) % (nw.__package__, nw.__package__, lockDetails), + ) % (nw.__appname__, nw.__appname__, lockDetails), QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if msgRes == QMessageBox.Yes: @@ -872,7 +872,7 @@ class GuiMain(QMainWindow): if msgRes != QMessageBox.Yes: return False - logger.info("Exiting %s" % nw.__package__) + logger.info("Exiting %s" % nw.__appname__) if not self.isFocusMode: self.mainConf.setMainPanePos(self.splitMain.sizes()) @@ -1037,7 +1037,7 @@ class GuiMain(QMainWindow): return True def _setWindowTitle(self, projName=None): - winTitle = "%s" % nw.__package__ + winTitle = "%s" % nw.__appname__ if projName is not None: winTitle += " - %s" % projName self.setWindowTitle(winTitle) From fc017cd8da1f35fdaa842ca29c459eb9bf31d13f Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Fri, 31 Jul 2020 16:41:46 +0200 Subject: [PATCH 02/13] Changed how the application name is passed around --- nw/__init__.py | 21 ++++++++++----------- nw/config.py | 4 ++-- nw/core/project.py | 26 ++++++++++++-------------- nw/gui/about.py | 6 +++--- nw/gui/mainmenu.py | 6 +++--- nw/gui/preferences.py | 10 +++++----- nw/guimain.py | 22 +++++++++++----------- 7 files changed, 46 insertions(+), 49 deletions(-) diff --git a/nw/__init__.py b/nw/__init__.py index b67a6f4f..7b546c14 100644 --- a/nw/__init__.py +++ b/nw/__init__.py @@ -37,7 +37,6 @@ from PyQt5.QtWidgets import QApplication, QErrorMessage from nw.config import Config __package__ = "nw" -__appname__ = "novelWriter" __author__ = "Veronica Berglyd Olsen" __copyright__ = "Copyright 2018–2020, Veronica Berglyd Olsen" __license__ = "GPLv3" @@ -46,7 +45,7 @@ __hexversion__ = "0x001002f0" __date__ = "2020-07-29" __maintainer__ = "Veronica Berglyd Olsen" __email__ = "code@vkbo.net" -__status__ = "Pre-Release" +__status__ = "Beta" __url__ = "https://github.com/vkbo/novelWriter" __issuesurl__ = "https://github.com/vkbo/novelWriter/issues" __domain__ = "novelwriter.io" @@ -91,7 +90,6 @@ CONFIG = Config() def main(sysArgs=None): """Parses command line, sets up logging, and launches main GUI. """ - if sysArgs is None: sysArgs = sys.argv[1:] @@ -112,7 +110,7 @@ def main(sysArgs=None): ] helpMsg = ( - "{appname} {version} ({status} {date})\n" + "novelWriter {version} ({status} {date})\n" "{copyright}\n" "\n" "This program is distributed in the hope that it will be useful,\n" @@ -133,7 +131,6 @@ def main(sysArgs=None): " --data= Alternative user data path.\n" " --testmode Do not display GUI. Used by the test suite.\n" ).format( - appname = __appname__, version = __version__, status = __status__, copyright = __copyright__, @@ -168,7 +165,9 @@ def main(sysArgs=None): print(helpMsg) sys.exit() elif inOpt in ("-v", "--version"): - print("%s %s Version %s [%s]" % (__appname__,__status__,__version__,__date__)) + print("%s %s Version %s [%s]" % ( + CONFIG.appName, __status__, __version__, __date__) + ) sys.exit() elif inOpt == "--info": debugLevel = logging.INFO @@ -219,7 +218,7 @@ def main(sysArgs=None): logger.setLevel(debugLevel) logger.info("Starting %s %s (%s) %s" % ( - __appname__, __version__, __hexversion__, __date__ + CONFIG.appName, __version__, __hexversion__, __date__ )) # Check Packages and Versions @@ -256,7 +255,7 @@ def main(sysArgs=None): "ERROR: %s cannot start due to the following issues:

" " - %s

Exiting." ) % ( - __appname__, "
 - ".join(errorData) + CONFIG.appName, "
 - ".join(errorData) )) errApp.exec_() sys.exit(1) @@ -270,11 +269,11 @@ def main(sysArgs=None): nwGUI = GuiMain() return nwGUI else: - nwApp = QApplication([__appname__,("-style=%s" % qtStyle)]) - nwApp.setApplicationName(__appname__) + nwApp = QApplication([CONFIG.appName, ("-style=%s" % qtStyle)]) + nwApp.setApplicationName(CONFIG.appName) nwApp.setApplicationVersion(__version__) nwApp.setWindowIcon(QIcon(CONFIG.appIcon)) - nwApp.setOrganizationDomain("novelwriter.io") + nwApp.setOrganizationDomain(__domain__) nwGUI = GuiMain() sys.exit(nwApp.exec_()) diff --git a/nw/config.py b/nw/config.py index fb60fe11..ec397aea 100644 --- a/nw/config.py +++ b/nw/config.py @@ -52,8 +52,8 @@ class Config: def __init__(self): # Set Application Variables - self.appName = nw.__appname__ - self.appHandle = nw.__appname__.lower() + self.appName = "novelWriter" + self.appHandle = self.appName.lower() self.showGUI = True self.debugInfo = False self.cmdOpen = None diff --git a/nw/core/project.py b/nw/core/project.py index 80ae358c..683affbe 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -368,23 +368,21 @@ class NWProject(): if fileVersion == "1.0": msgBox = QMessageBox() msgRes = msgBox.question(self.theParent, "Old Project Version", ( - "The project file and data is created by a %s version lower than 0.7. " - "Do you want to upgrade the project to the most recent format?

" - "Note that after the upgrade, you cannot open the project with an older " - "version of %s any more, so make sure you have a recent backup." - ) % ( - nw.__appname__, nw.__appname__ + "The project file and data is created by a novelWriter version " + "lower than 0.7. Do you want to upgrade the project to the " + "most recent format?

Note that after the upgrade, you " + "cannot open the project with an older version of novelWriter " + "any more, so make sure you have a recent backup." )) if msgRes != QMessageBox.Yes: return False elif fileVersion != "1.1" and fileVersion != "1.2": self.makeAlert(( - "Unknown or unsupported {nw:s} project file format. " - "The project cannot be opened by this version of {nw:s}. " - "The file was saved with {nw:s} version {vers:s}." + "Unknown or unsupported novelWriter project file format. " + "The project cannot be opened by this version of novelWriter. " + "The file was saved with novelWriter version {vers:s}." ).format( - nw = nw.__appname__, vers = appVersion, ), nwAlert.ERROR) return False @@ -395,11 +393,11 @@ class NWProject(): if int(hexVersion, 16) > int(nw.__hexversion__, 16) and self.mainConf.showGUI: msgBox = QMessageBox() msgRes = msgBox.question(self.theParent, "Version Conflict", ( - "This project was saved by a newer version of %s, version %s. This is version %s. " - "If you continue to open the project, some attributes and settings may not be " - "preserved. Continue opening the project?" + "This project was saved by a newer version of novelWriter, version %s. " + "This is version %s. If you continue to open the project, some attributes " + "and settings may not be preserved. Continue opening the project?" ) % ( - nw.__appname__, appVersion, nw.__version__ + appVersion, nw.__version__ )) if msgRes != QMessageBox.Yes: return False diff --git a/nw/gui/about.py b/nw/gui/about.py index 749daeeb..689a0aca 100644 --- a/nw/gui/about.py +++ b/nw/gui/about.py @@ -54,13 +54,13 @@ class GuiAbout(QDialog): self.innerBox = QHBoxLayout() self.innerBox.setSpacing(self.mainConf.pxInt(16)) - self.setWindowTitle("About %s" % nw.__appname__) + self.setWindowTitle("About %s" % self.mainConf.appName) self.setMinimumWidth(self.mainConf.pxInt(650)) self.setMinimumHeight(self.mainConf.pxInt(600)) iPx = self.mainConf.pxInt(96) self.guiDeco = self.theParent.theTheme.loadDecoration("nwicon", (iPx, iPx)) - self.lblName = QLabel("%s" % nw.__appname__) + self.lblName = QLabel("%s" % self.mainConf.appName) self.lblVers = QLabel("v%s" % nw.__version__) self.lblDate = QLabel(datetime.strptime(nw.__date__, "%Y-%m-%d").strftime("%x")) @@ -132,7 +132,7 @@ class GuiAbout(QDialog): "

Credits

" "

{credits:s}

" ).format( - name = nw.__appname__, + name = self.mainConf.appName, copyright = nw.__copyright__, website = nw.__url__, domain = nw.__domain__, diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py index 7e17b782..43b95385 100644 --- a/nw/gui/mainmenu.py +++ b/nw/gui/mainmenu.py @@ -258,7 +258,7 @@ class GuiMainMenu(QMenuBar): # Project > Exit self.aExitNW = QAction("Exit", self) - self.aExitNW.setStatusTip("Exit %s" % nw.__appname__) + self.aExitNW.setStatusTip("Exit %s" % self.mainConf.appName) self.aExitNW.setShortcut("Ctrl+Q") self.aExitNW.triggered.connect(self._menuExit) self.projMenu.addAction(self.aExitNW) @@ -804,8 +804,8 @@ class GuiMainMenu(QMenuBar): self.helpMenu = self.addMenu("&Help") # Help > About - self.aAboutNW = QAction("About %s" % nw.__appname__, self) - self.aAboutNW.setStatusTip("About %s" % nw.__appname__) + self.aAboutNW = QAction("About %s" % self.mainConf.appName, self) + self.aAboutNW.setStatusTip("About %s" % self.mainConf.appName) self.aAboutNW.triggered.connect(self._showAbout) self.helpMenu.addAction(self.aAboutNW) diff --git a/nw/gui/preferences.py b/nw/gui/preferences.py index 187c664f..6bb39033 100644 --- a/nw/gui/preferences.py +++ b/nw/gui/preferences.py @@ -108,7 +108,7 @@ class GuiPreferences(PagedDialog): msgBox = QMessageBox() msgBox.information( self, "Preferences", - "Some changes will not be applied until %s has been restarted." % nw.__appname__ + "Some changes will not be applied until novelWriter has been restarted." ) if validEntries: @@ -154,7 +154,7 @@ class GuiConfigEditGeneralTab(QWidget): self.mainForm.addRow( "Main GUI theme", self.selectTheme, - "Changing this requires restarting %s." % nw.__appname__ + "Changing this requires restarting novelWriter." ) ## Select Icon Theme @@ -170,7 +170,7 @@ class GuiConfigEditGeneralTab(QWidget): self.mainForm.addRow( "Main icon theme", self.selectIcons, - "Changing this requires restarting %s." % nw.__appname__ + "Changing this requires restarting novelWriter." ) ## Dark Icons @@ -193,7 +193,7 @@ class GuiConfigEditGeneralTab(QWidget): self.mainForm.addRow( "Font family", self.guiFont, - "Changing this requires restarting %s." % nw.__appname__, + "Changing this requires restarting novelWriter.", theButton = self.fontButton ) @@ -206,7 +206,7 @@ class GuiConfigEditGeneralTab(QWidget): self.mainForm.addRow( "Font size", self.guiFontSize, - "Changing this requires restarting %s." % nw.__appname__, + "Changing this requires restarting novelWriter.", theUnit = "pt" ) diff --git a/nw/guimain.py b/nw/guimain.py index ec8064a1..6ea48c9b 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -45,8 +45,8 @@ from nw.gui import ( GuiOutline, GuiOutlineDetails, GuiPreferences, GuiProjectLoad, GuiTheme, GuiProjectSettings, GuiProjectTree, GuiWritingStats ) -from .core import NWProject, NWDoc, NWIndex -from .constants import nwFiles, nwItemType, nwAlert +from nw.core import NWProject, NWDoc, NWIndex +from nw.constants import nwFiles, nwItemType, nwAlert logger = logging.getLogger(__name__) @@ -220,7 +220,7 @@ class GuiMain(QMainWindow): else: self.manageProjects() - logger.debug("%s is ready ..." % nw.__appname__) + logger.debug("novelWriter is ready ...") return @@ -384,13 +384,13 @@ class GuiMain(QMainWindow): msgBox = QMessageBox() msgRes = msgBox.warning( self, "Project Locked", ( - "The project is already open by another instance of %s, and is " - "therefore locked. Override lock and continue anyway?

" + "The project is already open by another instance of novelWriter, and " + "is therefore locked. Override lock and continue anyway?

" "Note: If the program or the computer previously crashed, the lock " - "can safely be overridden. If, however, another instance of %s has " - "the project open, overriding the lock may corrupt the project, and " - "is not recommended.%s" - ) % (nw.__appname__, nw.__appname__, lockDetails), + "can safely be overridden. If, however, another instance of " + "novelWriter has the project open, overriding the lock may corrupt " + "the project, and is not recommended.%s" + ) % lockDetails, QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if msgRes == QMessageBox.Yes: @@ -872,7 +872,7 @@ class GuiMain(QMainWindow): if msgRes != QMessageBox.Yes: return False - logger.info("Exiting %s" % nw.__appname__) + logger.info("Exiting novelWriter") if not self.isFocusMode: self.mainConf.setMainPanePos(self.splitMain.sizes()) @@ -1037,7 +1037,7 @@ class GuiMain(QMainWindow): return True def _setWindowTitle(self, projName=None): - winTitle = "%s" % nw.__appname__ + winTitle = self.mainConf.appName if projName is not None: winTitle += " - %s" % projName self.setWindowTitle(winTitle) From 9233cdd0146c8ed131b4d25ec57bef19395dfa3a Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Fri, 31 Jul 2020 17:49:10 +0200 Subject: [PATCH 03/13] Added exception handling for the main app itself --- nw/__init__.py | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/nw/__init__.py b/nw/__init__.py index 7b546c14..c3d08fcd 100644 --- a/nw/__init__.py +++ b/nw/__init__.py @@ -252,10 +252,10 @@ def main(sysArgs=None): errMsg.setMinimumWidth(500) errMsg.setMinimumHeight(300) errMsg.showMessage(( - "ERROR: %s cannot start due to the following issues:

" + "ERROR: novelWriter cannot start due to the following issues:

" " - %s

Exiting." ) % ( - CONFIG.appName, "
 - ".join(errorData) + "
 - ".join(errorData) )) errApp.exec_() sys.exit(1) @@ -268,13 +268,43 @@ def main(sysArgs=None): if testMode: nwGUI = GuiMain() return nwGUI + else: nwApp = QApplication([CONFIG.appName, ("-style=%s" % qtStyle)]) nwApp.setApplicationName(CONFIG.appName) nwApp.setApplicationVersion(__version__) nwApp.setWindowIcon(QIcon(CONFIG.appIcon)) nwApp.setOrganizationDomain(__domain__) - nwGUI = GuiMain() - sys.exit(nwApp.exec_()) + + try: + nwGUI = GuiMain() + sys.exit(nwApp.exec_()) + + except: + # novelWriter has crashed! + from traceback import print_tb, format_tb + + eInfo = sys.exc_info() + logger.critical("%s: %s" % (eInfo[0].__name__, eInfo[1])) + print_tb(eInfo[2]) + + del nwApp + + errApp = QApplication([]) + errMsg = QErrorMessage() + errMsg.setWindowTitle("Critical Error") + errMsg.setMinimumWidth(500) + errMsg.setMinimumHeight(300) + errMsg.showMessage(( + "

novelWriter has encountered a critical error!

" + "

%s:
%s

" + "

Traceback:
%s

" + "

Shutting down ...

" + ) % (eInfo[0].__name__, eInfo[1], "
".join(format_tb(eInfo[2])))) + errApp.exec_() + + del eInfo + + sys.exit(1) return From 350d5beed7c4bf20c6a1816e4058677fe0d6d639 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Fri, 31 Jul 2020 21:46:23 +0200 Subject: [PATCH 04/13] Some minor last tweaks to the exception handler --- nw/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nw/__init__.py b/nw/__init__.py index c3d08fcd..2271157a 100644 --- a/nw/__init__.py +++ b/nw/__init__.py @@ -165,8 +165,8 @@ def main(sysArgs=None): print(helpMsg) sys.exit() elif inOpt in ("-v", "--version"): - print("%s %s Version %s [%s]" % ( - CONFIG.appName, __status__, __version__, __date__) + print("novelWriter %s Version %s [%s]" % ( + __status__, __version__, __date__) ) sys.exit() elif inOpt == "--info": @@ -197,7 +197,7 @@ def main(sysArgs=None): CONFIG.cmdOpen = cmdOpen # Set Logging - logFmt = logging.Formatter(fmt=logFormat, datefmt="%Y-%m-%d %H:%M:%S", style="{") + logFmt = logging.Formatter(fmt=logFormat, style="{") if not logFile == "" and toFile: if path.isfile(logFile+".bak"): @@ -217,8 +217,8 @@ def main(sysArgs=None): logger.addHandler(cHandle) logger.setLevel(debugLevel) - logger.info("Starting %s %s (%s) %s" % ( - CONFIG.appName, __version__, __hexversion__, __date__ + logger.info("Starting novelWriter %s (%s) %s" % ( + __version__, __hexversion__, __date__ )) # Check Packages and Versions @@ -280,7 +280,7 @@ def main(sysArgs=None): nwGUI = GuiMain() sys.exit(nwApp.exec_()) - except: + except Exception: # novelWriter has crashed! from traceback import print_tb, format_tb From e5cdb2e8caa99175728f0b4858155dac46878a74 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 2 Aug 2020 14:34:09 +0200 Subject: [PATCH 05/13] Added error.py file for error handling functions --- nw/error.py | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 nw/error.py diff --git a/nw/error.py b/nw/error.py new file mode 100644 index 00000000..dd0cb109 --- /dev/null +++ b/nw/error.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +"""novelWriter Init + + novelWriter – Exception Handling +================================== + Error handling functions + + File History: + Created: 2020-08-02 [0.10.2] + + This file is a part of novelWriter + Copyright 2020, Veronica Berglyd Olsen + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + +def formatHtmlErrMsg(exType, exValue, exTrace): + """Generates a HTML version of an exception. + """ + try: + from traceback import format_tb + from nw import __issuesurl__ + + fmtTrace = "" + for trEntry in format_tb(exTrace): + for trLine in trEntry.split("\n"): + stripLine = trLine.lstrip(" ") + nIndent = len(trLine) - len(stripLine) + fmtTrace += " "*nIndent + stripLine + "
" + + theMessage = ( + "

Please report this error by submitting an issue report on " + "GitHub, providing a description and this error message.

" + "

Issue Tracker
%s

" + "

Error Type
%s: %s

" + "

Traceback
%s

" + ) % (__issuesurl__, exType.__name__, str(exValue), fmtTrace) + + return theMessage + + except Exception as e: + return "Could not generate error message.
%s" % str(e) + + return "Could not generate error message." + + +def exceptionHandler(exType, exValue, exTrace): + """Function to catch unhandled global exceptions. + """ + import logging + from traceback import print_tb, format_tb + from nw import __issuesurl__ + from PyQt5.QtWidgets import qApp, QApplication, QErrorMessage, QMessageBox + + logger = logging.getLogger(__name__) + logger.error("%s: %s" % (exType.__name__, str(exValue))) + print_tb(exTrace) + + try: + nwGUI = None + for qWin in qApp.topLevelWidgets(): + if qWin.objectName() == "GuiMain": + nwGUI = qWin + break + + if nwGUI is None: + logger.warning("Could not find main GUI window so cannot open error dialog") + return + + errMsg = QErrorMessage(nwGUI) + errMsg.setWindowTitle("Unhandled Error") + errMsg.resize(800, 400) + errMsg.showMessage(( + "

An unhandled error has been encountered

%s" + ) % formatHtmlErrMsg(exType, exValue, exTrace)) + + except Exception as e: + logger.error(str(e)) From 95a5ee7b8aadb3c0833854e248690536a2f21e6f Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 2 Aug 2020 14:34:44 +0200 Subject: [PATCH 06/13] Set up global error handler and modified error handling for initial GUI build --- nw/__init__.py | 58 ++++++++++++++++++++++++++++++-------------------- nw/guimain.py | 1 + 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/nw/__init__.py b/nw/__init__.py index 2271157a..5ffae592 100644 --- a/nw/__init__.py +++ b/nw/__init__.py @@ -34,6 +34,7 @@ from os import path, remove, rename from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QApplication, QErrorMessage +from nw.error import exceptionHandler from nw.config import Config __package__ = "nw" @@ -249,11 +250,12 @@ def main(sysArgs=None): if errorData: errApp = QApplication([]) errMsg = QErrorMessage() - errMsg.setMinimumWidth(500) - errMsg.setMinimumHeight(300) + errMsg.resize(500, 300) errMsg.showMessage(( - "ERROR: novelWriter cannot start due to the following issues:

" - " - %s

Exiting." + "

A critical error has been encountered

" + "

novelWriter cannot start due to the following issues:

" + "

 - %s

" + "

Shutting down ...

" ) % ( "
 - ".join(errorData) )) @@ -276,34 +278,44 @@ def main(sysArgs=None): nwApp.setWindowIcon(QIcon(CONFIG.appIcon)) nwApp.setOrganizationDomain(__domain__) + # We try to catch critical errors while setting up the main GUI + # by wrapping the main GUI in a try/except structure. This will + # not catch all exceptions for other parts of the application. + # For all other unhandled exceptions, we use a custom exception + # handler that pops a dialog box with the error message. + sys.excepthook = exceptionHandler + try: nwGUI = GuiMain() sys.exit(nwApp.exec_()) except Exception: - # novelWriter has crashed! - from traceback import print_tb, format_tb - eInfo = sys.exc_info() - logger.critical("%s: %s" % (eInfo[0].__name__, eInfo[1])) - print_tb(eInfo[2]) + from traceback import print_tb + from nw.error import formatHtmlErrMsg - del nwApp + exType, exValue, exTrace = sys.exc_info() - errApp = QApplication([]) - errMsg = QErrorMessage() - errMsg.setWindowTitle("Critical Error") - errMsg.setMinimumWidth(500) - errMsg.setMinimumHeight(300) - errMsg.showMessage(( - "

novelWriter has encountered a critical error!

" - "

%s:
%s

" - "

Traceback:
%s

" - "

Shutting down ...

" - ) % (eInfo[0].__name__, eInfo[1], "
".join(format_tb(eInfo[2])))) - errApp.exec_() + logger.critical("%s: %s" % (exType.__name__, str(exValue))) + print_tb(exTrace) - del eInfo + try: + del nwApp + + errApp = QApplication([]) + errMsg = QErrorMessage() + errMsg.setWindowTitle("Critical Error") + errMsg.resize(800, 400) + errMsg.showMessage(( + "

A critical error has been encountered

" + "%s" + "

Shutting down ...

" + ) % formatHtmlErrMsg(exType, exValue, exTrace)) + errApp.exec_() + + except Exception as e: + logger.critical("Could not create error message dialog.") + logger.critical(str(e)) sys.exit(1) diff --git a/nw/guimain.py b/nw/guimain.py index 6ea48c9b..12655f20 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -56,6 +56,7 @@ class GuiMain(QMainWindow): QMainWindow.__init__(self) logger.debug("Initialising GUI ...") + self.setObjectName("GuiMain") self.mainConf = nw.CONFIG # Some runtime info useful for debugging From 46c9f0fb5f5c435c59ca63d87534a9b87ce20f3f Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 2 Aug 2020 15:03:17 +0200 Subject: [PATCH 07/13] Added more infor to the error dialog --- nw/error.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/nw/error.py b/nw/error.py index dd0cb109..cb276752 100644 --- a/nw/error.py +++ b/nw/error.py @@ -29,8 +29,11 @@ def formatHtmlErrMsg(exType, exValue, exTrace): """Generates a HTML version of an exception. """ try: + import sys from traceback import format_tb - from nw import __issuesurl__ + from nw import __issuesurl__, __version__ + from PyQt5.Qt import PYQT_VERSION_STR + from PyQt5.QtCore import QT_VERSION_STR, QSysInfo fmtTrace = "" for trEntry in format_tb(exTrace): @@ -41,11 +44,25 @@ def formatHtmlErrMsg(exType, exValue, exTrace): theMessage = ( "

Please report this error by submitting an issue report on " - "GitHub, providing a description and this error message.

" - "

Issue Tracker
%s

" - "

Error Type
%s: %s

" - "

Traceback
%s

" - ) % (__issuesurl__, exType.__name__, str(exValue), fmtTrace) + "GitHub, providing a description and this error message. " + "URL: <{issueUrl}>.

" + "

Environment
Version: {nwVersion}, OS: {osType} ({osKernel})," + "Python: {pyVersion} ({pyHexVer:#x}), Qt: {qtVers}, PyQt: {pyqtVers}

" + "

Error Type
{exType}: {exMessage}

" + "

Traceback
{exTrace}

" + ).format( + nwVersion = __version__, + osType = sys.platform, + osKernel = QSysInfo.kernelVersion(), + pyVersion = sys.version.split()[0], + pyHexVer = sys.hexversion, + qtVers = QT_VERSION_STR, + pyqtVers = PYQT_VERSION_STR, + issueUrl = __issuesurl__, + exType = exType.__name__, + exMessage = str(exValue), + exTrace = fmtTrace + ) return theMessage From d3ab1caf3a3f5a6206b50e944e478cededa91bb7 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 2 Aug 2020 15:14:46 +0200 Subject: [PATCH 08/13] Block the error handler dialog in test mode --- nw/error.py | 7 ++++++- nw/gui/build.py | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/nw/error.py b/nw/error.py index cb276752..0301f3ba 100644 --- a/nw/error.py +++ b/nw/error.py @@ -77,13 +77,16 @@ def exceptionHandler(exType, exValue, exTrace): """ import logging from traceback import print_tb, format_tb - from nw import __issuesurl__ + from nw import CONFIG from PyQt5.QtWidgets import qApp, QApplication, QErrorMessage, QMessageBox logger = logging.getLogger(__name__) logger.error("%s: %s" % (exType.__name__, str(exValue))) print_tb(exTrace) + if not CONFIG.showGUI: + return + try: nwGUI = None for qWin in qApp.topLevelWidgets(): @@ -104,3 +107,5 @@ def exceptionHandler(exType, exValue, exTrace): except Exception as e: logger.error(str(e)) + + return diff --git a/nw/gui/build.py b/nw/gui/build.py index 34a01f05..93aa8271 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -432,7 +432,6 @@ class GuiBuildNovel(QDialog): def _buildPreview(self): """Build a preview of the project in the document viewer. """ - # Get Settings fmtTitle = self.fmtTitle.text().strip() fmtChapter = self.fmtChapter.text().strip() From 61190284be22e69d83fbd16eccaca7b46d596136 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 2 Aug 2020 21:22:50 +0200 Subject: [PATCH 09/13] About box shoud now point to the new website URL --- nw/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nw/__init__.py b/nw/__init__.py index 5ffae592..34d506ef 100644 --- a/nw/__init__.py +++ b/nw/__init__.py @@ -47,7 +47,7 @@ __date__ = "2020-07-29" __maintainer__ = "Veronica Berglyd Olsen" __email__ = "code@vkbo.net" __status__ = "Beta" -__url__ = "https://github.com/vkbo/novelWriter" +__url__ = "https://novelwriter.io" __issuesurl__ = "https://github.com/vkbo/novelWriter/issues" __domain__ = "novelwriter.io" __docurl__ = "https://novelwriter.readthedocs.io" From 148c3530e257d418035e0ff9d0e3d7dbd3680b15 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 2 Aug 2020 21:23:16 +0200 Subject: [PATCH 10/13] Split up author and credit entries in icon theme config files --- nw/assets/icons/typicons_colour_dark/icons.conf | 4 ++-- nw/assets/icons/typicons_colour_light/icons.conf | 4 ++-- nw/assets/icons/typicons_grey_dark/icons.conf | 4 ++-- nw/assets/icons/typicons_grey_light/icons.conf | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/nw/assets/icons/typicons_colour_dark/icons.conf b/nw/assets/icons/typicons_colour_dark/icons.conf index 33aeef93..c4167b90 100644 --- a/nw/assets/icons/typicons_colour_dark/icons.conf +++ b/nw/assets/icons/typicons_colour_dark/icons.conf @@ -9,8 +9,8 @@ [Main] name = Typicons Colour Dark description = Coulorised icons for dark GUI theme based on Typicons. -author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation) -credit = Stephen Hutchings +author = Veronica Berglyd Olsen (adaptation) +credit = Stephen Hutchings (icon design) url = https://github.com/stephenhutchings/typicons.font license = CC BY-SA 4.0 licenseurl = https://creativecommons.org/licenses/by-sa/4.0/ diff --git a/nw/assets/icons/typicons_colour_light/icons.conf b/nw/assets/icons/typicons_colour_light/icons.conf index 0ba6dd6b..be0b1853 100644 --- a/nw/assets/icons/typicons_colour_light/icons.conf +++ b/nw/assets/icons/typicons_colour_light/icons.conf @@ -9,8 +9,8 @@ [Main] name = Typicons Colour Light description = Coulorised icons for light GUI theme based on Typicons. -author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation) -credit = Stephen Hutchings +author = Veronica Berglyd Olsen (adaptation) +credit = Stephen Hutchings (icon design) url = https://github.com/stephenhutchings/typicons.font license = CC BY-SA 4.0 licenseurl = https://creativecommons.org/licenses/by-sa/4.0/ diff --git a/nw/assets/icons/typicons_grey_dark/icons.conf b/nw/assets/icons/typicons_grey_dark/icons.conf index 301c74ac..fba00697 100644 --- a/nw/assets/icons/typicons_grey_dark/icons.conf +++ b/nw/assets/icons/typicons_grey_dark/icons.conf @@ -9,8 +9,8 @@ [Main] name = Typicons Grey Dark description = Greyscaled icons for dark GUI theme based on Typicons. -author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation) -credit = Stephen Hutchings +author = Veronica Berglyd Olsen (adaptation) +credit = Stephen Hutchings (icon design) url = https://github.com/stephenhutchings/typicons.font license = CC BY-SA 4.0 licenseurl = https://creativecommons.org/licenses/by-sa/4.0/ diff --git a/nw/assets/icons/typicons_grey_light/icons.conf b/nw/assets/icons/typicons_grey_light/icons.conf index 6e98346a..62aba87f 100644 --- a/nw/assets/icons/typicons_grey_light/icons.conf +++ b/nw/assets/icons/typicons_grey_light/icons.conf @@ -9,8 +9,8 @@ [Main] name = Typicons Grey Light description = Greyscaled icons for light GUI theme based on Typicons. -author = Stephen Hutchings (original), Veronica Berglyd Olsen (adaptation) -credit = Stephen Hutchings +author = Veronica Berglyd Olsen (adaptation) +credit = Stephen Hutchings (icon design) url = https://github.com/stephenhutchings/typicons.font license = CC BY-SA 4.0 licenseurl = https://creativecommons.org/licenses/by-sa/4.0/ From b6e06a81d544f1fa8c6444af175903091a42b43a Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 2 Aug 2020 21:25:45 +0200 Subject: [PATCH 11/13] Setup file should get version from nw/__init__.py --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 091e3652..68134ad9 100755 --- a/setup.py +++ b/setup.py @@ -1,12 +1,14 @@ #!/usr/bin/env python3 import setuptools +from nw import __version__ + with open("README.md", "r") as inFile: long_description = inFile.read() setuptools.setup( name = "novelWriter", - version = "0.10.2", + version = __version__, author = "Veronica Berglyd Olsen", author_email = "code@vkbo.net", description = "A markdown-like document editor for writing novels", From 4d0777b02ec791977a7dcd84f0029cf02926419b Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 2 Aug 2020 21:35:45 +0200 Subject: [PATCH 12/13] Remove duplicate settings from setup, and add more classifiers --- setup.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/setup.py b/setup.py index 68134ad9..6638e0bc 100755 --- a/setup.py +++ b/setup.py @@ -1,10 +1,13 @@ #!/usr/bin/env python3 import setuptools -from nw import __version__ +from nw import __version__, __url__, __docurl__, __issuesurl__, __sourceurl__ with open("README.md", "r") as inFile: - long_description = inFile.read() + longDescription = inFile.read() + +with open("requirements.txt", "r") as inFile: + pkgRequirements = inFile.read().strip().splitlines() setuptools.setup( name = "novelWriter", @@ -12,10 +15,10 @@ setuptools.setup( author = "Veronica Berglyd Olsen", author_email = "code@vkbo.net", description = "A markdown-like document editor for writing novels", - long_description = long_description, + long_description = longDescription, long_description_content_type = "text/markdown", license = "GNU General Public License v3", - url = "https://github.com/vkbo/novelWriter", + url = __url__, entry_points = { "console_scripts" : ["novelWriter-cli=nw:main"], "gui_scripts" : ["novelWriter=nw:main"], @@ -24,26 +27,24 @@ setuptools.setup( include_package_data = True, package_data = {"": ["*.conf"]}, project_urls = { - "Bug Tracker": "https://github.com/vkbo/novelWriter/issues", - "Documentation": "https://novelwriter.readthedocs.io/", - "Source Code": "https://github.com/vkbo/novelWriter", + "Bug Tracker": __issuesurl__, + "Documentation": __docurl__, + "Source Code": __sourceurl__, }, classifiers = [ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: Implementation :: CPython", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - "Development Status :: 3 - Alpha", + "Development Status :: 4 - Beta", "Operating System :: OS Independent", "Intended Audience :: End Users/Desktop", "Natural Language :: English", "Topic :: Text Editors", ], python_requires = ">=3.6", - install_requires = [ - "pyqt5>=5.2.1", - "lxml>=4.2.0", - "pyenchant>=3.0.0", - ], + install_requires = pkgRequirements, ) From 56528d0f988cb2d0c41e1f33337aa6f42ee13240 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 2 Aug 2020 21:36:09 +0200 Subject: [PATCH 13/13] Add source url variable to nw init --- nw/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nw/__init__.py b/nw/__init__.py index 34d506ef..3373a34f 100644 --- a/nw/__init__.py +++ b/nw/__init__.py @@ -48,6 +48,7 @@ __maintainer__ = "Veronica Berglyd Olsen" __email__ = "code@vkbo.net" __status__ = "Beta" __url__ = "https://novelwriter.io" +__sourceurl__ = "https://github.com/vkbo/novelWriter" __issuesurl__ = "https://github.com/vkbo/novelWriter/issues" __domain__ = "novelwriter.io" __docurl__ = "https://novelwriter.readthedocs.io"