Merge pull request #256 from vkbo/gui_tweaks

Minor GUI Tweaks
This commit is contained in:
Veronica K. Berglyd Olsen
2020-05-28 23:11:27 +02:00
committed by GitHub
18 changed files with 142 additions and 89 deletions
+1
View File
@@ -6,6 +6,7 @@
* The back-references list now shows references to any tag in the open document, not just the first tag. Issue #227, PR #234.
* Clicking a tag now tries to scroll to the header where the tag is set. The index needed a couple of minor changes for this feature, so this will invalidate the old index for a project, and require a new to be built. This is done automatically. PR #234.
* Moved the Close button on the "Build Novel project" dialog to the area with the other buttons since we anyway increased the size of that area. PR #256.
**Project Structure**
+3 -1
View File
@@ -533,7 +533,9 @@ class Config:
# Write config file
try:
cnfParse.write(open(path.join(self.confPath,self.confFile),mode="w",encoding="utf8"))
cnfParse.write(
open(path.join(self.confPath, self.confFile), mode="w", encoding="utf8")
)
self.confChanged = False
except Exception as e:
logger.error("Could not save config file")
+8 -8
View File
@@ -113,7 +113,7 @@ class NWProject():
CUSTOM, and always have parent handle set to None.
"""
if not self.projTree.checkRootUnique(rootClass):
self.makeAlert("Duplicate root item detected!", nwAlert.ERROR)
self.makeAlert("Duplicate root item detected.", nwAlert.ERROR)
return None
newItem = NWItem(self)
newItem.setName(rootName)
@@ -995,7 +995,7 @@ class NWProject():
# Report status
if len(orphanFiles) > 0:
self.makeAlert(
"Found %d orphaned file(s) in project folder!" % len(orphanFiles),
"Found %d orphaned file(s) in project folder." % len(orphanFiles),
nwAlert.WARN
)
else:
@@ -1097,10 +1097,10 @@ class NWProject():
# END Class NWProject
# ================================================================================================ #
# =============================================================================================== #
# NWTree
# Class holding the project tree for the NWProject
# ================================================================================================ #
# =============================================================================================== #
class NWTree():
@@ -1435,10 +1435,10 @@ class NWTree():
# END Class NWTree
# ================================================================================================ #
# =============================================================================================== #
# NWItem
# Class holding the project items making up the NWProject
# ================================================================================================ #
# =============================================================================================== #
class NWItem():
@@ -1680,10 +1680,10 @@ class NWItem():
# END Class NWItem
# ================================================================================================ #
# =============================================================================================== #
# NWStatus
# Class holding the item status values stored in the NWProject
# ================================================================================================ #
# =============================================================================================== #
class NWStatus():
+6 -6
View File
@@ -35,9 +35,9 @@ from nw.constants import isoLanguage
logger = logging.getLogger(__name__)
# ================================================================================================ #
# =============================================================================================== #
# SpellChecking SuperClass
# ================================================================================================ #
# =============================================================================================== #
class NWSpellCheck():
@@ -129,9 +129,9 @@ class NWSpellCheck():
# END Class NWSpellCheck
# ================================================================================================ #
# =============================================================================================== #
# Enchant Based SpellChecking
# ================================================================================================ #
# =============================================================================================== #
class NWSpellEnchant(NWSpellCheck):
@@ -211,9 +211,9 @@ class NWSpellEnchantDummy:
# END Class NWSpellEnchantDummy
# ================================================================================================ #
# =============================================================================================== #
# Fallback SpellChecking Using difflib
# ================================================================================================ #
# =============================================================================================== #
class NWSpellSimple(NWSpellCheck):
"""Internal spell check tool that uses standard Python packages with
+2 -2
View File
@@ -9,7 +9,7 @@ from nw.gui.theme import GuiTheme
# Dialogs
from nw.gui.dialogs.about import GuiAbout
from nw.gui.dialogs.configeditor import GuiConfigEditor
from nw.gui.dialogs.preferences import GuiPreferences
from nw.gui.dialogs.docmerge import GuiDocMerge
from nw.gui.dialogs.docsplit import GuiDocSplit
from nw.gui.dialogs.itemeditor import GuiItemEditor
@@ -40,7 +40,7 @@ __all__ = [
"GuiMainStatus",
"GuiTheme",
"GuiAbout",
"GuiConfigEditor",
"GuiPreferences",
"GuiDocMerge",
"GuiDocSplit",
"GuiItemEditor",
+33 -24
View File
@@ -34,12 +34,12 @@ from time import time
from PyQt5.QtCore import Qt, QByteArray
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
from PyQt5.QtGui import (
QTextOption, QPalette, QColor, QTextDocumentWriter, QFont
QPalette, QColor, QTextDocumentWriter, QFont
)
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QTextBrowser, QPushButton, QLabel,
QLineEdit, QGroupBox, QGridLayout, QProgressBar, QMenu, QAction,
QFileDialog, QFontComboBox, QSpinBox, QDialogButtonBox
QFileDialog, QFontComboBox, QSpinBox
)
from nw.gui.additions import QSwitch
@@ -83,8 +83,7 @@ class GuiBuildNovel(QDialog):
self.optState.getInt("GuiBuildNovel", "winHeight", 800)
)
self.outerBox = QVBoxLayout()
self.innerBox = QHBoxLayout()
self.outerBox = QHBoxLayout()
self.toolsBox = QVBoxLayout()
self.docView = GuiBuildNovelDocView(self, self.theProject)
@@ -173,7 +172,9 @@ class GuiBuildNovel(QDialog):
self.textFont = QFontComboBox()
self.textFont.setFixedWidth(220)
self.textFont.setToolTip("The font is used for PDF and printing. Other formats have no font set.")
self.textFont.setToolTip(
"The font is used for PDF and printing. Other formats have no font set."
)
self.textFont.setCurrentFont(
QFont(self.optState.getString("GuiBuildNovel", "textFont", self.mainConf.textFont))
)
@@ -183,13 +184,17 @@ class GuiBuildNovel(QDialog):
self.textSize.setMinimum(5)
self.textSize.setMaximum(48)
self.textSize.setSingleStep(1)
self.textSize.setToolTip("The size is used for PDF and printing. Other formats have no size set.")
self.textSize.setToolTip(
"The size is used for PDF and printing. Other formats have no size set."
)
self.textSize.setValue(
self.optState.getInt("GuiBuildNovel", "textSize", self.mainConf.textSize)
)
self.justifyText = QSwitch()
self.justifyText.setToolTip("Applies to PDF, printing, HTML, and Open Document exports.")
self.justifyText.setToolTip(
"Applies to PDF, printing, HTML, and Open Document exports."
)
self.justifyText.setChecked(
self.optState.getBool("GuiBuildNovel", "justifyText", False)
)
@@ -211,15 +216,21 @@ class GuiBuildNovel(QDialog):
self.includeGroup.setLayout(self.includeForm)
self.includeSynopsis = QSwitch()
self.includeSynopsis.setToolTip("Include synopsis type comments in the output.")
self.includeSynopsis.setToolTip(
"Include synopsis type comments in the output."
)
self.includeSynopsis.setChecked(self.theProject.titleFormat["withSynopsis"])
self.includeComments = QSwitch()
self.includeComments.setToolTip("Include plain comments in the output.")
self.includeComments.setToolTip(
"Include plain comments in the output."
)
self.includeComments.setChecked(self.theProject.titleFormat["withComments"])
self.includeKeywords = QSwitch()
self.includeKeywords.setToolTip("Include meta keywords (tags, references) in the output.")
self.includeKeywords.setToolTip(
"Include meta keywords (tags, references) in the output."
)
self.includeKeywords.setChecked(self.theProject.titleFormat["withKeywords"])
self.includeForm.addWidget(QLabel("Include synopsis"), 0, 0, 1, 1, Qt.AlignLeft)
@@ -292,7 +303,7 @@ class GuiBuildNovel(QDialog):
# Action Buttons
# ==============
self.buttonForm = QGridLayout()
self.buttonBox = QHBoxLayout()
self.btnPrint = QPushButton("Print")
self.btnPrint.clicked.connect(self._printDocument)
@@ -326,12 +337,13 @@ class GuiBuildNovel(QDialog):
self.saveTXT.triggered.connect(lambda: self._saveDocument(self.FMT_TXT))
self.saveMenu.addAction(self.saveTXT)
self.buttonForm.addWidget(self.btnSave, 0, 0)
self.buttonForm.addWidget(self.btnPrint, 0, 1)
self.btnClose = QPushButton("Close")
self.btnClose.clicked.connect(self._doClose)
# Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
self.buttonBox.rejected.connect(self._doClose)
self.buttonBox.addWidget(self.btnSave)
self.buttonBox.addWidget(self.btnPrint)
self.buttonBox.addWidget(self.btnClose)
self.buttonBox.setSpacing(4)
# Assemble GUI
# ============
@@ -343,18 +355,15 @@ class GuiBuildNovel(QDialog):
self.toolsBox.addWidget(self.buildProgress)
self.toolsBox.addWidget(self.buildNovel)
self.toolsBox.addSpacing(8)
self.toolsBox.addLayout(self.buttonForm)
self.toolsBox.addLayout(self.buttonBox)
self.innerBox.addLayout(self.toolsBox)
self.innerBox.addWidget(self.docView)
self.outerBox.addLayout(self.toolsBox)
self.outerBox.addWidget(self.docView)
self.outerBox.setStretch(0, 0)
self.outerBox.setStretch(1, 1)
self.outerBox.addLayout(self.innerBox)
self.outerBox.addWidget(self.buttonBox)
self.setLayout(self.outerBox)
self.innerBox.setStretch(0, 0)
self.innerBox.setStretch(1, 1)
self.show()
logger.debug("GuiBuildNovel initialisation complete")
+2 -2
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from nw.gui.dialogs.about import GuiAbout
from nw.gui.dialogs.configeditor import GuiConfigEditor
from nw.gui.dialogs.preferences import GuiPreferences
from nw.gui.dialogs.docmerge import GuiDocMerge
from nw.gui.dialogs.docsplit import GuiDocSplit
from nw.gui.dialogs.itemeditor import GuiItemEditor
@@ -11,7 +11,7 @@ from nw.gui.dialogs.sessionlog import GuiSessionLogView
__all__ = [
"GuiAbout",
"GuiConfigEditor",
"GuiPreferences",
"GuiDocMerge",
"GuiDocSplit",
"GuiItemEditor",
+13 -10
View File
@@ -119,16 +119,19 @@ class GuiAbout(QDialog):
"<h2>About {name:s}</h2>"
"<p>{copyright:s}.</p>"
"<p>Website: <a href='{website:s}'>{domain:s}</a></p>"
"<p>{name:s} is a markdown-like text editor designed for organising and writing "
"novels. It is written in Python 3 with a Qt5 GUI, using PyQt5.</p>"
"<p>{name:s} 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.</p>"
"<p>{name:s} 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.</p>"
"<p>See the License tab for the full text, or visit the GNU website at "
"<a href='https://www.gnu.org/licenses/gpl-3.0.html'>GPL v3.0</a> for more details.</p>"
"<p>{name:s} is a markdown-like text editor designed for "
"organising and writing novels. It is written in Python 3 with a "
"Qt5 GUI, using PyQt5.</p>"
"<p>{name:s} 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.</p>"
"<p>{name:s} 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.</p>"
"<p>See the License tab for the full text, or visit the GNU website "
"at <a href='https://www.gnu.org/licenses/gpl-3.0.html'>GPL v3.0</a> "
"for more details.</p>"
"<h3>Credits</h3>"
"<p>{credits:s}</p>"
).format(
+9 -3
View File
@@ -150,7 +150,9 @@ class GuiDocSplit(QDialog):
), nwAlert.ERROR)
return
fHandle = self.theProject.newFolder(srcItem.itemName, srcItem.itemClass, srcItem.parHandle)
fHandle = self.theProject.newFolder(
srcItem.itemName, srcItem.itemClass, srcItem.parHandle
)
self.theParent.treeView.revealTreeItem(fHandle)
logger.verbose("Creating folder %s" % fHandle)
@@ -174,7 +176,9 @@ class GuiDocSplit(QDialog):
newItem = self.theProject.projTree[nHandle]
newItem.setLayout(itemLayout)
logger.verbose(
"Creating new document %s with text from line %d to %d" % (nHandle, iStart, iEnd-1)
"Creating new document %s with text from line %d to %d" % (
nHandle, iStart, iEnd-1
)
)
theText = "\n".join(theLines[iStart:iEnd])
@@ -227,7 +231,9 @@ class GuiDocSplit(QDialog):
spLevel = self.splitLevel.currentData()
self.optState.setValue("GuiDocSplit", "spLevel", spLevel)
logger.debug("Scanning document %s for headings level <= %d" % (self.sourceItem, spLevel))
logger.debug(
"Scanning document %s for headings level <= %d" % (self.sourceItem, spLevel)
)
lineNo = 0
for aLine in theText.splitlines():
@@ -44,12 +44,12 @@ from nw.constants import nwAlert, nwQuotes
logger = logging.getLogger(__name__)
class GuiConfigEditor(PagedDialog):
class GuiPreferences(PagedDialog):
def __init__(self, theParent, theProject):
PagedDialog.__init__(self, theParent)
logger.debug("Initialising ConfigEditor ...")
logger.debug("Initialising GuiPreferences ...")
self.mainConf = nw.CONFIG
self.theParent = theParent
@@ -74,7 +74,7 @@ class GuiConfigEditor(PagedDialog):
self.show()
logger.debug("ConfigEditor initialisation complete")
logger.debug("GuiPreferences initialisation complete")
return
@@ -122,7 +122,7 @@ class GuiConfigEditor(PagedDialog):
self.close()
return
# END Class GuiConfigEditor
# END Class GuiPreferences
class GuiConfigEditGeneralTab(QWidget):
@@ -228,7 +228,7 @@ class GuiConfigEditGeneralTab(QWidget):
## Backup Path
self.backupPath = self.mainConf.backupPath
self.backupGetPath = QPushButton(self.theTheme.getIcon("folder-open"),"Select Folder")
self.backupGetPath = QPushButton("Browse")
self.backupGetPath.clicked.connect(self._backupFolder)
self.backupPathRow = self.mainForm.addRow(
"Backup storage location",
+3 -1
View File
@@ -362,7 +362,9 @@ class GuiProjectEditStatus(QWidget):
self.listBox.takeItem(iRow)
self.colChanged = True
else:
self.theParent.makeAlert("Cannot delete status item that is in use.",nwAlert.ERROR)
self.theParent.makeAlert(
"Cannot delete status item that is in use.", nwAlert.ERROR
)
return
def _saveItem(self):
+14 -6
View File
@@ -180,11 +180,15 @@ class GuiSessionLogView(QDialog):
inData = inLine.split()
if len(inData) != 8:
continue
dStart = datetime.strptime("%s %s" % (inData[1],inData[2]), nwConst.tStampFmt)
dEnd = datetime.strptime("%s %s" % (inData[4],inData[5]), nwConst.tStampFmt)
dStart = datetime.strptime(
"%s %s" % (inData[1],inData[2]), nwConst.tStampFmt
)
dEnd = datetime.strptime(
"%s %s" % (inData[4],inData[5]), nwConst.tStampFmt
)
nWords = int(inData[7])
tDiff = dEnd - dStart
sDiff = tDiff.total_seconds()
tDiff = dEnd - dStart
sDiff = tDiff.total_seconds()
self.timeTotal += sDiff
if abs(nWords) > 0:
@@ -196,7 +200,9 @@ class GuiSessionLogView(QDialog):
if hideNegative and nWords < 0:
continue
newItem = QTreeWidgetItem([str(dStart),self._formatTime(sDiff),str(nWords),""])
newItem = QTreeWidgetItem(
[str(dStart), self._formatTime(sDiff), str(nWords), ""]
)
newItem.setTextAlignment(1,Qt.AlignRight)
newItem.setTextAlignment(2,Qt.AlignRight)
@@ -208,7 +214,9 @@ class GuiSessionLogView(QDialog):
self.listBox.addTopLevelItem(newItem)
except Exception as e:
self.theParent.makeAlert(["Failed to read session log file.",str(e)], nwAlert.ERROR)
self.theParent.makeAlert(
["Failed to read session log file.",str(e)], nwAlert.ERROR
)
return False
self.labelFilter.setText(self._formatTime(self.timeFilter))
+9 -3
View File
@@ -479,7 +479,9 @@ class GuiDocEditor(QTextEdit):
self.hLight.rehighlight()
qApp.restoreOverrideCursor()
afTime = time()
logger.debug("Document re-highlighted in %.3f milliseconds" % (1000*(afTime-bfTime)))
logger.debug(
"Document re-highlighted in %.3f milliseconds" % (1000*(afTime-bfTime))
)
return True
@@ -716,7 +718,9 @@ class GuiDocEditor(QTextEdit):
"""
sinceActive = time()-self.lastEdit
if sinceActive > 5*self.wcInterval:
logger.debug("Stopping word count timer: no activity last %.1f seconds" % sinceActive)
logger.debug(
"Stopping word count timer: no activity last %.1f seconds" % sinceActive
)
self.wcTimer.stop()
elif self.wCounter.isRunning():
logger.verbose("Word counter thread is busy")
@@ -952,7 +956,9 @@ class GuiDocEditor(QTextEdit):
theText = newText
cOffset -= 0
else:
logger.error("Unknown or unsupported block format requested: %s" % str(docAction))
logger.error(
"Unknown or unsupported block format requested: %s" % str(docAction)
)
return
# Replace the block text
+2 -2
View File
@@ -396,7 +396,7 @@ class GuiDocTree(QTreeWidget):
trItemP.takeChild(tIndex)
del self.theProject.projTree[tHandle]
else:
self.makeAlert(["Cannot delete folder.","It is not empty."], nwAlert.ERROR)
self.makeAlert("Cannot delete folder. It is not empty.", nwAlert.ERROR)
return False
elif nwItemS.itemType == nwItemType.ROOT:
@@ -407,7 +407,7 @@ class GuiDocTree(QTreeWidget):
self.theParent.mainMenu.setAvailableRoot()
self.theProject.setProjectChanged(True)
else:
self.makeAlert(["Cannot delete root folder.","It is not empty."], nwAlert.ERROR)
self.makeAlert("Cannot delete root folder. It is not empty.", nwAlert.ERROR)
return False
return True
+6 -2
View File
@@ -249,7 +249,9 @@ class GuiIcons:
try:
confParser.read_file(open(themeConf, mode="r", encoding="utf8"))
except Exception as e:
self.theParent.makeAlert(["Could not load theme config file.",str(e)],nwAlert.ERROR)
self.theParent.makeAlert(
["Could not load theme config file.",str(e)], nwAlert.ERROR
)
continue
themeName = ""
if confParser.has_section("Main"):
@@ -295,7 +297,9 @@ class GuiIcons:
# Finally. we check if we have a fallback icon
if self.mainConf.guiDark:
fbackIcon = path.join(self.mainConf.iconPath, self.fbackName, "%s-dark.svg" % iconKey)
fbackIcon = path.join(
self.mainConf.iconPath, self.fbackName, "%s-dark.svg" % iconKey
)
if path.isfile(fbackIcon):
logger.verbose("Loading icon '%s' from fallback theme" % iconKey)
return QIcon(fbackIcon)
+5 -1
View File
@@ -137,13 +137,17 @@ class GuiMainStatus(QStatusBar):
self._updateTime()
return True
##
# Setters
##
def setRefTime(self, theTime):
"""Set the reference time for the status bar clock.
"""
self.refTime = theTime
return
def setStatus(self, theMessage, timeOut=10.0):
def setStatus(self, theMessage, timeOut=20.0):
"""Set the status bar message to display for 'timeOut' seconds.
"""
self.showMessage(theMessage, int(timeOut*1000))
+6 -2
View File
@@ -284,7 +284,9 @@ class GuiTheme:
try:
confParser.read_file(open(themeConf, mode="r", encoding="utf8"))
except Exception as e:
self.theParent.makeAlert(["Could not load theme config file.",str(e)],nwAlert.ERROR)
self.theParent.makeAlert(
["Could not load theme config file.",str(e)], nwAlert.ERROR
)
continue
themeName = ""
if confParser.has_section("Main"):
@@ -314,7 +316,9 @@ class GuiTheme:
try:
confParser.read_file(open(syntaxPath, mode="r", encoding="utf8"))
except Exception as e:
self.theParent.makeAlert(["Could not load syntax file.",str(e)],nwAlert.ERROR)
self.theParent.makeAlert(
["Could not load syntax file.",str(e)], nwAlert.ERROR
)
return []
syntaxName = ""
if confParser.has_section("Main"):
+15 -11
View File
@@ -42,7 +42,7 @@ from PyQt5.QtWidgets import (
from nw.gui import (
GuiMainMenu, GuiMainStatus, GuiTheme, GuiDocTree, GuiDocEditor,
GuiDocViewer, GuiDocDetails, GuiSearchBar, GuiNoticeBar, GuiDocViewDetails,
GuiConfigEditor, GuiProjectSettings, GuiItemEditor, GuiProjectOutline,
GuiPreferences, GuiProjectSettings, GuiItemEditor, GuiProjectOutline,
GuiSessionLogView, GuiDocMerge, GuiDocSplit, GuiProjectLoad, GuiBuildNovel
)
from nw.core import NWProject, NWDoc, NWIndex
@@ -553,7 +553,7 @@ class GuiMain(QMainWindow):
if self.docEditor.theHandle is None:
self.makeAlert(
["Please open a document to import the text file into."],
"Please open a document to import the text file into.",
nwAlert.ERROR
)
return False
@@ -668,6 +668,12 @@ class GuiMain(QMainWindow):
theDoc = NWDoc(self.theProject, self)
for nDone, tItem in enumerate(self.theProject.projTree):
if tItem is not None:
self.statusBar.setStatus("Indexing: '%s'" % tItem.itemName)
else:
self.statusBar.setStatus("Indexing: Unknown item")
if tItem is not None and tItem.itemType == nwItemType.FILE:
logger.verbose("Scanning: %s" % tItem.itemName)
theText = theDoc.openDocument(tItem.itemHandle, showStatus=False)
@@ -683,16 +689,14 @@ class GuiMain(QMainWindow):
self.treeView.propagateCount(tItem.itemHandle, wC)
self.treeView.projectWordCount()
self.statusBar.setStatus("Building index: %.2f%%" % (100.0*(nDone + 1)/nItems))
self.docEditor.reloadText()
qApp.restoreOverrideCursor()
tEnd = time()
self.statusBar.setStatus("Indexing completed in %.1f ms" % ((tEnd - tStart)*1000.0))
self.docEditor.reloadText()
qApp.restoreOverrideCursor()
if self.mainConf.showGUI:
self.makeAlert(
"Project index rebuilt in %.3f seconds." % (tEnd - tStart), nwAlert.INFO
)
self.makeAlert("The project index has been successfully rebuilt.", nwAlert.INFO)
return True
@@ -737,7 +741,7 @@ class GuiMain(QMainWindow):
def editConfigDialog(self):
"""Open the preferences dialog.
"""
dlgConf = GuiConfigEditor(self, self.theProject)
dlgConf = GuiPreferences(self, self.theProject)
if dlgConf.exec_() == QDialog.Accepted:
logger.debug("Applying new preferences")
self.initMain()
@@ -778,7 +782,7 @@ class GuiMain(QMainWindow):
0 = info, 1 = warning, and 2 = error.
"""
if isinstance(theMessage, list):
popMsg = " ".join(theMessage)
popMsg = "<br>".join(theMessage)
logMsg = theMessage
else:
popMsg = theMessage