@@ -121,7 +121,7 @@ regularly tested on Windows 10.
|
|||||||
|
|
||||||
The application can be started from the source folder with one of the commands, depending on your
|
The application can be started from the source folder with one of the commands, depending on your
|
||||||
Python configuration:
|
Python configuration:
|
||||||
```
|
```bash
|
||||||
./novelWriter.py
|
./novelWriter.py
|
||||||
python novelWriter.py
|
python novelWriter.py
|
||||||
python3 novelWriter.py
|
python3 novelWriter.py
|
||||||
@@ -141,10 +141,9 @@ It is recommended that novelWriter runs with Qt 5.10 or later, and Python 3.6 or
|
|||||||
Qt as low as 5.2.1 and Python 3.4.3 has been tested, and worked in the past, but there are no
|
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.
|
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/Ubuntu systems, the following Python3 packages are needed:
|
||||||
|
|
||||||
* `python3-pyqt5` for the GUI
|
* `python3-pyqt5` for the GUI
|
||||||
* `python3-pyqt5.qtsvg` may need to be installed separately
|
|
||||||
* `python3-lxml` for writing project files
|
* `python3-lxml` for writing project files
|
||||||
|
|
||||||
These are optional, but recommended:
|
These are optional, but recommended:
|
||||||
|
|||||||
@@ -66,10 +66,6 @@ The following Python packages are required to run novelWriter:
|
|||||||
|
|
||||||
You can of course also install these packages from your operating system's package repository.
|
You can of course also install these packages from your operating system's package repository.
|
||||||
|
|
||||||
.. note::
|
|
||||||
Sometimes the SVG graphics package for PyQt5 must be installed separately. It is usually called
|
|
||||||
something like ``python3-pyqt5.qtsvg``.
|
|
||||||
|
|
||||||
PyQt/Qt should be at least 5.2.1, but ideally 5.10 or higher for nearly all features to work.
|
PyQt/Qt should be at least 5.2.1, but ideally 5.10 or higher for nearly all features to work.
|
||||||
Exporting to standard Markdown, for instance, requires PyQt/Qt 5.14. Searching using regular
|
Exporting to standard Markdown, for instance, requires PyQt/Qt 5.14. Searching using regular
|
||||||
expressions requires 5.3, and for full Unicode support, 5.13.
|
expressions requires 5.3, and for full Unicode support, 5.13.
|
||||||
|
|||||||
+4
-5
@@ -27,13 +27,13 @@ helpMsg = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
inOpts, inArgs = getopt.getopt(sys.argv[1:],shortOpt,longOpt)
|
inOpts, inArgs = getopt.getopt(sys.argv[1:], shortOpt, longOpt)
|
||||||
except getopt.GetoptError:
|
except getopt.GetoptError:
|
||||||
print(helpMsg)
|
print(helpMsg)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
for inOpt, inArg in inOpts:
|
for inOpt, inArg in inOpts:
|
||||||
if inOpt in ("-h","--help"):
|
if inOpt in ("-h", "--help"):
|
||||||
print(helpMsg)
|
print(helpMsg)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
elif inOpt in ("-d", "--debug"):
|
elif inOpt in ("-d", "--debug"):
|
||||||
@@ -41,7 +41,7 @@ for inOpt, inArg in inOpts:
|
|||||||
|
|
||||||
# Run pip
|
# Run pip
|
||||||
packList = ["pyinstaller"]
|
packList = ["pyinstaller"]
|
||||||
with open("requirements.txt",mode="r") as reqFile:
|
with open("requirements.txt", mode="r") as reqFile:
|
||||||
for reqPack in reqFile:
|
for reqPack in reqFile:
|
||||||
if len(reqPack.strip()) > 0:
|
if len(reqPack.strip()) > 0:
|
||||||
packList.append(reqPack)
|
packList.append(reqPack)
|
||||||
@@ -72,7 +72,7 @@ if buildWindowed:
|
|||||||
|
|
||||||
instOpt.append("novelWriter.py")
|
instOpt.append("novelWriter.py")
|
||||||
|
|
||||||
import PyInstaller.__main__
|
import PyInstaller.__main__ # noqa: F401
|
||||||
PyInstaller.__main__.run(instOpt)
|
PyInstaller.__main__.run(instOpt)
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
@@ -82,4 +82,3 @@ print("##################")
|
|||||||
print("")
|
print("")
|
||||||
print("If everything went well, the novelWriter executable should be in the folder named 'dist'")
|
print("If everything went well, the novelWriter executable should be in the folder named 'dist'")
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -4,10 +4,10 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import PyQt5.QtWidgets
|
import PyQt5.QtWidgets # noqa: F401
|
||||||
import PyQt5.QtGui
|
import PyQt5.QtGui # noqa: F401
|
||||||
import PyQt5.QtCore
|
import PyQt5.QtCore # noqa: F401
|
||||||
except:
|
except Exception:
|
||||||
print("ERROR: Failed to load dependency python3-pyqt5")
|
print("ERROR: Failed to load dependency python3-pyqt5")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -199,28 +199,28 @@ def main(sysArgs=None):
|
|||||||
|
|
||||||
# Check Packages and Versions
|
# Check Packages and Versions
|
||||||
errorData = []
|
errorData = []
|
||||||
|
errorCode = 0
|
||||||
if sys.hexversion < 0x030403f0:
|
if sys.hexversion < 0x030403f0:
|
||||||
errorData.append(
|
errorData.append(
|
||||||
"At least Python 3.4.3 is required, but 3.6 is highly recommended."
|
"At least Python 3.4.3 is required, but 3.6 is highly recommended."
|
||||||
)
|
)
|
||||||
|
errorCode |= 4
|
||||||
if CONFIG.verQtValue < 50200:
|
if CONFIG.verQtValue < 50200:
|
||||||
errorData.append(
|
errorData.append(
|
||||||
"At least Qt5 version 5.2 is required, found %s." % CONFIG.verQtString
|
"At least Qt5 version 5.2 is required, found %s." % CONFIG.verQtString
|
||||||
)
|
)
|
||||||
|
errorCode |= 8
|
||||||
if CONFIG.verPyQtValue < 50200:
|
if CONFIG.verPyQtValue < 50200:
|
||||||
errorData.append(
|
errorData.append(
|
||||||
"At least PyQt5 version 5.2 is required, found %s." % CONFIG.verPyQtString
|
"At least PyQt5 version 5.2 is required, found %s." % CONFIG.verPyQtString
|
||||||
)
|
)
|
||||||
|
errorCode |= 16
|
||||||
try:
|
|
||||||
import PyQt5.QtSvg # noqa: F401
|
|
||||||
except ImportError:
|
|
||||||
errorData.append("Python module 'PyQt5.QtSvg' is missing.")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import lxml # noqa: F401
|
import lxml # noqa: F401
|
||||||
except ImportError:
|
except ImportError:
|
||||||
errorData.append("Python module 'lxml' is missing.")
|
errorData.append("Python module 'lxml' is missing.")
|
||||||
|
errorCode |= 32
|
||||||
|
|
||||||
if errorData:
|
if errorData:
|
||||||
if not testMode:
|
if not testMode:
|
||||||
@@ -236,7 +236,7 @@ def main(sysArgs=None):
|
|||||||
"<br> - ".join(errorData)
|
"<br> - ".join(errorData)
|
||||||
))
|
))
|
||||||
errApp.exec_()
|
errApp.exec_()
|
||||||
sys.exit(10 + len(errorData))
|
sys.exit(errorCode)
|
||||||
|
|
||||||
# Finish initialising config
|
# Finish initialising config
|
||||||
CONFIG.initConfig(confPath, dataPath)
|
CONFIG.initConfig(confPath, dataPath)
|
||||||
|
|||||||
+2
-4
@@ -68,7 +68,6 @@ class Config:
|
|||||||
self.confPath = None # Folder where the config is saved
|
self.confPath = None # Folder where the config is saved
|
||||||
self.confFile = None # The config file name
|
self.confFile = None # The config file name
|
||||||
self.dataPath = None # Folder where app data is stored
|
self.dataPath = None # Folder where app data is stored
|
||||||
self.homePath = None # The user's home folder
|
|
||||||
self.lastPath = None # The last user-selected folder (browse dialogs)
|
self.lastPath = None # The last user-selected folder (browse dialogs)
|
||||||
self.appPath = None # The full path to the novelwriter package folder
|
self.appPath = None # The full path to the novelwriter package folder
|
||||||
self.appRoot = None # The full path to the novelwriter root folder
|
self.appRoot = None # The full path to the novelwriter root folder
|
||||||
@@ -257,8 +256,7 @@ class Config:
|
|||||||
logger.verbose("Data path: %s" % self.dataPath)
|
logger.verbose("Data path: %s" % self.dataPath)
|
||||||
|
|
||||||
self.confFile = self.appHandle+".conf"
|
self.confFile = self.appHandle+".conf"
|
||||||
self.homePath = os.path.expanduser("~")
|
self.lastPath = os.path.expanduser("~")
|
||||||
self.lastPath = self.homePath
|
|
||||||
self.appPath = getattr(sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__)))
|
self.appPath = getattr(sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__)))
|
||||||
self.appRoot = os.path.join(self.appPath, os.path.pardir)
|
self.appRoot = os.path.join(self.appPath, os.path.pardir)
|
||||||
self.assetPath = os.path.join(self.appPath, "assets")
|
self.assetPath = os.path.join(self.appPath, "assets")
|
||||||
@@ -268,7 +266,7 @@ class Config:
|
|||||||
self.appIcon = os.path.join(self.iconPath, "novelwriter.svg")
|
self.appIcon = os.path.join(self.iconPath, "novelwriter.svg")
|
||||||
|
|
||||||
logger.verbose("App path: %s" % self.appPath)
|
logger.verbose("App path: %s" % self.appPath)
|
||||||
logger.verbose("Home path: %s" % self.homePath)
|
logger.verbose("Last path: %s" % self.lastPath)
|
||||||
|
|
||||||
# If config folder does not exist, make it.
|
# If config folder does not exist, make it.
|
||||||
# This assumes that the os config folder itself exists.
|
# This assumes that the os config folder itself exists.
|
||||||
|
|||||||
@@ -757,6 +757,10 @@ class NWProject():
|
|||||||
if self.projPath is None or self.projPath == "":
|
if self.projPath is None or self.projPath == "":
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if self.projPath == os.path.expanduser("~"):
|
||||||
|
# Don't make a mess in the user's home folder
|
||||||
|
return False
|
||||||
|
|
||||||
self.projMeta = os.path.join(self.projPath, "meta")
|
self.projMeta = os.path.join(self.projPath, "meta")
|
||||||
self.projCache = os.path.join(self.projPath, "cache")
|
self.projCache = os.path.join(self.projPath, "cache")
|
||||||
self.projContent = os.path.join(self.projPath, "content")
|
self.projContent = os.path.join(self.projPath, "content")
|
||||||
|
|||||||
@@ -320,11 +320,12 @@ class GuiWritingStats(QDialog):
|
|||||||
|
|
||||||
# Generate the file name
|
# Generate the file name
|
||||||
if fileExt:
|
if fileExt:
|
||||||
fileName = "sessionStats.%s" % fileExt
|
saveDir = self.mainConf.lastPath
|
||||||
saveDir = self.mainConf.lastPath
|
|
||||||
savePath = os.path.join(saveDir, fileName)
|
|
||||||
if not os.path.isdir(saveDir):
|
if not os.path.isdir(saveDir):
|
||||||
saveDir = self.mainConf.homePath
|
saveDir = os.path.expanduser("~")
|
||||||
|
|
||||||
|
fileName = "sessionStats.%s" % fileExt
|
||||||
|
savePath = os.path.join(saveDir, fileName)
|
||||||
|
|
||||||
dlgOpt = QFileDialog.Options()
|
dlgOpt = QFileDialog.Options()
|
||||||
dlgOpt |= QFileDialog.DontUseNativeDialog
|
dlgOpt |= QFileDialog.DontUseNativeDialog
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ setuptools.setup(
|
|||||||
"console_scripts" : ["novelWriter-cli=nw:main"],
|
"console_scripts" : ["novelWriter-cli=nw:main"],
|
||||||
"gui_scripts" : ["novelWriter=nw:main"],
|
"gui_scripts" : ["novelWriter=nw:main"],
|
||||||
},
|
},
|
||||||
packages = setuptools.find_packages(exclude=["docs","tests","sample"]),
|
packages = setuptools.find_packages(exclude=["docs", "tests", "sample"]),
|
||||||
include_package_data = True,
|
include_package_data = True,
|
||||||
package_data = {"": ["*.conf"]},
|
package_data = {"": ["*.conf"]},
|
||||||
project_urls = {
|
project_urls = {
|
||||||
|
|||||||
+4
-2
@@ -85,7 +85,6 @@ def testLaunch(qtbot, monkeypatch, nwFuncTemp, nwTemp):
|
|||||||
assert ex.value.code == 2
|
assert ex.value.code == 2
|
||||||
|
|
||||||
# Simulate import error
|
# Simulate import error
|
||||||
monkeypatch.setitem(sys.modules, "PyQt5.QtSvg", None)
|
|
||||||
monkeypatch.setitem(sys.modules, "lxml", None)
|
monkeypatch.setitem(sys.modules, "lxml", None)
|
||||||
monkeypatch.setattr("sys.hexversion", 0x0)
|
monkeypatch.setattr("sys.hexversion", 0x0)
|
||||||
monkeypatch.setattr("nw.CONFIG.verQtValue", 50000)
|
monkeypatch.setattr("nw.CONFIG.verQtValue", 50000)
|
||||||
@@ -96,7 +95,10 @@ def testLaunch(qtbot, monkeypatch, nwFuncTemp, nwTemp):
|
|||||||
)
|
)
|
||||||
nwGUI.closeMain()
|
nwGUI.closeMain()
|
||||||
nwGUI.close()
|
nwGUI.close()
|
||||||
assert ex.value.code == 15
|
assert ex.value.code & 4 == 4 # Python version not satisfied
|
||||||
|
assert ex.value.code & 8 == 8 # Qt version not satisfied
|
||||||
|
assert ex.value.code & 16 == 16 # PyQt version not satisfied
|
||||||
|
assert ex.value.code & 32 == 32 # lxml package missing
|
||||||
monkeypatch.undo()
|
monkeypatch.undo()
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
|
|||||||
Reference in New Issue
Block a user