Merge branch 'master' into dev_0.6

This commit is contained in:
Veronica K. B. Olsen
2020-05-17 00:23:45 +02:00
14 changed files with 377 additions and 129 deletions
+4 -1
View File
@@ -64,7 +64,10 @@ There are no launcher icons yet.
Consult your operating system documentation for how to make those. Consult your operating system documentation for how to make those.
These will be added at some point, and I would appreciate any assistance from people working on Windows and MacOS as I don't use either of those operating systems. These will be added at some point, and I would appreciate any assistance from people working on Windows and MacOS as I don't use either of those operating systems.
## Dependencies ## Package Dependencies
It is recommended that novelWriter runs with Qt 5.9 or later, and Python 3.6 or later.
Running with Qt as low as 5.2.1 and Python 3.4.3 has been tested, and worked in the past, but there are no guarantees that this will keep working as these are not a part of the test builds.
For the apt package manager on Debian systems, the following Python3 packages are needed: For the apt package manager on Debian systems, the following Python3 packages are needed:
-16
View File
@@ -3,10 +3,6 @@
import sys import sys
if sys.hexversion < 0x030600F0:
print("ERROR: At least Python 3.6 is required")
sys.exit(1)
try: try:
import PyQt5.QtWidgets import PyQt5.QtWidgets
import PyQt5.QtGui import PyQt5.QtGui
@@ -15,18 +11,6 @@ except:
print("ERROR: Failed to load dependency python3-pyqt5") print("ERROR: Failed to load dependency python3-pyqt5")
sys.exit(1) sys.exit(1)
try:
import PyQt5.QtSvg
except:
print("ERROR: Failed to load dependency python3-pyqt5.qtsvg")
sys.exit(1)
try:
import lxml
except:
print("ERROR: Failed to load dependency python3-lxml")
sys.exit(1)
if __name__ == "__main__": if __name__ == "__main__":
import nw import nw
nw.main(sys.argv[1:]) nw.main(sys.argv[1:])
+42 -3
View File
@@ -32,9 +32,8 @@ import logging
from os import path, remove, rename from os import path, remove, rename
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication, QErrorMessage
from nw.guimain import GuiMain
from nw.config import Config from nw.config import Config
__package__ = "novelWriter" __package__ = "novelWriter"
@@ -67,7 +66,7 @@ __credits__ = [
# VERBOSE Use for outputting values and program flow details # VERBOSE Use for outputting values and program flow details
# #
# Adding verbose logging levels # Add verbose logging level
VERBOSE = 5 VERBOSE = 5
logging.addLevelName(VERBOSE, "VERBOSE") logging.addLevelName(VERBOSE, "VERBOSE")
def logVerbose(self, message, *args, **kws): def logVerbose(self, message, *args, **kws):
@@ -217,8 +216,48 @@ def main(sysArgs=None):
logger.setLevel(debugLevel) logger.setLevel(debugLevel)
# Check Packages and Versions
errorData = []
if sys.hexversion < 0x030403F0:
errorData.append(
"At least Python 3.4.3 is required, but 3.6 is highly recommended."
)
if CONFIG.verQtValue < 50200:
errorData.append(
"At least Qt5 version 5.2 is required, found %s." % CONFIG.verQtString
)
if CONFIG.verPyQtValue < 50200:
errorData.append(
"At least PyQt5 version 5.2 is required, found %s." % CONFIG.verPyQtString
)
try:
import PyQt5.QtSvg
except:
errorData.append("Python module 'PyQt5.QtSvg' is missing.")
try:
import lxml
except:
errorData.append("Python module 'lxml' is missing.")
if errorData:
errApp = QApplication([])
errMsg = QErrorMessage()
errMsg.setMinimumWidth(500)
errMsg.setMinimumHeight(300)
errMsg.showMessage((
"ERROR: %s cannot start due to the following issues:<br><br>"
"&nbsp;-&nbsp;%s<br><br>Exiting."
) % (
__package__, "<br>&nbsp;-&nbsp;".join(errorData)
))
errApp.exec_()
sys.exit(1)
# Finish initialising config
CONFIG.initConfig(confPath, dataPath) CONFIG.initConfig(confPath, dataPath)
# Import GUI (after dependency checks), and launch
from nw.guimain import GuiMain
if testMode: if testMode:
nwGUI = GuiMain() nwGUI = GuiMain()
return nwGUI return nwGUI
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="1.0rc1 (09960d6f05, 2020-04-09)"
sodipodi:docname="status_stats-dark.svg"
id="svg1983"
viewBox="0 0 24 24"
height="24"
width="24"
version="1.2">
<metadata
id="metadata1989">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1987" />
<sodipodi:namedview
inkscape:current-layer="svg1983"
inkscape:window-maximized="1"
inkscape:window-y="0"
inkscape:window-x="0"
inkscape:cy="10.95082"
inkscape:cx="12"
inkscape:zoom="38.125"
showgrid="false"
id="namedview1985"
inkscape:window-height="1344"
inkscape:window-width="2560"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
bordercolor="#666666"
pagecolor="#ffffff" />
<path
style="stroke-width:1.26319;fill:#aeaeae;fill-opacity:1"
inkscape:connector-curvature="0"
id="path1981"
d="m 14.526375,2.5260936 c 0,-1.3958223 -1.131816,-2.5263751 -2.526375,-2.5263751 -1.394559,0 -2.526375,1.1305528 -2.526375,2.5263751 V 17.684345 h 5.05275 z m 6.315937,5.0527501 c 0,-1.3958223 -1.131815,-2.5263751 -2.526374,-2.5263751 -1.39456,0 -2.526375,1.1305528 -2.526375,2.5263751 V 17.684345 h 5.052749 z M 8.210437,11.368407 c 0,-1.3958225 -1.1318154,-2.5263765 -2.5263744,-2.5263765 -1.394559,0 -2.5263751,1.130554 -2.5263751,2.5263765 v 6.315938 H 8.210437 Z M 20.842312,21.473906 H 3.1576875 c -0.6985427,0 -1.2631875,0.564645 -1.2631875,1.263187 0,0.698543 0.5646448,1.263188 1.2631875,1.263188 H 20.842312 c 0.698544,0 1.263188,-0.564645 1.263188,-1.263188 0,-0.698542 -0.564644,-1.263187 -1.263188,-1.263187 z" />
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+56
View File
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="1.0rc1 (09960d6f05, 2020-04-09)"
sodipodi:docname="status_stats.svg"
id="svg1983"
viewBox="0 0 24 24"
height="24"
width="24"
version="1.2">
<metadata
id="metadata1989">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1987" />
<sodipodi:namedview
inkscape:current-layer="svg1983"
inkscape:window-maximized="1"
inkscape:window-y="0"
inkscape:window-x="0"
inkscape:cy="10.95082"
inkscape:cx="12"
inkscape:zoom="38.125"
showgrid="false"
id="namedview1985"
inkscape:window-height="1344"
inkscape:window-width="2560"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
bordercolor="#666666"
pagecolor="#ffffff" />
<path
style="stroke-width:1.26319;fill:#000000;fill-opacity:0.72000003"
inkscape:connector-curvature="0"
id="path1981"
d="m 14.526375,2.5260936 c 0,-1.3958223 -1.131816,-2.5263751 -2.526375,-2.5263751 -1.394559,0 -2.526375,1.1305528 -2.526375,2.5263751 V 17.684345 h 5.05275 z m 6.315937,5.0527501 c 0,-1.3958223 -1.131815,-2.5263751 -2.526374,-2.5263751 -1.39456,0 -2.526375,1.1305528 -2.526375,2.5263751 V 17.684345 h 5.052749 z M 8.210437,11.368407 c 0,-1.3958225 -1.1318154,-2.5263765 -2.5263744,-2.5263765 -1.394559,0 -2.5263751,1.130554 -2.5263751,2.5263765 v 6.315938 H 8.210437 Z M 20.842312,21.473906 H 3.1576875 c -0.6985427,0 -1.2631875,0.564645 -1.2631875,1.263187 0,0.698543 0.5646448,1.263188 1.2631875,1.263188 H 20.842312 c 0.698544,0 1.263188,-0.564645 1.263188,-1.263188 0,-0.698542 -0.564644,-1.263187 -1.263188,-1.263187 z" />
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+7 -2
View File
@@ -36,6 +36,7 @@ from time import time
from PyQt5.Qt import PYQT_VERSION_STR from PyQt5.Qt import PYQT_VERSION_STR
from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo from PyQt5.QtCore import QT_VERSION_STR, QStandardPaths, QSysInfo
from PyQt5.QtWidgets import QErrorMessage
from nw.constants import nwFiles, nwUnicode from nw.constants import nwFiles, nwUnicode
from nw.common import splitVersionNumber, formatTimeStamp from nw.common import splitVersionNumber, formatTimeStamp
@@ -176,8 +177,12 @@ class Config:
self.osUnknown = True self.osUnknown = True
# Other System Info # Other System Info
self.hostName = QSysInfo.machineHostName() if self.verQtValue >= 50600:
self.kernelVer = QSysInfo.kernelVersion() self.hostName = QSysInfo.machineHostName()
self.kernelVer = QSysInfo.kernelVersion()
else:
self.hostName = "Unknown"
self.kernelVer = "Unknown"
# Packages # Packages
self.hasEnchant = False self.hasEnchant = False
+2
View File
@@ -73,6 +73,8 @@ class NWDoc():
"""Open a document from handle, capturing potential file system """Open a document from handle, capturing potential file system
errors and parse meta data. errors and parse meta data.
""" """
# Always clear first, since the object will often be reused.
self.clearDocument()
self.docHandle = tHandle self.docHandle = tHandle
if not isOrphan: if not isOrphan:
-1
View File
@@ -975,7 +975,6 @@ class NWProject():
oName = "" oName = ""
if aDoc.openDocument(oHandle, showStatus=False, isOrphan=True): if aDoc.openDocument(oHandle, showStatus=False, isOrphan=True):
oName, oPath = aDoc.getMeta() oName, oPath = aDoc.getMeta()
aDoc.clearDocument()
if oName == "": if oName == "":
nOrph += 1 nOrph += 1
+1 -1
View File
@@ -296,7 +296,7 @@ class GuiProjectEditStatus(QWidget):
newItem.setIcon(QIcon(newIcon)) newItem.setIcon(QIcon(newIcon))
newItem.setData(Qt.UserRole, len(self.colData)) newItem.setData(Qt.UserRole, len(self.colData))
self.listBox.addItem(newItem) self.listBox.addItem(newItem)
self.colData.append((iName,*iCol,oName)) self.colData.append((iName,iCol[0],iCol[1],iCol[2],oName))
self.colCounts.append(nUse) self.colCounts.append(nUse)
return newItem return newItem
+15 -10
View File
@@ -216,18 +216,23 @@ class GuiDocEditor(QTextEdit):
# font changed, otherwise we just clear the editor entirely, # font changed, otherwise we just clear the editor entirely,
# which makes it read only. # which makes it read only.
if self.theHandle is not None: if self.theHandle is not None:
# We must save the current handle as clearEditor() sets it self.reloadText()
# to None
tHandle = self.theHandle
self.clearEditor()
self.loadText(tHandle)
self.updateDocMargins()
else: else:
self.clearEditor() self.clearEditor()
return True return True
def loadText(self, tHandle, tLine=None): def reloadText(self):
"""Reloads the document currently being edited.
"""
if self.theHandle is not None:
tHandle = self.theHandle
self.clearEditor()
self.loadText(tHandle, showStatus=False)
self.updateDocMargins()
return
def loadText(self, tHandle, tLine=None, showStatus=True):
"""Load text from a document into the editor. If we have an io """Load text from a document into the editor. If we have an io
error, we must handle this and clear the editor so that we don't error, we must handle this and clear the editor so that we don't
risk overwriting the file if it exists. This can for instance risk overwriting the file if it exists. This can for instance
@@ -237,7 +242,7 @@ class GuiDocEditor(QTextEdit):
the file. the file.
""" """
theDoc = self.nwDocument.openDocument(tHandle) theDoc = self.nwDocument.openDocument(tHandle, showStatus=showStatus)
if theDoc is None: if theDoc is None:
# There was an io error # There was an io error
self.clearEditor() self.clearEditor()
@@ -257,7 +262,6 @@ class GuiDocEditor(QTextEdit):
self.setPlainText(theDoc) self.setPlainText(theDoc)
afTime = time() afTime = time()
logger.debug("Document highlighted in %.3f milliseconds" % (1000*(afTime-bfTime))) logger.debug("Document highlighted in %.3f milliseconds" % (1000*(afTime-bfTime)))
self.updateDocMargins()
if tLine is None: if tLine is None:
self.setCursorPosition(self.nwDocument.theItem.cursorPos) self.setCursorPosition(self.nwDocument.theItem.cursorPos)
@@ -275,6 +279,7 @@ class GuiDocEditor(QTextEdit):
self.theParent.noticeBar.showNote("This document is read only.") self.theParent.noticeBar.showNote("This document is read only.")
self.docTitle.setTitleFromHandle(self.theHandle) self.docTitle.setTitleFromHandle(self.theHandle)
self.updateDocMargins()
self.hLight.spellCheck = spTemp self.hLight.spellCheck = spTemp
qApp.restoreOverrideCursor() qApp.restoreOverrideCursor()
@@ -354,7 +359,7 @@ class GuiDocEditor(QTextEdit):
# The line below causes issues with large documents as it # The line below causes issues with large documents as it
# triggers an early repaint that seems to only render a part of # triggers an early repaint that seems to only render a part of
# the document. Leaving it here as a warning for now. # the document. Leaving it here as a warning for now.
# self.qDocument.contentsChange.emit(0,0,0) # self.qDocument.contentsChange.emit(0, 0, 0)
return return
+1
View File
@@ -60,6 +60,7 @@ class GuiIcons:
"proj_folder" : (QStyle.SP_DirIcon, "folder"), "proj_folder" : (QStyle.SP_DirIcon, "folder"),
"status_lang" : (None, None), "status_lang" : (None, None),
"status_time" : (None, None), "status_time" : (None, None),
"status_stats" : (None, None),
## Button Icons ## Button Icons
"folder-open" : (QStyle.SP_DirOpenIcon, "folder-open"), "folder-open" : (QStyle.SP_DirOpenIcon, "folder-open"),
"delete" : (QStyle.SP_DialogDiscardButton, "edit-delete"), "delete" : (QStyle.SP_DialogDiscardButton, "edit-delete"),
+169 -67
View File
@@ -31,10 +31,11 @@ import nw
from time import time from time import time
from PyQt5.QtCore import Qt, QTimer from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QColor, QPixmap, QFont from PyQt5.QtGui import QColor, QPixmap, QFont, QPainter
from PyQt5.QtWidgets import QStatusBar, QLabel from PyQt5.QtWidgets import qApp, QStatusBar, QLabel, QAbstractButton
from nw.core import NWSpellCheck from nw.core import NWSpellCheck
from nw.common import formatInt
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -49,59 +50,71 @@ class GuiMainStatus(QStatusBar):
self.theParent = theParent self.theParent = theParent
self.refTime = None self.refTime = None
self.iconGrey = QPixmap(16,16) self.charCount = 0
self.iconYellow = QPixmap(16,16) self.wordCount = 0
self.iconGreen = QPixmap(16,16) self.paraCount = 0
self.projWords = 0
self.sessWords = 0
self.monoFont = QFont("Monospace",10) self.monoFont = QFont("Monospace",10)
self.iconGrey.fill(QColor(*self.theParent.theTheme.statNone)) colNone = QColor(*self.theParent.theTheme.statNone)
self.iconYellow.fill(QColor(*self.theParent.theTheme.statUnsaved)) colTrue = QColor(*self.theParent.theTheme.statUnsaved)
self.iconGreen.fill(QColor(*self.theParent.theTheme.statSaved)) colFalse = QColor(*self.theParent.theTheme.statSaved)
self.boxStats = QLabel() # Permanent Widgets
self.boxStats.setToolTip("Project Word Count | Session Word Count") # =================
self.timeBox = QLabel("") ## The Spell Checker Language
self.timeBox.setToolTip("Session Time")
self.timeBox.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.timeBox.setFont(self.monoFont)
self.timeIcon = QLabel()
self.timeIcon.setPixmap(self.theParent.theTheme.getPixmap("status_time",(14,14)))
self.boxCounts = QLabel()
self.boxCounts.setToolTip("Document Character | Word | Paragraph Count")
self.projChanged = QLabel("")
self.projChanged.setFixedHeight(14)
self.projChanged.setFixedWidth(14)
self.projChanged.setToolTip("Project Changes Saved")
self.docChanged = QLabel("")
self.docChanged.setFixedHeight(14)
self.docChanged.setFixedWidth(14)
self.docChanged.setToolTip("Document Changes Saved")
self.langBox = QLabel("None")
self.langIcon = QLabel("") self.langIcon = QLabel("")
self.langText = QLabel("None")
self.langIcon.setPixmap(self.theParent.theTheme.getPixmap("status_lang",(14,14))) self.langIcon.setPixmap(self.theParent.theTheme.getPixmap("status_lang",(14,14)))
self.langIcon.setContentsMargins(0, 0, 0, 0)
# Add Them self.langText.setContentsMargins(0, 0, 8, 0)
self.addPermanentWidget(self.langIcon) self.addPermanentWidget(self.langIcon)
self.addPermanentWidget(self.langBox) self.addPermanentWidget(self.langText)
self.addPermanentWidget(QLabel(" "))
self.addPermanentWidget(self.docChanged)
self.addPermanentWidget(self.boxCounts)
self.addPermanentWidget(QLabel(" "))
self.addPermanentWidget(self.projChanged)
self.addPermanentWidget(self.boxStats)
self.addPermanentWidget(QLabel(" "))
self.addPermanentWidget(self.timeIcon)
self.addPermanentWidget(self.timeBox)
## The Editor Status
self.docIcon = StatusLED(colNone, colTrue, colFalse, 14, 14, self)
self.docText = QLabel("Editor")
self.docIcon.setContentsMargins(0, 0, 0, 0)
self.docText.setContentsMargins(0, 0, 8, 0)
self.addPermanentWidget(self.docIcon)
self.addPermanentWidget(self.docText)
## The Project Status
self.projIcon = StatusLED(colNone, colTrue, colFalse, 14, 14, self)
self.projText = QLabel("Project")
self.projIcon.setContentsMargins(0, 0, 0, 0)
self.projText.setContentsMargins(0, 0, 8, 0)
self.addPermanentWidget(self.projIcon)
self.addPermanentWidget(self.projText)
## The Project and Session Stats
self.statsIcon = QLabel()
self.statsText = QLabel("")
self.statsIcon.setPixmap(self.theParent.theTheme.getPixmap("status_stats",(14,14)))
self.statsIcon.setContentsMargins(0, 0, 0, 0)
self.statsText.setContentsMargins(0, 0, 8, 0)
self.addPermanentWidget(self.statsIcon)
self.addPermanentWidget(self.statsText)
## The Session Clock
self.timeIcon = QLabel()
self.timeText = QLabel("")
self.timeIcon.setPixmap(self.theParent.theTheme.getPixmap("status_time",(14,14)))
self.timeText.setToolTip("Session Time")
self.timeText.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.timeText.setFont(self.monoFont)
self.timeIcon.setContentsMargins(0, 0, 0, 0)
self.timeText.setContentsMargins(0, 0, 0, 0)
self.addPermanentWidget(self.timeIcon)
self.addPermanentWidget(self.timeText)
# Other Settings
self.setSizeGripEnabled(True) self.setSizeGripEnabled(True)
# Start the Clock
self.sessionTimer = QTimer() self.sessionTimer = QTimer()
self.sessionTimer.setInterval(1000) self.sessionTimer.setInterval(1000)
self.sessionTimer.timeout.connect(self._updateTime) self.sessionTimer.timeout.connect(self._updateTime)
@@ -114,64 +127,102 @@ class GuiMainStatus(QStatusBar):
return return
def clearStatus(self): def clearStatus(self):
"""Reset all widgets on the status bar to default values.
"""
self.setRefTime(None) self.setRefTime(None)
self.setStats(0,0) self.setStats(0, 0)
self.setCounts(0,0,0) self.setCounts(0, 0, 0)
self.setProjectStatus(None) self.setProjectStatus(None)
self.setDocumentStatus(None) self.setDocumentStatus(None)
self._updateTime() self._updateTime()
return True return True
def setRefTime(self, theTime): def setRefTime(self, theTime):
"""Set the reference time for the status bar clock.
"""
self.refTime = theTime self.refTime = theTime
return return
def setStatus(self, theMessage, timeOut=10.0): def setStatus(self, theMessage, timeOut=10.0):
"""Set the status bar message to display for 'timeOut' seconds.
"""
self.showMessage(theMessage, int(timeOut*1000)) self.showMessage(theMessage, int(timeOut*1000))
qApp.processEvents()
return return
def setLanguage(self, theLanguage): def setLanguage(self, theLanguage):
"""Set the language code for the spell checker.
"""
if theLanguage is None: if theLanguage is None:
self.langBox.setText("None") self.langText.setText("None")
else: else:
self.langBox.setText(NWSpellCheck.expandLanguage(theLanguage)) self.langText.setText(NWSpellCheck.expandLanguage(theLanguage))
return return
def setProjectStatus(self, isChanged): def setProjectStatus(self, isChanged):
if isChanged is None: """Set the project status colour icon.
self.projChanged.setPixmap(self.iconGrey) """
elif isChanged == True: self.projIcon.setState(isChanged)
self.projChanged.setPixmap(self.iconYellow)
elif isChanged == False:
self.projChanged.setPixmap(self.iconGreen)
else:
self.projChanged.setPixmap(self.iconGrey)
return return
def setDocumentStatus(self, isChanged): def setDocumentStatus(self, isChanged):
if isChanged is None: """Set the document status colour icon.
self.docChanged.setPixmap(self.iconGrey) """
elif isChanged == True: self.docIcon.setState(isChanged)
self.docChanged.setPixmap(self.iconYellow)
elif isChanged == False:
self.docChanged.setPixmap(self.iconGreen)
else:
self.docChanged.setPixmap(self.iconGrey)
return return
def setStats(self, pWC, sWC): def setStats(self, pWC, sWC):
self.boxStats.setText("<b>Project:</b> {:d} : {:d}".format(pWC,sWC)) """Set the current project statistics.
"""
self.projWords = pWC
self.sessWords = sWC
self._updateStats()
return return
def setCounts(self, cC, wC, pC): def setCounts(self, cC, wC, pC):
self.boxCounts.setText("<b>Document:</b> {:d} : {:d} : {:d}".format(cC,wC,pC)) """Set the current document statistics.
"""
self.charCount = cC
self.wordCount = wC
self.paraCount = pC
self._updateStats()
return return
## ##
# Internal Functions # Internal Functions
## ##
def _updateStats(self):
"""Update statistics.
"""
self.statsText.setToolTip((
"<b>Document Stats</b><br>"
"Characters: {cC:n}<br>"
"Words: {wC:n}<br>"
"Paragraphs: {pC:n}<br>"
"<br>"
"<b>Project Stats</b><br>"
"Words Total: {pWC:n}<br>"
"This Session: {sWC:n}"
).format(
cC = self.charCount,
wC = self.wordCount,
pC = self.paraCount,
pWC = self.projWords,
sWC = self.sessWords,
))
self.statsText.setText((
"D:{wC:n} P:{pWC:n} S:{sWC:n}"
).format(
wC = self.wordCount,
pWC = self.projWords,
sWC = self.sessWords,
))
return
def _updateTime(self): def _updateTime(self):
"""Update the session clock.
"""
if self.refTime is None: if self.refTime is None:
theTime = "00:00:00" theTime = "00:00:00"
else: else:
@@ -182,7 +233,58 @@ class GuiMainStatus(QStatusBar):
tM = tM - tH*60 tM = tM - tH*60
tS = tS - tM*60 - tH*3600 tS = tS - tM*60 - tH*3600
theTime = "%02d:%02d:%02d" % (tH,tM,tS) theTime = "%02d:%02d:%02d" % (tH,tM,tS)
self.timeBox.setText(theTime) self.timeText.setText(theTime)
return return
# END Class GuiMainStatus # END Class GuiMainStatus
class StatusLED(QAbstractButton):
def __init__(self, colNone, colTrue, colFalse, sW, sH, parent=None):
super().__init__(parent=parent)
self.colNone = colNone
self.colTrue = colTrue
self.colFalse = colFalse
self._theCol = colNone
self.setFixedWidth(sW)
self.setFixedHeight(sH)
return
##
# Getters and Setters
##
def setState(self, theState):
"""Set the colour state.
"""
if theState is None:
self._theCol = self.colNone
elif theState == True:
self._theCol = self.colTrue
elif theState == False:
self._theCol = self.colFalse
else:
self._theCol = self.colNone
self.update()
return
##
# Events
##
def paintEvent(self, event):
"""Drawing the LED.
"""
qPalette = self.palette()
qPaint = QPainter(self)
qPaint.setRenderHint(QPainter.Antialiasing, True)
qPaint.setPen(qPalette.dark().color())
qPaint.setBrush(self._theCol)
qPaint.setOpacity(1.0)
qPaint.drawEllipse(1, 1, self.width()-2, self.height()-2)
return
# END Class StatusLED
+2 -1
View File
@@ -91,7 +91,8 @@ class GuiDocHighlighter(QSyntaxHighlighter):
self.colTagErr = QColor(*self.theTheme.colTagErr) self.colTagErr = QColor(*self.theTheme.colTagErr)
self.colRepTag = QColor(*self.theTheme.colRepTag) self.colRepTag = QColor(*self.theTheme.colRepTag)
self.colMod = QColor(*self.theTheme.colMod) self.colMod = QColor(*self.theTheme.colMod)
self.colTrail = QColor(*self.theTheme.colEmph,64) self.colTrail = QColor(*self.theTheme.colEmph)
self.colTrail.setAlpha(64)
self.hStyles = { self.hStyles = {
"header1" : self._makeFormat(self.colHead, "bold",1.8), "header1" : self._makeFormat(self.colHead, "bold",1.8),
+22 -27
View File
@@ -26,17 +26,17 @@
""" """
import logging import logging
import time
import nw import nw
from os import path from os import path
from datetime import datetime from datetime import datetime
from time import time
from PyQt5.QtCore import Qt, QTimer from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QIcon, QPixmap, QColor, QKeySequence from PyQt5.QtGui import QIcon, QPixmap, QColor, QKeySequence, QCursor
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
qApp, QMainWindow, QVBoxLayout, QFrame, QSplitter, QFileDialog, QShortcut, qApp, QMainWindow, QVBoxLayout, QFrame, QSplitter, QFileDialog, QShortcut,
QMessageBox, QProgressDialog, QDialog, QTabWidget QMessageBox, QDialog, QTabWidget
) )
from nw.gui import ( from nw.gui import (
@@ -638,6 +638,8 @@ class GuiMain(QMainWindow):
return return
def rebuildTree(self): def rebuildTree(self):
"""Rebuild the project tree.
"""
self._makeStatusIcons() self._makeStatusIcons()
self._makeImportIcons() self._makeImportIcons()
self.treeView.clearTree() self.treeView.clearTree()
@@ -645,37 +647,25 @@ class GuiMain(QMainWindow):
return return
def rebuildIndex(self): def rebuildIndex(self):
"""Rebuild the entire index.
"""
if not self.hasProject: if not self.hasProject:
return False return False
logger.debug("Rebuilding indices ...") logger.debug("Rebuilding index ...")
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
tStart = time()
self.treeView.saveTreeOrder() self.treeView.saveTreeOrder()
self.theIndex.clearIndex() self.theIndex.clearIndex()
nItems = len(self.theProject.projTree) nItems = len(self.theProject.projTree)
dlgProg = QProgressDialog("Scanning files ...", "Cancel", 0, nItems, self) theDoc = NWDoc(self.theProject, self)
dlgProg.setWindowModality(Qt.WindowModal) for nDone, tItem in enumerate(self.theProject.projTree):
dlgProg.setMinimumDuration(0)
dlgProg.setFixedWidth(480)
dlgProg.setLabelText("Starting file scan ...")
dlgProg.setValue(0)
dlgProg.show()
time.sleep(0.5)
nDone = 0
for tItem in self.theProject.projTree:
dlgProg.setValue(nDone)
if tItem is not None and tItem.itemType == nwItemType.FILE: if tItem is not None and tItem.itemType == nwItemType.FILE:
dlgProg.setLabelText("Scanning: %s" % tItem.itemName)
logger.verbose("Scanning: %s" % tItem.itemName) logger.verbose("Scanning: %s" % tItem.itemName)
theText = theDoc.openDocument(tItem.itemHandle, showStatus=False)
theDoc = NWDoc(self.theProject, self)
theText = theDoc.openDocument(tItem.itemHandle, False)
# Build tag index # Build tag index
self.theIndex.scanText(tItem.itemHandle, theText) self.theIndex.scanText(tItem.itemHandle, theText)
@@ -688,11 +678,16 @@ class GuiMain(QMainWindow):
self.treeView.propagateCount(tItem.itemHandle, wC) self.treeView.propagateCount(tItem.itemHandle, wC)
self.treeView.projectWordCount() self.treeView.projectWordCount()
nDone += 1 self.statusBar.setStatus("Building index: %.2f%%" % (100.0*(nDone + 1)/nItems))
if dlgProg.wasCanceled():
break
dlgProg.setValue(nItems) self.docEditor.reloadText()
qApp.restoreOverrideCursor()
tEnd = time()
if self.mainConf.showGUI:
self.makeAlert(
"Project index rebuilt in %.3f seconds." % (tEnd - tStart), nwAlert.INFO
)
return True return True