From 2f9645491cd34940208222905e6df6f52a9af6bf Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 15:51:59 +0200
Subject: [PATCH 01/22] The histogram bar of the writing stats dialog can now
be capped
---
nw/gui/writingstats.py | 26 ++++++++++++++++++++------
1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/nw/gui/writingstats.py b/nw/gui/writingstats.py
index 40df85c9..8268a714 100644
--- a/nw/gui/writingstats.py
+++ b/nw/gui/writingstats.py
@@ -36,7 +36,7 @@ from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont, QPixmap
from PyQt5.QtWidgets import (
qApp, QDialog, QTreeWidget, QTreeWidgetItem, QDialogButtonBox, QGridLayout,
- QLabel, QGroupBox, QMenu, QAction, QFileDialog
+ QLabel, QGroupBox, QMenu, QAction, QFileDialog, QSpinBox, QHBoxLayout
)
from nw.constants import nwConst, nwFiles, nwAlert
@@ -207,6 +207,18 @@ class GuiWritingStats(QDialog):
self.filterForm.addWidget(self.groupByDay, 4, 1)
self.filterForm.setRowStretch(5, 1)
+ # Settings
+ self.histMax = QSpinBox(self)
+ self.histMax.setMinimum(100)
+ self.histMax.setMaximum(100000)
+ self.histMax.setSingleStep(100)
+ self.histMax.valueChanged.connect(self._updateListBox)
+
+ self.optsBox = QHBoxLayout()
+ self.optsBox.addStretch(1)
+ self.optsBox.addWidget(QLabel("Word count cap for histogram"), 0)
+ self.optsBox.addWidget(self.histMax, 0)
+
# Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
self.buttonBox.rejected.connect(self._doClose)
@@ -226,9 +238,10 @@ class GuiWritingStats(QDialog):
# Assemble
self.outerBox = QGridLayout()
self.outerBox.addWidget(self.listBox, 0, 0, 1, 2)
- self.outerBox.addWidget(self.infoBox, 1, 0)
- self.outerBox.addWidget(self.filterBox, 1, 1)
- self.outerBox.addWidget(self.buttonBox, 2, 0, 1, 2)
+ self.outerBox.addLayout(self.optsBox, 1, 0, 1, 2)
+ self.outerBox.addWidget(self.infoBox, 2, 0)
+ self.outerBox.addWidget(self.filterBox, 2, 1)
+ self.outerBox.addWidget(self.buttonBox, 3, 0, 1, 2)
self.outerBox.setRowStretch(0, 1)
self.setLayout(self.outerBox)
@@ -449,6 +462,7 @@ class GuiWritingStats(QDialog):
hideZeros = self.hideZeros.isChecked()
hideNegative = self.hideNegative.isChecked()
groupByDay = self.groupByDay.isChecked()
+ histMax = self.histMax.value()
# Group the data
if groupByDay:
@@ -509,7 +523,7 @@ class GuiWritingStats(QDialog):
sStart = dStart.strftime(nwConst.tStampFmt)
self.filterData.append((dStart, sStart, sDiff, dwTotal, wcNovel, wcNotes))
- listMax = max(listMax, dwTotal)
+ listMax = min(max(listMax, dwTotal), histMax)
pcTotal = wcTotal
# Populate the list
@@ -522,7 +536,7 @@ class GuiWritingStats(QDialog):
if nWords > 0 and listMax > 0:
theBar = self.barImage.scaled(
- int(200*nWords/listMax),
+ int(200*min(nWords, histMax)/listMax),
self.barHeight,
Qt.IgnoreAspectRatio,
Qt.FastTransformation
From cd0f67d9008c87048844446e94a45bbf8cbfb9b5 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 15:57:45 +0200
Subject: [PATCH 02/22] Prevent the enter key from closing the stats dialog
---
nw/gui/writingstats.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/nw/gui/writingstats.py b/nw/gui/writingstats.py
index 8268a714..76c45a58 100644
--- a/nw/gui/writingstats.py
+++ b/nw/gui/writingstats.py
@@ -220,10 +220,15 @@ class GuiWritingStats(QDialog):
self.optsBox.addWidget(self.histMax, 0)
# Buttons
- self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
+ self.buttonBox = QDialogButtonBox()
self.buttonBox.rejected.connect(self._doClose)
+ self.btnClose = self.buttonBox.addButton(QDialogButtonBox.Close)
+ self.btnClose.setAutoDefault(False)
+
self.btnSave = self.buttonBox.addButton("Save As", QDialogButtonBox.ActionRole)
+ self.btnSave.setAutoDefault(False)
+
self.saveMenu = QMenu(self)
self.btnSave.setMenu(self.saveMenu)
From ceabf50cf914e152054737e10aefebcdcf4a9b7b Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 16:01:33 +0200
Subject: [PATCH 03/22] The histogram cap setting is now saved to the options
file
---
nw/core/options.py | 1 +
nw/gui/writingstats.py | 7 ++++++-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/nw/core/options.py b/nw/core/options.py
index 3bdf88f3..a91f6f68 100644
--- a/nw/core/options.py
+++ b/nw/core/options.py
@@ -58,6 +58,7 @@ class OptionState():
"hideZeros",
"hideNegative",
"groupByDay",
+ "histMax",
},
"GuiDocSplit": {
"spLevel",
diff --git a/nw/gui/writingstats.py b/nw/gui/writingstats.py
index 76c45a58..387c173e 100644
--- a/nw/gui/writingstats.py
+++ b/nw/gui/writingstats.py
@@ -212,11 +212,14 @@ class GuiWritingStats(QDialog):
self.histMax.setMinimum(100)
self.histMax.setMaximum(100000)
self.histMax.setSingleStep(100)
+ self.histMax.setValue(
+ self.optState.getInt("GuiWritingStats", "histMax", 2000)
+ )
self.histMax.valueChanged.connect(self._updateListBox)
self.optsBox = QHBoxLayout()
self.optsBox.addStretch(1)
- self.optsBox.addWidget(QLabel("Word count cap for histogram"), 0)
+ self.optsBox.addWidget(QLabel("Word count cap for the histogram"), 0)
self.optsBox.addWidget(self.histMax, 0)
# Buttons
@@ -280,6 +283,7 @@ class GuiWritingStats(QDialog):
hideZeros = self.hideZeros.isChecked()
hideNegative = self.hideNegative.isChecked()
groupByDay = self.groupByDay.isChecked()
+ histMax = self.histMax.value()
self.optState.setValue("GuiWritingStats", "winWidth", winWidth)
self.optState.setValue("GuiWritingStats", "winHeight", winHeight)
@@ -293,6 +297,7 @@ class GuiWritingStats(QDialog):
self.optState.setValue("GuiWritingStats", "hideZeros", hideZeros)
self.optState.setValue("GuiWritingStats", "hideNegative", hideNegative)
self.optState.setValue("GuiWritingStats", "groupByDay", groupByDay)
+ self.optState.setValue("GuiWritingStats", "histMax", histMax)
self.optState.saveSettings()
self.close()
From 5c1abfd22003cfe4bfc894b42882d2c13ac50138 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 16:11:16 +0200
Subject: [PATCH 04/22] Fix the slot for the various update signals
---
nw/gui/writingstats.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/nw/gui/writingstats.py b/nw/gui/writingstats.py
index 387c173e..6e4a4df8 100644
--- a/nw/gui/writingstats.py
+++ b/nw/gui/writingstats.py
@@ -461,8 +461,10 @@ class GuiWritingStats(QDialog):
return True
- def _updateListBox(self):
- """Load/reload the content of the list box.
+ def _updateListBox(self, dummyVar=None):
+ """Load/reload the content of the list box. The dummyVar
+ variable captures the variable sent from the widgets connecting
+ to it and discards it.
"""
self.listBox.clear()
self.timeFilter = 0.0
From 364aa774549932f7a21c97a06269aceeb54f905c Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 16:19:47 +0200
Subject: [PATCH 05/22] Added some info about the writing statistics tool in
the docs
---
docs/source/projects.rst | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/docs/source/projects.rst b/docs/source/projects.rst
index dbf90b6c..cc3808ec 100644
--- a/docs/source/projects.rst
+++ b/docs/source/projects.rst
@@ -277,3 +277,20 @@ Settings`.
For the backup to be able to run, the :guilabel:`Working Title` must be set in :guilabel:`Project
Settings`. This value is used to generate the folder name for the zip files. Without it, the
backup will not run at all, but produce a warning message.
+
+.. _a_proj_stats:
+
+Writing Statistics
+==================
+
+When you work on your project, a log file records when you opened it, when you closed it, and how
+many words you added to your novel and note files during the session. You can view this file in the
+``meta`` folder in the directory where you saved your project. The file is named
+``sessionStats.log``.
+
+A small tool to view the content of this file is available in the :guilabel:`Tools` menu under
+:guilabel:`Writing Statistics`. You can also launch it by pressing :kbd:`F6`.
+
+The tool will show a list of all your sessions, and a set of filters to apply to it. You can also
+export the filtered data to a JSON file or to a CSV file that can be opened by a spreadsheet
+application like for instance Libre Office Calc.
From d6e91df208d7abcd8075296e241002ad4784833d Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 16:30:57 +0200
Subject: [PATCH 06/22] Updates to the travis script
---
.travis.yml | 34 ++++++++++++++++++++++++----------
1 file changed, 24 insertions(+), 10 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 873ded23..704d96d4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,16 +4,14 @@ services:
- xvfb
language: python
cache: bundler
+
addons:
apt:
packages:
- libenchant-dev
- python3-pyqt5
- python3-pyqt5.qtsvg
-python:
- - "3.6"
- - "3.7"
- - "3.8"
+
install:
- pip install --upgrade pip
- pip install -r requirements.txt
@@ -23,10 +21,26 @@ install:
- pip install pytest-cov
- pip install pytest-qt
- pip install codecov
-script:
- - python -m pytest --cov=nw -m "project|core|gui" -v
-after_success:
- - codecov
-after_failure:
- - cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes
+stages:
+ - name: Initial
+ - name: Full
+
+jobs:
+ include:
+ - stage: Initial
+ python:
+ - 3.8
+ script:
+ - python -m pytest --cov=nw -m "project|core|gui" -v
+ after_success:
+ - codecov
+ after_failure:
+ - cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes
+
+ - stage: Full
+ python:
+ - 3.6
+ - 3.7
+ script:
+ - python -m pytest --cov=nw -m "project|core|gui" -v
From e8ad068a4296d355218b9896878a7e33a58aa038 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 16:43:06 +0200
Subject: [PATCH 07/22] Another try at getting 3.7 to run
---
.travis.yml | 20 +++++++-------------
1 file changed, 7 insertions(+), 13 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 704d96d4..84c57f36 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -29,18 +29,12 @@ stages:
jobs:
include:
- stage: Initial
- python:
- - 3.8
- script:
- - python -m pytest --cov=nw -m "project|core|gui" -v
- after_success:
- - codecov
- after_failure:
- - cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes
+ python: 3.8
+ script: python -m pytest --cov=nw -m "project|core|gui" -v
+ after_success: codecov
+ after_failure: cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes
- stage: Full
- python:
- - 3.6
- - 3.7
- script:
- - python -m pytest --cov=nw -m "project|core|gui" -v
+ python: 3.6
+ - python: 3.7
+ script: python -m pytest -m "project|core|gui" -v
From 71d908fa3ebc52b46c2817d4f179fd4b45fb48c7 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 16:52:28 +0200
Subject: [PATCH 08/22] Fixed 3.6 and added 3.9
---
.travis.yml | 44 +++++++++++++++++++++++++++++++-------------
1 file changed, 31 insertions(+), 13 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 84c57f36..561951f3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,23 +18,41 @@ install:
# - pip install pytest-faulthandler
- pip install PyVirtualDisplay
- pip install pytest-xvfb
- - pip install pytest-cov
- pip install pytest-qt
- - pip install codecov
stages:
- - name: Initial
- - name: Full
+ - name: Main
+ - name: Supported
jobs:
include:
- - stage: Initial
- python: 3.8
- script: python -m pytest --cov=nw -m "project|core|gui" -v
- after_success: codecov
- after_failure: cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes
+ - stage:
+ - Main
+ python:
+ - 3.8
+ install:
+ - pip install pytest-cov
+ - pip install codecov
+ script:
+ - python -m pytest --cov=nw -m "project|core|gui" -v
+ after_success:
+ - codecov
+ after_failure:
+ - cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes
- - stage: Full
- python: 3.6
- - python: 3.7
- script: python -m pytest -m "project|core|gui" -v
+ - stage:
+ - Supported
+ python:
+ - 3.6
+ script:
+ - python -m pytest -m "project|core|gui" -v
+
+ - python:
+ - 3.7
+ script:
+ - python -m pytest -m "project|core|gui" -v
+
+ - python:
+ - 3.9-dev
+ script:
+ - python -m pytest -m "project|core|gui" -v
From 59e588ebf571af9e7946d1b512fd899f14dde0e5 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 16:58:01 +0200
Subject: [PATCH 09/22] Move the install commands back
---
.travis.yml | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 561951f3..b2ffa4a2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,12 +13,13 @@ addons:
- python3-pyqt5.qtsvg
install:
- - pip install --upgrade pip
+# - pip install --upgrade pip
- pip install -r requirements.txt
-# - pip install pytest-faulthandler
- pip install PyVirtualDisplay
+ - pip install pytest-cov
- pip install pytest-xvfb
- pip install pytest-qt
+ - pip install codecov
stages:
- name: Main
@@ -30,9 +31,6 @@ jobs:
- Main
python:
- 3.8
- install:
- - pip install pytest-cov
- - pip install codecov
script:
- python -m pytest --cov=nw -m "project|core|gui" -v
after_success:
From 9922efda77b765e3e6b4542093fab6a77a01f2ff Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 17:06:05 +0200
Subject: [PATCH 10/22] Trying different packages
---
.travis.yml | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index b2ffa4a2..5d7fbf31 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,10 +11,11 @@ addons:
- libenchant-dev
- python3-pyqt5
- python3-pyqt5.qtsvg
+ - python3-lxml
install:
# - pip install --upgrade pip
- - pip install -r requirements.txt
+# - pip install -r requirements.txt
- pip install PyVirtualDisplay
- pip install pytest-cov
- pip install pytest-xvfb
@@ -32,7 +33,7 @@ jobs:
python:
- 3.8
script:
- - python -m pytest --cov=nw -m "project|core|gui" -v
+ - python -m pytest --cov=nw -v
after_success:
- codecov
after_failure:
@@ -43,14 +44,14 @@ jobs:
python:
- 3.6
script:
- - python -m pytest -m "project|core|gui" -v
+ - python -m pytest -v
- python:
- 3.7
script:
- - python -m pytest -m "project|core|gui" -v
+ - python -m pytest -v
- python:
- 3.9-dev
script:
- - python -m pytest -m "project|core|gui" -v
+ - python -m pytest -v
From 7eabb7a1f4611ae468cbd68e3e191af7837d8704 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 17:09:30 +0200
Subject: [PATCH 11/22] That did not work
---
.travis.yml | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 5d7fbf31..af892e46 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,7 +15,7 @@ addons:
install:
# - pip install --upgrade pip
-# - pip install -r requirements.txt
+ - pip install -r requirements.txt
- pip install PyVirtualDisplay
- pip install pytest-cov
- pip install pytest-xvfb
@@ -25,6 +25,8 @@ install:
stages:
- name: Main
- name: Supported
+ - name: Future
+# if: branch = main
jobs:
include:
@@ -51,7 +53,9 @@ jobs:
script:
- python -m pytest -v
- - python:
+ - stage:
+ - Future
+ python:
- 3.9-dev
script:
- python -m pytest -v
From d5b7c027fb47ceac5f9d62cfaf6f6353ffc04dec Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 17:18:42 +0200
Subject: [PATCH 12/22] Added conditional for future test stage
---
.travis.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index af892e46..4e0f2cf7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,7 +26,7 @@ stages:
- name: Main
- name: Supported
- name: Future
-# if: branch = main
+ if: branch = main
jobs:
include:
From 6a664a1890066e82d2ee261aaf8ce93b3a62611e Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 17:23:52 +0200
Subject: [PATCH 13/22] Drop 3.9 for now as it takes too long to build
dependencies
---
.travis.yml | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 4e0f2cf7..eb85544e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,8 @@ install:
stages:
- name: Main
- name: Supported
- - name: Future
- if: branch = main
+# - name: Future
+# if: branch = main
jobs:
include:
@@ -53,9 +53,9 @@ jobs:
script:
- python -m pytest -v
- - stage:
- - Future
- python:
- - 3.9-dev
- script:
- - python -m pytest -v
+# - stage:
+# - Future
+# python:
+# - 3.9-dev
+# script:
+# - python -m pytest -v
From 76fcc7434251f10164bbe964c8641e83f164bf33 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 18:27:16 +0200
Subject: [PATCH 14/22] Make writing stats and buid dialogs non-modal
---
nw/guimain.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/nw/guimain.py b/nw/guimain.py
index f4dc33d8..3768fc3c 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -803,16 +803,18 @@ class GuiMain(QMainWindow):
"""Open the build project dialog.
"""
if self.hasProject:
- dlgExport = GuiBuildNovel(self, self.theProject)
- dlgExport.exec_()
+ dlgBuild = GuiBuildNovel(self, self.theProject)
+ dlgBuild.setModal(False)
+ dlgBuild.show()
return True
def showWritingStatsDialog(self):
"""Open the session log dialog.
"""
if self.hasProject:
- dlgTLine = GuiWritingStats(self, self.theProject)
- dlgTLine.exec_()
+ dlgStats = GuiWritingStats(self, self.theProject)
+ dlgStats.setModal(False)
+ dlgStats.show()
return True
def makeAlert(self, theMessage, theLevel=nwAlert.INFO):
From d1982c0abbbcd03840ec0c0d95ab539027c25449 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 18:37:25 +0200
Subject: [PATCH 15/22] Moved the about dialog functions to main gui class
---
nw/gui/build.py | 2 --
nw/gui/mainmenu.py | 24 +++---------------------
nw/guimain.py | 18 +++++++++++++++++-
3 files changed, 20 insertions(+), 24 deletions(-)
diff --git a/nw/gui/build.py b/nw/gui/build.py
index 93aa8271..58b152f8 100644
--- a/nw/gui/build.py
+++ b/nw/gui/build.py
@@ -401,8 +401,6 @@ class GuiBuildNovel(QDialog):
self.setLayout(self.outerBox)
self.buildNovel.setFocus()
- self.show()
-
logger.debug("GuiBuildNovel initialisation complete")
# Load from Cache
diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py
index 7f996dda..2ac66e33 100644
--- a/nw/gui/mainmenu.py
+++ b/nw/gui/mainmenu.py
@@ -28,13 +28,10 @@
import logging
import nw
-from os import path
-
from PyQt5.QtCore import QUrl, QProcess
from PyQt5.QtGui import QDesktopServices
-from PyQt5.QtWidgets import QMenuBar, QAction, QMessageBox
+from PyQt5.QtWidgets import QMenuBar, QAction
-from nw.gui.about import GuiAbout
from nw.constants import nwItemType, nwItemClass, nwDocAction, nwDocInsert
logger = logging.getLogger(__name__)
@@ -150,21 +147,6 @@ class GuiMainMenu(QMenuBar):
self.theProject.setAutoOutline(theMode)
return True
- def _showAbout(self):
- """Show the about dialog.
- """
- if self.mainConf.showGUI:
- msgAbout = GuiAbout(self.theParent)
- msgAbout.exec_()
- return True
-
- def _showAboutQt(self):
- """Show Qt's own About dialog.
- """
- msgBox = QMessageBox()
- msgBox.aboutQt(self.theParent,"About Qt")
- return True
-
def _openAssistant(self):
"""Open the documentation in Qt Assistant.
"""
@@ -853,13 +835,13 @@ class GuiMainMenu(QMenuBar):
# Help > About
self.aAboutNW = QAction("About %s" % self.mainConf.appName, self)
self.aAboutNW.setStatusTip("About %s" % self.mainConf.appName)
- self.aAboutNW.triggered.connect(self._showAbout)
+ self.aAboutNW.triggered.connect(self.theParent.showAboutNWDialog)
self.helpMenu.addAction(self.aAboutNW)
# Help > About Qt5
self.aAboutQt = QAction("About Qt5", self)
self.aAboutQt.setStatusTip("About Qt5")
- self.aAboutQt.triggered.connect(self._showAboutQt)
+ self.aAboutQt.triggered.connect(self.theParent.showAboutQtDialog)
self.helpMenu.addAction(self.aAboutQt)
# Help > Separator
diff --git a/nw/guimain.py b/nw/guimain.py
index 3768fc3c..09571d61 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -43,7 +43,7 @@ from nw.gui import (
GuiBuildNovel, GuiDocEditor, GuiDocMerge, GuiDocSplit, GuiDocViewDetails,
GuiDocViewer, GuiItemDetails, GuiItemEditor, GuiMainMenu, GuiMainStatus,
GuiOutline, GuiOutlineDetails, GuiPreferences, GuiProjectLoad, GuiTheme,
- GuiProjectSettings, GuiProjectTree, GuiWritingStats
+ GuiProjectSettings, GuiProjectTree, GuiWritingStats, GuiAbout
)
from nw.core import NWProject, NWDoc, NWIndex
from nw.constants import nwFiles, nwItemType, nwAlert
@@ -817,6 +817,22 @@ class GuiMain(QMainWindow):
dlgStats.show()
return True
+ def showAboutNWDialog(self):
+ """Show the about dialog for novelWriter.
+ """
+ if self.mainConf.showGUI:
+ dlgAbout = GuiAbout(self)
+ dlgAbout.exec_()
+ return True
+
+ def showAboutQtDialog(self):
+ """Show the about dialog for Qt.
+ """
+ if self.mainConf.showGUI:
+ msgBox = QMessageBox()
+ msgBox.aboutQt(self, "About Qt")
+ return True
+
def makeAlert(self, theMessage, theLevel=nwAlert.INFO):
"""Alert both the user and the logger at the same time. Message
can be either a string or an array of strings. Severity level is
From 16cfa13abbe3c186955d76818a4e0b11a54483af Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 18:39:03 +0200
Subject: [PATCH 16/22] Removed show() from a few dialogs
---
nw/gui/docmerge.py | 1 -
nw/gui/docsplit.py | 1 -
nw/gui/preferences.py | 2 --
3 files changed, 4 deletions(-)
diff --git a/nw/gui/docmerge.py b/nw/gui/docmerge.py
index 6b16102d..f6a565d1 100644
--- a/nw/gui/docmerge.py
+++ b/nw/gui/docmerge.py
@@ -79,7 +79,6 @@ class GuiDocMerge(QDialog):
self.setLayout(self.outerBox)
self.rejected.connect(self._doClose)
- self.show()
self._populateList()
diff --git a/nw/gui/docsplit.py b/nw/gui/docsplit.py
index 98ef95d7..d143a056 100644
--- a/nw/gui/docsplit.py
+++ b/nw/gui/docsplit.py
@@ -92,7 +92,6 @@ class GuiDocSplit(QDialog):
self.setLayout(self.outerBox)
self.rejected.connect(self._doClose)
- self.show()
self._populateList()
diff --git a/nw/gui/preferences.py b/nw/gui/preferences.py
index 999464b3..31986546 100644
--- a/nw/gui/preferences.py
+++ b/nw/gui/preferences.py
@@ -71,8 +71,6 @@ class GuiPreferences(PagedDialog):
self.buttonBox.rejected.connect(self._doClose)
self.addControls(self.buttonBox)
- self.show()
-
logger.debug("GuiPreferences initialisation complete")
return
From aa8b2a7f234f8e15381a1e76d5c04664098ce6ca Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 19:17:32 +0200
Subject: [PATCH 17/22] Add build date to docs
---
docs/source/conf.py | 27 ++-------------------------
docs/source/index.rst | 2 ++
2 files changed, 4 insertions(+), 25 deletions(-)
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 49c00c9c..e9a330a7 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -31,39 +31,16 @@ release = "0.11.0"
# -- General configuration ---------------------------------------------------
-# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = "1.0"
-
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named "sphinx.ext.*") or your custom
-# ones.
extensions = [
"sphinx_rtd_theme",
]
-
-# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
-
-# The suffix(es) of source filenames.
-# You can specify multiple suffix as a list of string:
-# source_suffix = [".rst", ".md"]
source_suffix = ".rst"
-
-# The master toctree document.
master_doc = "index"
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-# This is also used if you do content translation via gettext catalogs.
-# Usually you set "language" from the command line for these cases.
+today_fmt = "%A, %B %d %Y at %H:%M"
language = None
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
-
-# The name of the Pygments (syntax highlighting) style to use.
pygments_style = None
@@ -98,7 +75,7 @@ html_css_files = [
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
-htmlhelp_basename = "novelWriterdoc"
+htmlhelp_basename = "novelWriterDoc"
# -- Options for LaTeX output ------------------------------------------------
diff --git a/docs/source/index.rst b/docs/source/index.rst
index da2335db..152c6666 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -26,6 +26,8 @@ novelWriter |release|
:target: https://pypi.org/project/novelWriter/
:alt: Python Version
+**Last Updated:** |today|
+
novelWriter is a markdown-like text editor designed for writing novels and larger projects of many
smaller plain text documents. It uses its own flavour of markdown that supports a meta data syntax
for comments, synopsis and cross-referencing between files. The idea is to have a simple text editor
From a9b6e00ba7fcf38e0b54a5c1e9b5c3da2fc9bbe4 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 20:55:45 +0200
Subject: [PATCH 18/22] Cleanup of unused imports
---
nw/constants/constants.py | 2 +-
nw/core/document.py | 2 +-
nw/core/tools.py | 2 --
nw/error.py | 4 ++--
nw/gui/doceditor.py | 2 +-
nw/gui/docsplit.py | 1 +
nw/gui/itemdetails.py | 4 ++--
nw/gui/itemeditor.py | 1 -
nw/gui/outlinedetails.py | 3 ++-
nw/gui/projtree.py | 6 +++---
nw/gui/statusbar.py | 4 ++--
nw/guimain.py | 2 +-
12 files changed, 16 insertions(+), 17 deletions(-)
diff --git a/nw/constants/constants.py b/nw/constants/constants.py
index c6e17a15..cd7bf350 100644
--- a/nw/constants/constants.py
+++ b/nw/constants/constants.py
@@ -25,7 +25,7 @@
along with this program. If not, see .
"""
-from nw.constants.enum import nwItemClass, nwItemLayout, nwOutline, nwDocInsert
+from nw.constants.enum import nwItemClass, nwItemLayout, nwOutline
class nwConst():
diff --git a/nw/core/document.py b/nw/core/document.py
index 560815e6..8c8b523f 100644
--- a/nw/core/document.py
+++ b/nw/core/document.py
@@ -28,7 +28,7 @@
import logging
import nw
-from os import path, mkdir, rename, unlink
+from os import path, rename, unlink
from nw.core.item import NWItem
from nw.constants import nwAlert
diff --git a/nw/core/tools.py b/nw/core/tools.py
index dd2c8c84..b46f0db8 100644
--- a/nw/core/tools.py
+++ b/nw/core/tools.py
@@ -30,8 +30,6 @@
import logging
import nw
-from os import path, unlink, rmdir
-
logger = logging.getLogger(__name__)
# =============================================================================================== #
diff --git a/nw/error.py b/nw/error.py
index 0301f3ba..e8466657 100644
--- a/nw/error.py
+++ b/nw/error.py
@@ -76,9 +76,9 @@ def exceptionHandler(exType, exValue, exTrace):
"""Function to catch unhandled global exceptions.
"""
import logging
- from traceback import print_tb, format_tb
+ from traceback import print_tb
from nw import CONFIG
- from PyQt5.QtWidgets import qApp, QApplication, QErrorMessage, QMessageBox
+ from PyQt5.QtWidgets import qApp, QErrorMessage
logger = logging.getLogger(__name__)
logger.error("%s: %s" % (exType.__name__, str(exValue)))
diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py
index a4d88f65..2ec72b62 100644
--- a/nw/gui/doceditor.py
+++ b/nw/gui/doceditor.py
@@ -39,7 +39,7 @@ from PyQt5.QtCore import (
Qt, QSize, QThread, QTimer, pyqtSlot, QRegExp, QRegularExpression
)
from PyQt5.QtGui import (
- QTextCursor, QTextOption, QKeySequence, QFont, QColor, QPalette, QIcon,
+ QTextCursor, QTextOption, QKeySequence, QFont, QColor, QPalette,
QTextDocument, QCursor, QPixmap
)
from PyQt5.QtWidgets import (
diff --git a/nw/gui/docsplit.py b/nw/gui/docsplit.py
index d143a056..60b4069a 100644
--- a/nw/gui/docsplit.py
+++ b/nw/gui/docsplit.py
@@ -33,6 +33,7 @@ from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QComboBox, QListWidget, QAbstractItemView,
QListWidgetItem, QDialogButtonBox, QLabel
)
+
from nw.constants import nwAlert, nwItemType, nwItemClass, nwItemLayout
from nw.gui.custom import QHelpLabel
from nw.core import NWDoc
diff --git a/nw/gui/itemdetails.py b/nw/gui/itemdetails.py
index 4d0f5641..dea27381 100644
--- a/nw/gui/itemdetails.py
+++ b/nw/gui/itemdetails.py
@@ -29,11 +29,11 @@ import logging
import nw
from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QFont, QIcon, QPixmap
+from PyQt5.QtGui import QFont, QPixmap
from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel
from nw.constants import (
- nwLabels, nwItemClass, nwItemType, nwItemLayout, nwUnicode
+ nwLabels, nwItemClass, nwItemType, nwItemLayout
)
logger = logging.getLogger(__name__)
diff --git a/nw/gui/itemeditor.py b/nw/gui/itemeditor.py
index 65c4cde9..b1c2421a 100644
--- a/nw/gui/itemeditor.py
+++ b/nw/gui/itemeditor.py
@@ -28,7 +28,6 @@
import logging
import nw
-from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QGridLayout, QLineEdit, QComboBox, QLabel,
QDialogButtonBox
diff --git a/nw/gui/outlinedetails.py b/nw/gui/outlinedetails.py
index 5e197449..e18ac9d1 100644
--- a/nw/gui/outlinedetails.py
+++ b/nw/gui/outlinedetails.py
@@ -30,7 +30,8 @@ import nw
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
- QScrollArea, QWidget, QGridLayout, QHBoxLayout, QGroupBox, QLabel, QSizePolicy
+ QScrollArea, QWidget, QGridLayout, QHBoxLayout, QGroupBox, QLabel,
+ QSizePolicy
)
from nw.constants import nwLabels, nwKeyWords
diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py
index d6eb8cdf..3baf5257 100644
--- a/nw/gui/projtree.py
+++ b/nw/gui/projtree.py
@@ -30,15 +30,15 @@ import logging
import nw
from PyQt5.QtCore import Qt, QSize
-from PyQt5.QtGui import QFont, QColor, QIcon
+from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (
qApp, QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMessageBox,
- QHeaderView, QMenu, QAction
+ QMenu, QAction
)
from nw.core import NWDoc
from nw.constants import (
- nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert, nwUnicode
+ nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert
)
logger = logging.getLogger(__name__)
diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py
index 040125aa..395fc5cc 100644
--- a/nw/gui/statusbar.py
+++ b/nw/gui/statusbar.py
@@ -30,8 +30,8 @@ import nw
from time import time
-from PyQt5.QtCore import Qt, QTimer
-from PyQt5.QtGui import QColor, QPixmap, QFont, QPainter
+from PyQt5.QtCore import QTimer
+from PyQt5.QtGui import QColor, QPainter
from PyQt5.QtWidgets import qApp, QStatusBar, QLabel, QAbstractButton
from nw.core import NWSpellCheck
diff --git a/nw/guimain.py b/nw/guimain.py
index 09571d61..46db9d1f 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -46,7 +46,7 @@ from nw.gui import (
GuiProjectSettings, GuiProjectTree, GuiWritingStats, GuiAbout
)
from nw.core import NWProject, NWDoc, NWIndex
-from nw.constants import nwFiles, nwItemType, nwAlert
+from nw.constants import nwItemType, nwAlert
logger = logging.getLogger(__name__)
From 56b2cbee10f62d65e46571883e1587feff3aadf9 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 21:54:32 +0200
Subject: [PATCH 19/22] Code cleanup and added comments
---
nw/common.py | 12 +++--
nw/config.py | 14 +++--
nw/core/document.py | 9 ++--
nw/core/spellcheck.py | 1 -
nw/core/tokenizer.py | 3 --
nw/error.py | 2 +-
nw/gui/docmerge.py | 2 -
nw/gui/docsplit.py | 4 +-
nw/gui/itemeditor.py | 3 +-
nw/gui/mainmenu.py | 27 ++++++----
nw/gui/outline.py | 4 ++
nw/gui/preferences.py | 23 +++++---
nw/gui/projload.py | 3 ++
nw/gui/projsettings.py | 53 +++++++++++++++----
nw/gui/projtree.py | 28 +++++++++-
nw/gui/statusbar.py | 2 +-
nw/gui/theme.py | 109 +++++++++++++++++++-------------------
nw/gui/writingstats.py | 2 +-
nw/guimain.py | 116 +++++++++++++++++++++++++----------------
19 files changed, 270 insertions(+), 147 deletions(-)
diff --git a/nw/common.py b/nw/common.py
index 872365df..b15e148a 100644
--- a/nw/common.py
+++ b/nw/common.py
@@ -35,6 +35,8 @@ from nw.constants import nwConst
logger = logging.getLogger(__name__)
def checkString(checkValue, defaultValue, allowNone=False):
+ """Check if a variable is a string or a none.
+ """
if allowNone:
if checkValue == None:
return None
@@ -45,6 +47,8 @@ def checkString(checkValue, defaultValue, allowNone=False):
return defaultValue
def checkInt(checkValue, defaultValue, allowNone=False):
+ """Check if a variable is an integer or a none.
+ """
if allowNone:
if checkValue == None:
return None
@@ -56,6 +60,8 @@ def checkInt(checkValue, defaultValue, allowNone=False):
return defaultValue
def checkBool(checkValue, defaultValue, allowNone=False):
+ """Check if a variable is a boolean or a none.
+ """
if allowNone:
if checkValue == None:
return None
@@ -92,7 +98,8 @@ def isHandle(theString):
return not invalidChar
def colRange(rgbStart, rgbEnd, nStep):
-
+ """Generate a range of colours from one RGB value to another.
+ """
if len(rgbStart) != 3 and len(rgbEnd) != 3 and nStep < 1:
logger.error("Cannot create colour range from given parameters")
return None
@@ -124,7 +131,7 @@ def colRange(rgbStart, rgbEnd, nStep):
def formatInt(theInt):
"""Formats an integer with k, M, G etc.
"""
- postFix = ["k","M","G","T","P","E"]
+ postFix = ["k", "M", "G", "T", "P", "E"]
theVal = float(theInt)
if theVal > 1000.0:
@@ -153,7 +160,6 @@ def splitVersionNumber(vString):
""" Splits a version string on the form aa.bb.cc into major, minor
and patch, and computes an integer value aabbcc.
"""
-
vMajor = 0
vMinor = 0
vPatch = 0
diff --git a/nw/config.py b/nw/config.py
index 619125fa..97e78c90 100644
--- a/nw/config.py
+++ b/nw/config.py
@@ -88,7 +88,7 @@ class Config:
self.guiSyntax = "default_light"
self.guiIcons = "typicons_colour_light"
self.guiDark = False
- self.guiLang = "en" # Hardcoded for now
+ self.guiLang = "en" # Hardcoded for now since the GUI is only in English
self.guiFont = ""
self.guiFontSize = 11
self.guiScale = 1.0 # Set automatically by Theme class
@@ -286,7 +286,7 @@ class Config:
# Check if config file exists
if self.confPath is not None:
- if path.isfile(path.join(self.confPath,self.confFile)):
+ if path.isfile(path.join(self.confPath, self.confFile)):
# If it exists, load it
self.loadConfig()
else:
@@ -855,7 +855,9 @@ class Config:
##
def _unpackList(self, inStr, listLen, listDefault, castTo=int):
- inData = inStr.split(",")
+ """Unpack a comma separated string of items into a list.
+ """
+ inData = inStr.split(",")
outData = []
for i in range(listLen):
try:
@@ -865,9 +867,13 @@ class Config:
return outData
def _packList(self, inData):
+ """Pack a list of items into a comma separated string.
+ """
return ", ".join(str(inVal) for inVal in inData)
def _parseLine(self, cnfParse, cnfSec, cnfName, cnfType, cnfDefault):
+ """Parse a line and return the correct datatype.
+ """
if cnfParse.has_section(cnfSec):
if cnfParse.has_option(cnfSec, cnfName):
if cnfType == self.CNF_STR:
@@ -883,6 +889,8 @@ class Config:
return cnfDefault
def _checkNone(self, checkVal):
+ """Convert a string to a none type.
+ """
if checkVal is None:
return None
if isinstance(checkVal, str):
diff --git a/nw/core/document.py b/nw/core/document.py
index 8c8b523f..0c29f995 100644
--- a/nw/core/document.py
+++ b/nw/core/document.py
@@ -44,10 +44,11 @@ class NWDoc():
self.mainConf = nw.CONFIG
self.theProject = theProject
self.theParent = theParent
- self.theItem = None
- self.docHandle = None
- self.fileLoc = None
- self.docMeta = ""
+
+ self.theItem = None
+ self.docHandle = None
+ self.fileLoc = None
+ self.docMeta = ""
# Internal Mapping
self.makeAlert = self.theParent.makeAlert
diff --git a/nw/core/spellcheck.py b/nw/core/spellcheck.py
index 28e25e40..d4740b56 100644
--- a/nw/core/spellcheck.py
+++ b/nw/core/spellcheck.py
@@ -195,7 +195,6 @@ class NWSpellEnchant(NWSpellCheck):
class NWSpellEnchantDummy:
"""Fallback for when Enchant is selected, but not installed.
"""
-
def __init__(self):
return
diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py
index 3050cfc0..2ea8f330 100644
--- a/nw/core/tokenizer.py
+++ b/nw/core/tokenizer.py
@@ -227,7 +227,6 @@ class Tokenizer():
"""Set the text for the tokenizer from a handle. If theText is
not set, load it from the file.
"""
-
self.theHandle = theHandle
self.theItem = self.theProject.projTree[theHandle]
if self.theItem is None:
@@ -308,7 +307,6 @@ class Tokenizer():
4: The internal formatting map of the text, self.FMT_*
5: The style of the block, self.A_*
"""
-
# RegExes for adding formatting tags within text lines
rxFormats = [
(QRegularExpression(nwRegEx.FMT_I), [None, self.FMT_I_B, None, self.FMT_I_E]),
@@ -457,7 +455,6 @@ class Tokenizer():
"""Apply formatting to the text headers according to document
layout and user settings.
"""
-
# No special header formatting for notes and no-layout files
if self.isNone or self.isNote:
return
diff --git a/nw/error.py b/nw/error.py
index e8466657..4c3fef17 100644
--- a/nw/error.py
+++ b/nw/error.py
@@ -46,7 +46,7 @@ def formatHtmlErrMsg(exType, exValue, exTrace):
"
Please report this error by submitting an issue report on "
"GitHub, providing a description and this error message. "
"URL: <{issueUrl}>.
"
- "Environment
Version: {nwVersion}, OS: {osType} ({osKernel}),"
+ "
Environment
Version: {nwVersion}, OS: {osType} ({osKernel}), "
"Python: {pyVersion} ({pyHexVer:#x}), Qt: {qtVers}, PyQt: {pyqtVers}
"
"Error Type
{exType}: {exMessage}
"
"Traceback
{exTrace}
"
diff --git a/nw/gui/docmerge.py b/nw/gui/docmerge.py
index f6a565d1..547cbd44 100644
--- a/nw/gui/docmerge.py
+++ b/nw/gui/docmerge.py
@@ -95,7 +95,6 @@ class GuiDocMerge(QDialog):
create a new file in the same parent folder. The old files are
not removed in the merge process, and must be deleted manually.
"""
-
logger.verbose("GuiDocMerge merge button clicked")
finalOrder = []
@@ -142,7 +141,6 @@ class GuiDocMerge(QDialog):
are then added to the list view in order. The list itself can be
reordered by the user.
"""
-
tHandle = self.theParent.treeView.getSelectedHandle()
self.sourceItem = tHandle
if tHandle is None:
diff --git a/nw/gui/docsplit.py b/nw/gui/docsplit.py
index 60b4069a..218b4855 100644
--- a/nw/gui/docsplit.py
+++ b/nw/gui/docsplit.py
@@ -110,7 +110,6 @@ class GuiDocSplit(QDialog):
settings. The old file is not removed in the merge process, and
must be deleted manually.
"""
-
logger.verbose("GuiDocSplit split button clicked")
if self.sourceItem is None:
@@ -132,7 +131,7 @@ class GuiDocSplit(QDialog):
nLines = len(theLines)
theLines.insert(0, "%Split Doc")
logger.debug(
- "Splitting document %s with %d lines" % (self.sourceItem,nLines)
+ "Splitting document %s with %d lines" % (self.sourceItem, nLines)
)
finalOrder = []
@@ -210,7 +209,6 @@ class GuiDocSplit(QDialog):
are then added to the list view in order. The list itself can be
reordered by the user.
"""
-
if self.sourceItem is None:
self.sourceItem = self.theParent.treeView.getSelectedHandle()
diff --git a/nw/gui/itemeditor.py b/nw/gui/itemeditor.py
index b1c2421a..1a149a09 100644
--- a/nw/gui/itemeditor.py
+++ b/nw/gui/itemeditor.py
@@ -150,7 +150,6 @@ class GuiItemEditor(QDialog):
def _doSave(self):
"""Save the setting to the item.
"""
-
logger.verbose("ItemEditor save button clicked")
itemName = self.editName.text()
@@ -171,6 +170,8 @@ class GuiItemEditor(QDialog):
return
def _doClose(self):
+ """Close the dialog without saving the settings.
+ """
logger.verbose("ItemEditor close button clicked")
self.close()
return
diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py
index 2ac66e33..b81bac82 100644
--- a/nw/gui/mainmenu.py
+++ b/nw/gui/mainmenu.py
@@ -192,7 +192,8 @@ class GuiMainMenu(QMenuBar):
##
def _buildProjectMenu(self):
-
+ """Assemble the Project menu.
+ """
# Project
self.projMenu = self.addMenu("&Project")
@@ -295,7 +296,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildDocumentMenu(self):
-
+ """Assemble the Document menu.
+ """
# Document
self.docuMenu = self.addMenu("&Document")
@@ -377,7 +379,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildEditMenu(self):
-
+ """Assemble the Edit menu.
+ """
# Edit
self.editMenu = self.addMenu("&Edit")
@@ -439,7 +442,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildViewMenu(self):
-
+ """Assemble the View menu.
+ """
# View
self.viewMenu = self.addMenu("&View")
@@ -486,7 +490,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildInsertMenu(self):
-
+ """Assemble the Insert menu.
+ """
# Insert
self.insertMenu = self.addMenu("&Insert")
@@ -576,7 +581,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildSearchMenu(self):
-
+ """Assemble the Search menu.
+ """
# Search
self.srcMenu = self.addMenu("&Search")
@@ -627,7 +633,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildFormatMenu(self):
-
+ """Assemble the Format menu.
+ """
# Format
self.fmtMenu = self.addMenu("&Format")
@@ -732,7 +739,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildToolsMenu(self):
-
+ """Assemble the Tools menu.
+ """
# Tools
self.toolsMenu = self.addMenu("&Tools")
@@ -828,7 +836,8 @@ class GuiMainMenu(QMenuBar):
return
def _buildHelpMenu(self):
-
+ """Assemble the Help menu.
+ """
# Help
self.helpMenu = self.addMenu("&Help")
diff --git a/nw/gui/outline.py b/nw/gui/outline.py
index cd4ff718..c1986b98 100644
--- a/nw/gui/outline.py
+++ b/nw/gui/outline.py
@@ -195,8 +195,10 @@ class GuiOutline(QTreeWidget):
tLine = int(tItem.text(self.colIndex[nwOutline.LINE]))
except:
tLine = 1
+
logger.verbose("User selected entry with handle %s on line %s" % (tHandle, tLine))
self.theParent.openDocument(tHandle, tLine=tLine-1, doScroll=True)
+
return
def _itemSelected(self):
@@ -208,6 +210,7 @@ class GuiOutline(QTreeWidget):
tHandle = selItems[0].data(self.colIndex[nwOutline.TITLE], Qt.UserRole)
sTitle = selItems[0].data(self.colIndex[nwOutline.LINE], Qt.UserRole)
self.theParent.projMeta.showItem(tHandle, sTitle)
+
return
def _headerRightClick(self, clickPos):
@@ -232,6 +235,7 @@ class GuiOutline(QTreeWidget):
if theItem in self.colIndex:
self.setColumnHidden(self.colIndex[theItem], not isChecked)
self._saveHeaderState()
+
return
##
diff --git a/nw/gui/preferences.py b/nw/gui/preferences.py
index 31986546..8b103194 100644
--- a/nw/gui/preferences.py
+++ b/nw/gui/preferences.py
@@ -80,7 +80,9 @@ class GuiPreferences(PagedDialog):
##
def _doSave(self):
-
+ """Trigger all the save functions in the tabs, and collect the
+ status of the saves.
+ """
logger.verbose("ConfigEditor save button clicked")
validEntries = True
@@ -115,6 +117,8 @@ class GuiPreferences(PagedDialog):
return
def _doClose(self):
+ """Close the preferences without saving the changes.
+ """
logger.verbose("ConfigEditor close button clicked")
self.close()
return
@@ -284,7 +288,8 @@ class GuiConfigEditGeneralTab(QWidget):
return
def saveValues(self):
-
+ """Save the values set for this tab.
+ """
validEntries = True
needsRestart = False
@@ -329,7 +334,6 @@ class GuiConfigEditGeneralTab(QWidget):
def _backupFolder(self):
"""Open a dialog to select the backup folder.
"""
-
currDir = self.backupPath
if not path.isdir(currDir):
currDir = ""
@@ -515,7 +519,8 @@ class GuiConfigEditLayoutTab(QWidget):
return
def saveValues(self):
-
+ """Save the values set for this tab.
+ """
validEntries = True
needsRestart = False
@@ -681,7 +686,8 @@ class GuiConfigEditEditingTab(QWidget):
return
def saveValues(self):
-
+ """Save the values set for this tab.
+ """
validEntries = True
needsRestart = False
@@ -712,6 +718,8 @@ class GuiConfigEditEditingTab(QWidget):
##
def _disableComboItem(self, theList, theValue):
+ """Disable a list item in the combo box.
+ """
theIdx = theList.findData(theValue)
theModel = theList.model()
anItem = theModel.item(1)
@@ -719,6 +727,8 @@ class GuiConfigEditEditingTab(QWidget):
return theModel
def _doUpdateSpellTool(self, currIdx):
+ """Update the list of dictionaries based on spell tool selected.
+ """
spellTool = self.spellToolList.currentData()
self._updateLanguageList(spellTool)
return
@@ -903,7 +913,8 @@ class GuiConfigEditAutoReplaceTab(QWidget):
return
def saveValues(self):
-
+ """Save the values set for this tab.
+ """
validEntries = True
needsRestart = False
diff --git a/nw/gui/projload.py b/nw/gui/projload.py
index 738a493c..eddbbb33 100644
--- a/nw/gui/projload.py
+++ b/nw/gui/projload.py
@@ -152,6 +152,7 @@ class GuiProjectLoad(QDialog):
"""
logger.verbose("GuiProjectLoad open button clicked")
self._saveDialogState()
+
selItems = self.listBox.selectedItems()
if selItems:
self.openPath = selItems[0].data(self.C_NAME, Qt.UserRole)
@@ -160,6 +161,7 @@ class GuiProjectLoad(QDialog):
else:
self.openPath = None
self.openState = self.NONE_STATE
+
return
def _doSelectRecent(self):
@@ -189,6 +191,7 @@ class GuiProjectLoad(QDialog):
self.openPath = thePath
self.openState = self.OPEN_STATE
self.accept()
+
return
def _doClose(self):
diff --git a/nw/gui/projsettings.py b/nw/gui/projsettings.py
index b8b47a90..86ea3ead 100644
--- a/nw/gui/projsettings.py
+++ b/nw/gui/projsettings.py
@@ -97,6 +97,7 @@ class GuiProjectSettings(PagedDialog):
bookTitle = self.tabMain.editTitle.text()
bookAuthors = self.tabMain.editAuthors.toPlainText()
doBackup = not self.tabMain.doBackup.isChecked()
+
self.theProject.setProjectName(projName)
self.theProject.setBookTitle(bookTitle)
self.theProject.setBookAuthors(bookAuthors)
@@ -105,11 +106,14 @@ class GuiProjectSettings(PagedDialog):
if self.tabStatus.colChanged:
statusCol = self.tabStatus.getNewList()
self.theProject.setStatusColours(statusCol)
+
if self.tabImport.colChanged:
importCol = self.tabImport.getNewList()
self.theProject.setImportColours(importCol)
+
if self.tabStatus.colChanged or self.tabImport.colChanged:
self.theParent.rebuildTree()
+
if self.tabReplace.arChanged:
newList = self.tabReplace.getNewList()
self.theProject.setAutoReplace(newList)
@@ -119,7 +123,7 @@ class GuiProjectSettings(PagedDialog):
return
def _doClose(self):
- """Close the dialog.
+ """Save settings and close the dialog.
"""
winWidth = self.mainConf.rpxInt(self.width())
winHeight = self.mainConf.rpxInt(self.height())
@@ -372,6 +376,8 @@ class GuiProjectEditStatus(QWidget):
##
def _selectColour(self):
+ """Open a dialog to select the status icon colour.
+ """
logger.verbose("Item colour button clicked")
if self.selColour is not None:
newCol = QColorDialog.getColor(
@@ -386,6 +392,8 @@ class GuiProjectEditStatus(QWidget):
return
def _newItem(self):
+ """Create a new status item.
+ """
logger.verbose("New item button clicked")
newItem = self._addItem("New Item", (0, 0, 0), None, 0)
newItem.setBackground(QBrush(QColor(0, 255, 0, 80)))
@@ -393,6 +401,8 @@ class GuiProjectEditStatus(QWidget):
return
def _delItem(self):
+ """Delete a status item.
+ """
logger.verbose("Delete item button clicked")
selItem = self._getSelectedItem()
if selItem is not None:
@@ -408,6 +418,8 @@ class GuiProjectEditStatus(QWidget):
return
def _saveItem(self):
+ """Save changes made to a status item.
+ """
logger.verbose("Save item button clicked")
selItem = self._getSelectedItem()
iRow = self.listBox.row(selItem)
@@ -427,6 +439,8 @@ class GuiProjectEditStatus(QWidget):
return
def _addItem(self, iName, iCol, oName, nUse):
+ """Add a status item to the list.
+ """
newIcon = QPixmap(self.iPx, self.iPx)
newIcon.fill(QColor(*iCol))
newItem = QListWidgetItem()
@@ -439,11 +453,14 @@ class GuiProjectEditStatus(QWidget):
return newItem
def _selectedItem(self):
+ """Extract the info of a selected item and populate the settings
+ boxes and button.
+ """
logger.verbose("Item selected")
selItem = self._getSelectedItem()
if selItem is not None:
- selIdx = selItem.data(Qt.UserRole)
- selVal = self.colData[selIdx]
+ selIdx = selItem.data(Qt.UserRole)
+ selVal = self.colData[selIdx]
self.selColour = QColor(selVal[1], selVal[2], selVal[3])
newIcon = QPixmap(self.iPx, self.iPx)
newIcon.fill(self.selColour)
@@ -459,6 +476,8 @@ class GuiProjectEditStatus(QWidget):
##
def _getSelectedItem(self):
+ """Get the currently selected item.
+ """
selItem = self.listBox.selectedItems()
if len(selItem) == 0:
return None
@@ -467,6 +486,8 @@ class GuiProjectEditStatus(QWidget):
return None
def _rowsMoved(self):
+ """A row has been moved, so sett the changed flag.
+ """
logger.verbose("A drag move event occurred")
self.colChanged = True
return
@@ -506,9 +527,9 @@ class GuiProjectEditReplace(QWidget):
self.editKey = QLineEdit()
self.editValue = QLineEdit()
- self.saveButton = QPushButton(self.theTheme.getIcon("done"),"")
- self.addButton = QPushButton(self.theTheme.getIcon("add"),"")
- self.delButton = QPushButton(self.theTheme.getIcon("remove"),"")
+ self.saveButton = QPushButton(self.theTheme.getIcon("done"), "")
+ self.addButton = QPushButton(self.theTheme.getIcon("add"), "")
+ self.delButton = QPushButton(self.theTheme.getIcon("remove"), "")
self.saveButton.setToolTip("Save entry")
self.addButton.setToolTip("Add new entry")
self.delButton.setToolTip("Delete selected entry")
@@ -536,11 +557,13 @@ class GuiProjectEditReplace(QWidget):
return
def getNewList(self):
+ """Extract the list from the widget.
+ """
newList = {}
for n in range(self.listBox.topLevelItemCount()):
tItem = self.listBox.topLevelItem(n)
- aKey = self._stripNotAllowed(tItem.text(0))
- aVal = tItem.text(1)
+ aKey = self._stripNotAllowed(tItem.text(0))
+ aVal = tItem.text(1)
if len(aKey) > 0:
newList[aKey] = aVal
return newList
@@ -550,6 +573,9 @@ class GuiProjectEditReplace(QWidget):
##
def _selectedItem(self):
+ """Extract the details from the selected item and populate the
+ edit form.
+ """
selItem = self._getSelectedItem()
if selItem is None:
return False
@@ -564,7 +590,8 @@ class GuiProjectEditReplace(QWidget):
return True
def _saveEntry(self):
-
+ """Save the form data into the list widget.
+ """
selItem = self._getSelectedItem()
if selItem is None:
return False
@@ -586,6 +613,8 @@ class GuiProjectEditReplace(QWidget):
return
def _addEntry(self):
+ """Add a new list entry.
+ """
saveKey = "" % (self.listBox.topLevelItemCount() + 1)
newVal = ""
newItem = QTreeWidgetItem([saveKey, newVal])
@@ -593,6 +622,8 @@ class GuiProjectEditReplace(QWidget):
return True
def _delEntry(self):
+ """Delete the selected entry.
+ """
selItem = self._getSelectedItem()
if selItem is None:
return False
@@ -601,12 +632,16 @@ class GuiProjectEditReplace(QWidget):
return True
def _getSelectedItem(self):
+ """Extract the currently selected item.
+ """
selItem = self.listBox.selectedItems()
if len(selItem) == 0:
return None
return selItem[0]
def _stripNotAllowed(self, theKey):
+ """Clean up the replace key string.
+ """
retKey = ""
for c in theKey:
if c.isalnum():
diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py
index 3baf5257..124e0a59 100644
--- a/nw/gui/projtree.py
+++ b/nw/gui/projtree.py
@@ -90,7 +90,7 @@ class GuiProjectTree(QTreeWidget):
# for some fonts like the Ubuntu font.
treeHeader = self.header()
treeHeader.setStretchLastSection(True)
- treeHeader.setMinimumSectionSize(iPx+6)
+ treeHeader.setMinimumSectionSize(iPx + 6)
# Allow Move by Drag & Drop
self.setDragEnabled(True)
@@ -237,6 +237,7 @@ class GuiProjectTree(QTreeWidget):
has focus. This also applies when the menu is used.
"""
if qApp.focusWidget() == self and self.theParent.hasProject:
+
tHandle = self.getSelectedHandle()
tItem = self._getTreeItem(tHandle)
pItem = tItem.parent()
@@ -248,6 +249,7 @@ class GuiProjectTree(QTreeWidget):
return False
cItem = self.takeTopLevelItem(tIndex)
self.insertTopLevelItem(nIndex, cItem)
+
else:
tIndex = pItem.indexOfChild(tItem)
nChild = pItem.childCount()
@@ -256,11 +258,14 @@ class GuiProjectTree(QTreeWidget):
return False
cItem = pItem.takeChild(tIndex)
pItem.insertChild(nIndex, cItem)
+
self.clearSelection()
cItem.setSelected(True)
self._setTreeChanged(True)
+
else:
return False
+
return True
def saveTreeOrder(self):
@@ -516,8 +521,10 @@ class GuiProjectTree(QTreeWidget):
for i in range(pItem.childCount()):
pCount += int(pItem.child(i).text(self.C_COUNT))
pHandle = pItem.data(self.C_NAME, Qt.UserRole)
+
if not nDepth > 200 and pHandle != "":
self.propagateCount(pHandle, pCount, nDepth+1)
+
return
def projectWordCount(self):
@@ -533,9 +540,11 @@ class GuiProjectTree(QTreeWidget):
if tItem == self.orphRoot:
continue
nWords += int(tItem.text(self.C_COUNT))
+
self.theProject.setProjectWordCount(nWords)
sWords = self.theProject.getSessionWordCount()
self.theParent.statusBar.setStats(nWords,sWords)
+
return
def buildTree(self):
@@ -547,9 +556,11 @@ class GuiProjectTree(QTreeWidget):
logger.debug("Building project tree ...")
self.clear()
iCount = 0
+
for nwItem in self.theProject.getProjectItems():
iCount += 1
self._addTreeItem(nwItem)
+
logger.debug("%d items added to project tree" % iCount)
return True
@@ -558,10 +569,13 @@ class GuiProjectTree(QTreeWidget):
selected, return the first.
"""
selItem = self.selectedItems()
+
if len(selItem) == 0:
return None
+
if isinstance(selItem[0], QTreeWidgetItem):
return selItem[0].data(self.C_NAME, Qt.UserRole)
+
return None
def getSelectedHandles(self):
@@ -572,6 +586,7 @@ class GuiProjectTree(QTreeWidget):
for n in range(len(selItems)):
if isinstance(selItems[n], QTreeWidgetItem):
selHandles.append(selItems[n].data(self.C_NAME, Qt.UserRole))
+
return selHandles
def setSelectedHandle(self, tHandle, doScroll=False):
@@ -580,12 +595,14 @@ class GuiProjectTree(QTreeWidget):
if tHandle in self.theMap:
self.clearSelection()
self.theMap[tHandle].setSelected(True)
+
selItems = self.selectedIndexes()
if selItems and doScroll:
self.scrollTo(
selItems[0], QAbstractItemView.PositionAtCenter
)
return True
+
return False
##
@@ -601,9 +618,11 @@ class GuiProjectTree(QTreeWidget):
tHandle = selItem.data(self.C_NAME, Qt.UserRole)
tItem = self.theProject.projTree[tHandle]
self.setSelectedHandle(tHandle) # Just to be safe
+
if self.ctxMenu.filterActions(tItem):
# Only open menu if any actions remain after filter
self.ctxMenu.exec_(self.viewport().mapToGlobal(clickPos))
+
return
##
@@ -615,9 +634,11 @@ class GuiProjectTree(QTreeWidget):
mouse in a blank area of the tree view.
"""
QTreeWidget.mousePressEvent(self, theEvent)
+
selItem = self.indexAt(theEvent.pos())
if not selItem.isValid():
self.clearSelection()
+
return
def dropEvent(self, theEvent):
@@ -766,6 +787,7 @@ class GuiProjectTree(QTreeWidget):
trashHandle = self.theProject.trashFolder()
if trashHandle is None:
return None
+
trItem = self._getTreeItem(trashHandle)
if trItem is None:
trItem = self._addTreeItem(
@@ -773,6 +795,7 @@ class GuiProjectTree(QTreeWidget):
)
trItem.setExpanded(True)
self._setTreeChanged(True)
+
return trItem
def _addOrphanedRoot(self):
@@ -790,6 +813,7 @@ class GuiProjectTree(QTreeWidget):
newItem.setExpanded(True)
newItem.setData(self.C_NAME, Qt.UserRole, "")
newItem.setIcon(self.C_NAME, self.theTheme.getIcon("proj_orphan"))
+
return
def _cleanOrphanedRoot(self):
@@ -834,10 +858,12 @@ class GuiProjectTree(QTreeWidget):
if trItemP is None:
logger.error("Failed to find new parent item of %s" % tHandle)
return
+
pHandle = trItemP.data(self.C_NAME, Qt.UserRole)
nwItemS.setParent(pHandle)
self.setTreeItemValues(tHandle)
self._setTreeChanged(True)
+
return
def _setTreeChanged(self, theState):
diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py
index 395fc5cc..330d7de9 100644
--- a/nw/gui/statusbar.py
+++ b/nw/gui/statusbar.py
@@ -264,7 +264,7 @@ class StatusLED(QAbstractButton):
qPaint.setPen(qPalette.dark().color())
qPaint.setBrush(self._theCol)
qPaint.setOpacity(1.0)
- qPaint.drawEllipse(1, 1, self.width()-2, self.height()-2)
+ qPaint.drawEllipse(1, 1, self.width() - 2, self.height() - 2)
return
# END Class StatusLED
diff --git a/nw/gui/theme.py b/nw/gui/theme.py
index 0265df62..134d91dd 100644
--- a/nw/gui/theme.py
+++ b/nw/gui/theme.py
@@ -77,11 +77,11 @@ class GuiTheme:
self.themeLicenseUrl = ""
## GUI
- self.treeWCount = [ 0, 0, 0]
- self.statNone = [120,120,120]
- self.statUnsaved = [120,120, 40]
- self.statSaved = [ 40,120, 0]
- self.helpText = [ 0, 0, 0]
+ self.treeWCount = [ 0, 0, 0]
+ self.statNone = [120, 120, 120]
+ self.statUnsaved = [120, 120, 40]
+ self.statSaved = [ 40, 120, 0]
+ self.helpText = [ 0, 0, 0]
# Loaded Syntax Settings
@@ -95,22 +95,22 @@ class GuiTheme:
self.syntaxLicenseUrl = ""
## Colours
- self.colBack = [255,255,255]
- self.colText = [ 0, 0, 0]
- self.colLink = [ 0, 0, 0]
- self.colHead = [ 0, 0, 0]
- self.colHeadH = [ 0, 0, 0]
- self.colEmph = [ 0, 0, 0]
- self.colDialN = [ 0, 0, 0]
- self.colDialD = [ 0, 0, 0]
- self.colDialS = [ 0, 0, 0]
- self.colComm = [ 0, 0, 0]
- self.colKey = [ 0, 0, 0]
- self.colVal = [ 0, 0, 0]
- self.colSpell = [ 0, 0, 0]
- self.colTagErr = [ 0, 0, 0]
- self.colRepTag = [ 0, 0, 0]
- self.colMod = [ 0, 0, 0]
+ self.colBack = [255, 255, 255]
+ self.colText = [ 0, 0, 0]
+ self.colLink = [ 0, 0, 0]
+ self.colHead = [ 0, 0, 0]
+ self.colHeadH = [ 0, 0, 0]
+ self.colEmph = [ 0, 0, 0]
+ self.colDialN = [ 0, 0, 0]
+ self.colDialD = [ 0, 0, 0]
+ self.colDialS = [ 0, 0, 0]
+ self.colComm = [ 0, 0, 0]
+ self.colKey = [ 0, 0, 0]
+ self.colVal = [ 0, 0, 0]
+ self.colSpell = [ 0, 0, 0]
+ self.colTagErr = [ 0, 0, 0]
+ self.colRepTag = [ 0, 0, 0]
+ self.colMod = [ 0, 0, 0]
# Changeable Settings
self.guiTheme = None
@@ -144,9 +144,9 @@ class GuiTheme:
qMetric = QFontMetrics(self.guiFont)
self.fontPointSize = self.guiFont.pointSizeF()
self.fontPixelSize = int(round(qMetric.height()))
- self.baseIconSize = int(round(qMetric.ascent()))
- self.textNHeight = qMetric.boundingRect("N").height()
- self.textNWidth = qMetric.boundingRect("N").width()
+ self.baseIconSize = int(round(qMetric.ascent()))
+ self.textNHeight = qMetric.boundingRect("N").height()
+ self.textNWidth= qMetric.boundingRect("N").width()
logger.verbose("GUI Font Family: %s" % self.guiFont.family())
logger.verbose("GUI Font Point Size: %.2f" % self.fontPointSize)
@@ -223,10 +223,10 @@ class GuiTheme:
self.guiTheme = self.mainConf.guiTheme
self.guiSyntax = self.mainConf.guiSyntax
self.themeRoot = self.mainConf.themeRoot
- self.themePath = path.join(self.mainConf.themeRoot,self.guiPath,self.guiTheme)
- self.syntaxFile = path.join(self.themeRoot,self.syntaxPath,self.guiSyntax+".conf")
- self.confFile = path.join(self.themePath,self.confName)
- self.cssFile = path.join(self.themePath,self.cssName)
+ self.themePath = path.join(self.mainConf.themeRoot, self.guiPath, self.guiTheme)
+ self.syntaxFile = path.join(self.themeRoot, self.syntaxPath, self.guiSyntax+".conf")
+ self.confFile = path.join(self.themePath, self.confName)
+ self.cssFile = path.join(self.themePath, self.cssName)
self.loadTheme()
self.loadSyntax()
@@ -256,7 +256,7 @@ class GuiTheme:
cssData = ""
try:
if path.isfile(self.cssFile):
- with open(self.cssFile,mode="r",encoding="utf8") as inFile:
+ with open(self.cssFile, mode="r", encoding="utf8") as inFile:
cssData = inFile.read()
except Exception as e:
logger.error("Could not load theme css file")
@@ -329,13 +329,13 @@ class GuiTheme:
## Main
cnfSec = "Main"
if confParser.has_section(cnfSec):
- self.syntaxName = self._parseLine( confParser, cnfSec, "name", "")
- self.syntaxDescription = self._parseLine( confParser, cnfSec, "description", "")
- self.syntaxAuthor = self._parseLine( confParser, cnfSec, "author", "")
- self.syntaxCredit = self._parseLine( confParser, cnfSec, "credit", "")
- self.syntaxUrl = self._parseLine( confParser, cnfSec, "url", "")
- self.syntaxLicense = self._parseLine( confParser, cnfSec, "license", "")
- self.syntaxLicenseUrl = self._parseLine( confParser, cnfSec, "licenseurl", "")
+ self.syntaxName = self._parseLine(confParser, cnfSec, "name", "")
+ self.syntaxDescription = self._parseLine(confParser, cnfSec, "description", "")
+ self.syntaxAuthor = self._parseLine(confParser, cnfSec, "author", "")
+ self.syntaxCredit = self._parseLine(confParser, cnfSec, "credit", "")
+ self.syntaxUrl = self._parseLine(confParser, cnfSec, "url", "")
+ self.syntaxLicense = self._parseLine(confParser, cnfSec, "license", "")
+ self.syntaxLicenseUrl = self._parseLine(confParser, cnfSec, "licenseurl", "")
## Syntax
cnfSec = "Syntax"
@@ -376,7 +376,7 @@ class GuiTheme:
confParser.read_file(inFile)
except Exception as e:
self.theParent.makeAlert(
- ["Could not load theme config file.",str(e)], nwAlert.ERROR
+ ["Could not load theme config file.", str(e)], nwAlert.ERROR
)
continue
themeName = ""
@@ -409,7 +409,7 @@ class GuiTheme:
confParser.read_file(inFile)
except Exception as e:
self.theParent.makeAlert(
- ["Could not load syntax file.",str(e)], nwAlert.ERROR
+ ["Could not load syntax file.", str(e)], nwAlert.ERROR
)
return []
syntaxName = ""
@@ -429,8 +429,10 @@ class GuiTheme:
##
def _loadColour(self, confParser, cnfSec, cnfName):
+ """Load a colour value from a config string.
+ """
if confParser.has_option(cnfSec,cnfName):
- inData = confParser.get(cnfSec,cnfName).split(",")
+ inData = confParser.get(cnfSec,cnfName).split(",")
outData = []
try:
outData.append(int(inData[0]))
@@ -438,16 +440,18 @@ class GuiTheme:
outData.append(int(inData[2]))
except:
logger.error("Could not load theme colours for '%s' from config file" % cnfName)
- outData = [0,0,0]
+ outData = [0, 0, 0]
else:
logger.warning("Could not find theme colours for '%s' in config file" % cnfName)
- outData = [0,0,0]
+ outData = [0, 0, 0]
return outData
def _setPalette(self, confParser, cnfSec, cnfName, paletteVal):
+ """Set a palette colour value from a config string.
+ """
readCol = []
if confParser.has_option(cnfSec,cnfName):
- inData = confParser.get(cnfSec,cnfName).split(",")
+ inData = confParser.get(cnfSec,cnfName).split(",")
try:
readCol.append(int(inData[0]))
readCol.append(int(inData[1]))
@@ -547,8 +551,8 @@ class GuiIcons:
"reference" : (None, None),
## Switches
- "sticky-on" : (None, None),
- "sticky-off" : (None, None),
+ "sticky-on" : (None, None),
+ "sticky-off" : (None, None),
}
DECO_MAP = {
@@ -615,13 +619,13 @@ class GuiIcons:
## Main
cnfSec = "Main"
if confParser.has_section(cnfSec):
- self.themeName = self._parseLine( confParser, cnfSec, "name", "")
- self.themeDescription = self._parseLine( confParser, cnfSec, "description", "")
- self.themeAuthor = self._parseLine( confParser, cnfSec, "author", "")
- self.themeCredit = self._parseLine( confParser, cnfSec, "credit", "")
- self.themeUrl = self._parseLine( confParser, cnfSec, "url", "")
- self.themeLicense = self._parseLine( confParser, cnfSec, "license", "")
- self.themeLicenseUrl = self._parseLine( confParser, cnfSec, "licenseurl", "")
+ self.themeName = self._parseLine(confParser, cnfSec, "name", "")
+ self.themeDescription = self._parseLine(confParser, cnfSec, "description", "")
+ self.themeAuthor = self._parseLine(confParser, cnfSec, "author", "")
+ self.themeCredit = self._parseLine(confParser, cnfSec, "credit", "")
+ self.themeUrl = self._parseLine(confParser, cnfSec, "url", "")
+ self.themeLicense = self._parseLine(confParser, cnfSec, "license", "")
+ self.themeLicenseUrl = self._parseLine(confParser, cnfSec, "licenseurl", "")
## Palette
cnfSec = "Map"
@@ -705,7 +709,7 @@ class GuiIcons:
confParser.read_file(inFile)
except Exception as e:
self.theParent.makeAlert(
- ["Could not load theme config file.",str(e)], nwAlert.ERROR
+ ["Could not load theme config file.", str(e)], nwAlert.ERROR
)
continue
themeName = ""
@@ -730,7 +734,6 @@ class GuiIcons:
an icon exists. Prefer svg files over png files. Always returns
a QIcon.
"""
-
if iconKey not in self.ICON_MAP:
logger.error("Requested unknown icon name '%s'" % iconKey)
return QIcon()
diff --git a/nw/gui/writingstats.py b/nw/gui/writingstats.py
index 6e4a4df8..955376a7 100644
--- a/nw/gui/writingstats.py
+++ b/nw/gui/writingstats.py
@@ -90,7 +90,7 @@ class GuiWritingStats(QDialog):
)
self.listBox = QTreeWidget()
- self.listBox.setHeaderLabels(["Session Start","Length","Words","Histogram"])
+ self.listBox.setHeaderLabels(["Session Start", "Length", "Words", "Histogram"])
self.listBox.setIndentation(0)
self.listBox.setColumnWidth(self.C_TIME, wCol0)
self.listBox.setColumnWidth(self.C_LENGTH, wCol1)
diff --git a/nw/guimain.py b/nw/guimain.py
index 46db9d1f..8c760531 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -128,7 +128,7 @@ class GuiMain(QMainWindow):
self.tabWidget = QTabWidget()
self.tabWidget.setTabPosition(QTabWidget.East)
self.tabWidget.setStyleSheet("QTabWidget::pane {border: 0;}")
- self.tabWidget.addTab(self.splitDocs, "Editor")
+ self.tabWidget.addTab(self.splitDocs, "Editor")
self.tabWidget.addTab(self.splitOutline, "Outline")
self.tabWidget.currentChanged.connect(self._mainTabChanged)
@@ -139,8 +139,6 @@ class GuiMain(QMainWindow):
self.splitMain.addWidget(self.tabWidget)
self.splitMain.setSizes(self.mainConf.getMainPanePos())
- self.setCentralWidget(self.splitMain)
-
self.idxTree = self.splitMain.indexOf(self.treePane)
self.idxMain = self.splitMain.indexOf(self.tabWidget)
self.idxEditor = self.splitDocs.indexOf(self.docEditor)
@@ -167,8 +165,8 @@ class GuiMain(QMainWindow):
# Set Main Window Elements
self.setMenuBar(self.mainMenu)
+ self.setCentralWidget(self.splitMain)
self.setStatusBar(self.statusBar)
- self.statusBar.setStatus("Ready")
# Finalise Initialisation
##########################
@@ -222,6 +220,7 @@ class GuiMain(QMainWindow):
self.manageProjects()
logger.debug("novelWriter is ready ...")
+ self.statusBar.setStatus("novelWriter is ready ...")
return
@@ -348,9 +347,7 @@ class GuiMain(QMainWindow):
return saveOK
def openProject(self, projFile):
- """Open a project. The parameter projFile is passed from the
- open recent projects menu, and must be set to be forwarded to
- the project class. Otherwise, we just return.
+ """Open a project from a projFile path.
"""
if projFile is None:
return False
@@ -365,43 +362,47 @@ class GuiMain(QMainWindow):
# Try to open the project
if not self.theProject.openProject(projFile):
- if self.theProject.lockedBy is not None:
- if self.mainConf.showGUI:
- try:
- lockDetails = (
- "
The project was locked by the computer "
- "'%s' (%s %s), last active on %s"
- ) % (
- self.theProject.lockedBy[0],
- self.theProject.lockedBy[1],
- self.theProject.lockedBy[2],
- datetime.fromtimestamp(
- int(self.theProject.lockedBy[3])
- ).strftime("%x %X")
- )
- except:
- lockDetails = ""
+ # The project open failed.
- msgBox = QMessageBox()
- msgRes = msgBox.warning(
- self, "Project Locked", (
- "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 "
- "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:
- if not self.theProject.openProject(projFile, overrideLock=True):
- return False
- else:
- return False
- else:
+ if self.theProject.lockedBy is None:
+ # The project is not locked, so failed for some other
+ # reason handled by the project class.
return False
+ if self.mainConf.showGUI:
+ try:
+ lockDetails = (
+ "
The project was locked by the computer "
+ "'%s' (%s %s), last active on %s"
+ ) % (
+ self.theProject.lockedBy[0],
+ self.theProject.lockedBy[1],
+ self.theProject.lockedBy[2],
+ datetime.fromtimestamp(
+ int(self.theProject.lockedBy[3])
+ ).strftime("%x %X")
+ )
+ except:
+ lockDetails = ""
+
+ msgBox = QMessageBox()
+ msgRes = msgBox.warning(
+ self, "Project Locked", (
+ "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 "
+ "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:
+ if not self.theProject.openProject(projFile, overrideLock=True):
+ return False
+ else:
+ return False
+
# Project is loaded
self.hasProject = True
@@ -570,7 +571,7 @@ class GuiMain(QMainWindow):
dlgOpt = QFileDialog.Options()
dlgOpt |= QFileDialog.DontUseNativeDialog
inPath = QFileDialog.getOpenFileName(
- self,"Import File",lastPath,options=dlgOpt,filter=";;".join(extFilter)
+ self, "Import File", lastPath, options=dlgOpt, filter=";;".join(extFilter)
)
if inPath:
loadFile = inPath[0]
@@ -582,7 +583,7 @@ class GuiMain(QMainWindow):
theText = None
try:
- with open(loadFile,mode="rt",encoding="utf8") as inFile:
+ with open(loadFile, mode="rt", encoding="utf8") as inFile:
theText = inFile.read()
self.mainConf.setLastPath(loadFile)
except Exception as e:
@@ -602,7 +603,7 @@ class GuiMain(QMainWindow):
if not self.docEditor.isEmpty():
if self.mainConf.showGUI:
msgBox = QMessageBox()
- msgRes = msgBox.question(self, "Import Document",(
+ msgRes = msgBox.question(self, "Import Document", (
"Importing the file will overwrite the current content of the document. "
"Do you want to proceed?"
))
@@ -917,6 +918,8 @@ class GuiMain(QMainWindow):
return True
def setFocus(self, paneNo):
+ """Switch focus to one of the three main gUi panes.
+ """
if paneNo == 1:
self.treeView.setFocus()
elif paneNo == 2:
@@ -926,6 +929,8 @@ class GuiMain(QMainWindow):
return
def closeDocEditor(self):
+ """Close the document edit panel. This does not hide the editor.
+ """
self.closeDocument()
self.theProject.setLastEdited(None)
return
@@ -1059,6 +1064,8 @@ class GuiMain(QMainWindow):
return True
def _setWindowTitle(self, projName=None):
+ """Set the window title and add the project's working title.
+ """
winTitle = self.mainConf.appName
if projName is not None:
winTitle += " - %s" % projName
@@ -1066,19 +1073,30 @@ class GuiMain(QMainWindow):
return True
def _autoSaveProject(self):
- if (self.hasProject and self.theProject.projChanged and
- self.theProject.projPath is not None):
+ """Triggered by the auto-save project timer to save the project.
+ """
+ doSave = self.hasProject
+ doSave &= self.theProject.projChanged
+ doSave &= self.theProject.projPath is not None
+
+ if doSave:
logger.debug("Autosaving project")
self.saveProject(autoSave=True)
+
return
def _autoSaveDocument(self):
+ """Triggered by the auto-save document timer to save the
+ document.
+ """
if self.hasProject and self.docEditor.docChanged:
logger.debug("Autosaving document")
self.saveDocument()
return
def _makeStatusIcons(self):
+ """Generate all the item status icons based on project settings.
+ """
self.statusIcons = {}
iPx = self.mainConf.pxInt(32)
for sLabel, sCol, _ in self.theProject.statusItems:
@@ -1088,6 +1106,9 @@ class GuiMain(QMainWindow):
return
def _makeImportIcons(self):
+ """Generate all the item importance icons based on project
+ settings.
+ """
self.importIcons = {}
iPx = self.mainConf.pxInt(32)
for sLabel, sCol, _ in self.theProject.importItems:
@@ -1101,6 +1122,9 @@ class GuiMain(QMainWindow):
##
def closeEvent(self, theEvent):
+ """Capture the closing event of the GUI and call the close
+ function to handle all the close process steps.
+ """
if self.closeMain():
theEvent.accept()
else:
From c38951cf541eaf99b065607063b81a9566a7f252 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 8 Aug 2020 22:09:59 +0200
Subject: [PATCH 20/22] Force timezone for docs
---
docs/source/conf.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/docs/source/conf.py b/docs/source/conf.py
index e9a330a7..1e5bd90a 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -15,7 +15,7 @@
# import os
# import sys
# sys.path.insert(0, os.path.abspath("."))
-import sphinx_rtd_theme
+import os, time, sphinx_rtd_theme
# -- Project information -----------------------------------------------------
@@ -31,6 +31,9 @@ release = "0.11.0"
# -- General configuration ---------------------------------------------------
+os.environ["TZ"] = "Europe/Oslo"
+time.tzset()
+
# needs_sphinx = "1.0"
extensions = [
"sphinx_rtd_theme",
From 1215995531438146519692c3b30c92a3f8d30e17 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sun, 9 Aug 2020 15:42:58 +0200
Subject: [PATCH 21/22] Bumped version to 0.11.1
---
docs/source/conf.py | 4 ++--
nw/__init__.py | 6 +++---
sample/nwProject.nwx | 8 ++++----
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 1e5bd90a..a57814b7 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -24,9 +24,9 @@ copyright = "2018-2020, Veronica Berglyd Olsen"
author = "Veronica Berglyd Olsen"
# The short X.Y version
-version = "0.11.0"
+version = "0.11.1"
# The full version, including alpha/beta/rc tags
-release = "0.11.0"
+release = "0.11.1"
# -- General configuration ---------------------------------------------------
diff --git a/nw/__init__.py b/nw/__init__.py
index ccc49e66..c3fbf7c0 100644
--- a/nw/__init__.py
+++ b/nw/__init__.py
@@ -41,9 +41,9 @@ __package__ = "nw"
__author__ = "Veronica Berglyd Olsen"
__copyright__ = "Copyright 2018–2020, Veronica Berglyd Olsen"
__license__ = "GPLv3"
-__version__ = "0.11.0"
-__hexversion__ = "0x001100f0"
-__date__ = "2020-08-08"
+__version__ = "0.11.1"
+__hexversion__ = "0x001101f0"
+__date__ = "2020-08-09"
__maintainer__ = "Veronica Berglyd Olsen"
__email__ = "code@vkbo.net"
__status__ = "Beta"
diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx
index 6877047e..5be0eab5 100644
--- a/sample/nwProject.nwx
+++ b/sample/nwProject.nwx
@@ -1,13 +1,13 @@
-
+
Sample Project
Sample Project
Jane Smith
Jay Doh
- 663
+ 675
122
- 32747
+ 32866
False
@@ -119,7 +119,7 @@
1811
318
8
- 1143
+ 42
-
Another Scene
From fff03b8986952a07a109191d39299d9a90739fbd Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sun, 9 Aug 2020 15:56:19 +0200
Subject: [PATCH 22/22] Updated changelog
---
CHANGELOG.md | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 115d4f00..563384ec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,26 @@
# novelWriter ChangeLog
+## Version 0.11.1 [2020-08-09]
+
+**Bugfixes**
+
+* The modality of the dialogs have been made more consistent and a few issues with conflicting settings resolved. Mostly the latter relates to some dialogs both having the `exec_()` call and the `show()` call. The former implies modal, the latter does not, and the latter usually took precedence. All dialogs are now modal with the exception of the Writing Statistics and Build Novel Project tools. PR #389.
+
+**User Interface**
+
+* The Help menu entries for the documentation have been improved a bit. If the local copy of the documentations is present (both files are checked now), and the Qt Assistant is installed, the "Documentation (Local)" entry is visible with `F1` as keyboard shortcut. The "Documentation (Online)" is always visible with `Shift+F1` keyboard shortcut. The `F1` key redirects to this too if the local copy isn't available. PR #386.
+* The Writing Statistics tool now has the ability to set a cap between 100 and 100 000 words on the word count histogram bars. This is useful if the user has added a large chunk of text, in which case the histogram bar is dominated by this one entry. Now, anything on and above the cap value will have a full bar, and all other entries scale from 0 to the cap value. PR #387.
+
+**Documentation**
+
+* The main index page of the documentation now has a build date on it. PR #390.
+
+**Other Changes**
+
+* The Travis CI build system has been altered to first check that the tests pass for Python 3.8, for then to move to the other supported Python versions. These are currently 3.6 and 3.7. Python 3.9 will be added when it is released in October. PR #388.
+* Some clean-up of the source code, mostly in terms of unused imports and missing docstrings. PR #391.
+
+
## Version 0.11 [2020-08-08]
Note: The source code has now switched to a default branch named `main` ahead of the changes planned by GitHub.