Merge branch 'master' into dev_0.6
This commit is contained in:
@@ -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:
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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>"
|
||||||
|
" - %s<br><br>Exiting."
|
||||||
|
) % (
|
||||||
|
__package__, "<br> - ".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 |
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user