+3
-1
@@ -1,7 +1,9 @@
|
|||||||
# Setup
|
# Setup/Install
|
||||||
MANIFEST
|
MANIFEST
|
||||||
dist/
|
dist/
|
||||||
|
build/
|
||||||
deploy/
|
deploy/
|
||||||
|
*.spec
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
docs/build/
|
docs/build/
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
@@ -10,6 +10,7 @@ This is the documentation for novelWriter |version|.
|
|||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
introduction
|
introduction
|
||||||
|
started
|
||||||
interface
|
interface
|
||||||
projects
|
projects
|
||||||
structure
|
structure
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
***************
|
||||||
|
Getting Started
|
||||||
|
***************
|
||||||
|
|
||||||
|
You can download novelWriter from https://github.com/vkbo/novelWriter/releases
|
||||||
|
|
||||||
|
Latest version is |version|:
|
||||||
|
|
||||||
|
* ZIP file: https://github.com/vkbo/novelWriter/archive/v0.3.2.zip
|
||||||
|
* TAR file: https://github.com/vkbo/novelWriter/archive/v0.3.2.tar.gz
|
||||||
|
|
||||||
|
Extract the archive to a location of your choice.
|
||||||
|
|
||||||
|
Installing Dependencies
|
||||||
|
=======================
|
||||||
|
|
||||||
|
If you already have Python installed, and don't mind starting novelWriter from command line, all you need to do is to open your command line tool, find the folder and run:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
python -m pip install -r requirements.txt
|
||||||
|
|
||||||
|
On some operating systems you need to use ``python3`` instead of ``python``.
|
||||||
|
|
||||||
|
The following Python packages are required to run novelWriter:
|
||||||
|
|
||||||
|
* ``pyqt5`` for the GUI
|
||||||
|
* ``appdirs`` for locating the system's config folder
|
||||||
|
* ``lxml`` for writing project files
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Sometimes the SVG graphics package for pyqt5 must be installed separately.
|
||||||
|
|
||||||
|
The following are optional, but recommended:
|
||||||
|
|
||||||
|
* ``pyenchant`` for spell checking
|
||||||
|
* ``pycountry`` for translating language codes to language names
|
||||||
|
* ``latexcodec`` for escaping unicode characters in LaTeX export
|
||||||
|
* ``pypandoc`` for additional exports to Word, Open Office, eBooks, etc.
|
||||||
|
|
||||||
|
|
||||||
|
Running novelWriter
|
||||||
|
===================
|
||||||
|
|
||||||
|
If all the required dependencies are met, you can run novelWriter from the command line:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
python novelWriter.py
|
||||||
|
|
||||||
|
A few switches are supported from the command line, mostly to assist in debugging if an error is encountered.
|
||||||
|
To list all options, run:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
python novelWriter.py --help
|
||||||
|
|
||||||
|
|
||||||
|
Building a Standalone Executable
|
||||||
|
================================
|
||||||
|
|
||||||
|
A standalone executable can be built with pyinstaller, using the python script named "install.py" in the source folder.
|
||||||
|
This script will automatically try to install all dependencies and build the standalone executable of novelWriter.
|
||||||
|
You can run the script by typing the following into your command prompt:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
python install.py
|
||||||
|
|
||||||
|
If successful, the executable will be in the "dist" folder.
|
||||||
|
|
||||||
|
|
||||||
|
Additional Instructions for Windows
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
If you don't have Python installed, you can download it from the python.org website.
|
||||||
|
The installers for Windows are available at https://www.python.org/downloads/windows/
|
||||||
|
|
||||||
|
novelWriter should work with Python 3.5 or higher, and the executable installer is the easiest to install.
|
||||||
|
Please note that the `pyenchant` package for spell checking does not currently work with the x86-64 version, so if you want spell checking, you must install the x86 version.
|
||||||
|
|
||||||
|
Also, make sure you select the "Add Python to PATH" option.
|
||||||
|
|
||||||
|
.. image:: images/python_win_install.png
|
||||||
|
:width: 600
|
||||||
|
|
||||||
|
Once Python is set up and running, you can either run novelWriter from the folder where you extracted it, or you can build an executable and run that from a desktop icon instead.
|
||||||
|
|
||||||
|
An install script is provided to automatically pull all dependencies and build a single executable.
|
||||||
|
Open a command prompt in the folder where you extracted novelWriter, and run:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
python install.py
|
||||||
|
|
||||||
|
If everything went well, you should find a "novelWriter.exe" file in a folder named "dist".
|
||||||
|
You can right-click it and create a desktop icon if you wish.
|
||||||
Executable
+85
@@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import getopt
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Defaults
|
||||||
|
buildWindowed = True
|
||||||
|
|
||||||
|
# Parse Options
|
||||||
|
shortOpt = "hd"
|
||||||
|
longOpt = [
|
||||||
|
"help",
|
||||||
|
"debug",
|
||||||
|
]
|
||||||
|
helpMsg = (
|
||||||
|
"\n"
|
||||||
|
"novelWriter Install Script\n"
|
||||||
|
"\n"
|
||||||
|
"Usage:\n"
|
||||||
|
" -h, --help Print this message.\n"
|
||||||
|
" -d, --debug Build novelWriter for debugging. To debug novelwriter after build,\n"
|
||||||
|
" run it from command line with the debug options. Please check the\n"
|
||||||
|
" novelWriter --help output for details.\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
inOpts, inArgs = getopt.getopt(sys.argv[1:],shortOpt,longOpt)
|
||||||
|
except getopt.GetoptError:
|
||||||
|
print(helpMsg)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
for inOpt, inArg in inOpts:
|
||||||
|
if inOpt in ("-h","--help"):
|
||||||
|
print(helpMsg)
|
||||||
|
sys.exit()
|
||||||
|
elif inOpt in ("-d", "--debug"):
|
||||||
|
buildWindowed = False
|
||||||
|
|
||||||
|
# Run pip
|
||||||
|
packList = ["pyinstaller"]
|
||||||
|
with open("requirements.txt",mode="r") as reqFile:
|
||||||
|
for reqPack in reqFile:
|
||||||
|
if len(reqPack.strip()) > 0:
|
||||||
|
packList.append(reqPack)
|
||||||
|
|
||||||
|
for packName in packList:
|
||||||
|
print("Installing package dependency: %s" % packName)
|
||||||
|
try:
|
||||||
|
subprocess.call([sys.executable, "-m", "pip", "install", packName])
|
||||||
|
except Exception as e:
|
||||||
|
print("Failed with error:")
|
||||||
|
print(str(e))
|
||||||
|
|
||||||
|
# Run pyinstaller
|
||||||
|
if sys.platform.startswith("win32"):
|
||||||
|
dotDot = ";"
|
||||||
|
else:
|
||||||
|
dotDot = ":"
|
||||||
|
|
||||||
|
instOpt = [
|
||||||
|
"--name=novelWriter",
|
||||||
|
"--onefile",
|
||||||
|
"--add-data=%s%s%s" % (os.path.join("nw", "themes"), dotDot,"themes"),
|
||||||
|
"--add-data=%s%s%s" % (os.path.join("nw", "graphics"),dotDot,"graphics"),
|
||||||
|
"--icon=%s" % os.path.join("nw", "graphics", "novelWriter.ico"),
|
||||||
|
]
|
||||||
|
if buildWindowed:
|
||||||
|
instOpt.append("--windowed")
|
||||||
|
|
||||||
|
instOpt.append("novelWriter.py")
|
||||||
|
|
||||||
|
import PyInstaller.__main__
|
||||||
|
PyInstaller.__main__.run(instOpt)
|
||||||
|
|
||||||
|
print("")
|
||||||
|
print("##################")
|
||||||
|
print(" Build Finished")
|
||||||
|
print("##################")
|
||||||
|
print("")
|
||||||
|
print("If everything went well, the novelWriter executable should be in the folder named 'dist'")
|
||||||
|
print("")
|
||||||
|
|
||||||
+7
-6
@@ -10,8 +10,9 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import sys
|
||||||
import getopt
|
import getopt
|
||||||
|
import logging
|
||||||
|
|
||||||
from os import path, remove, rename
|
from os import path, remove, rename
|
||||||
from PyQt5.QtWidgets import QApplication
|
from PyQt5.QtWidgets import QApplication
|
||||||
@@ -126,15 +127,15 @@ def main(sysArgs):
|
|||||||
inOpts, inArgs = getopt.getopt(sysArgs,shortOpt,longOpt)
|
inOpts, inArgs = getopt.getopt(sysArgs,shortOpt,longOpt)
|
||||||
except getopt.GetoptError:
|
except getopt.GetoptError:
|
||||||
print(helpMsg)
|
print(helpMsg)
|
||||||
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)
|
||||||
exit()
|
sys.exit()
|
||||||
elif inOpt in ("-v", "--version"):
|
elif inOpt in ("-v", "--version"):
|
||||||
print("%s %s Version %s" % (__package__,__status__,__version__))
|
print("%s %s Version %s" % (__package__,__status__,__version__))
|
||||||
exit()
|
sys.exit()
|
||||||
elif inOpt in ("-d", "--debug"):
|
elif inOpt in ("-d", "--debug"):
|
||||||
debugLevel = logging.DEBUG
|
debugLevel = logging.DEBUG
|
||||||
debugStr = "{name:>22}:{lineno:<4d} {levelname:8} {message:}"
|
debugStr = "{name:>22}:{lineno:<4d} {levelname:8} {message:}"
|
||||||
@@ -196,6 +197,6 @@ def main(sysArgs):
|
|||||||
else:
|
else:
|
||||||
nwApp = QApplication([__package__])
|
nwApp = QApplication([__package__])
|
||||||
nwGUI = GuiMain()
|
nwGUI = GuiMain()
|
||||||
exit(nwApp.exec_())
|
sys.exit(nwApp.exec_())
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
+3
-3
@@ -158,13 +158,13 @@ class Config:
|
|||||||
self.confFile = self.appHandle+".conf"
|
self.confFile = self.appHandle+".conf"
|
||||||
self.homePath = path.expanduser("~")
|
self.homePath = path.expanduser("~")
|
||||||
self.lastPath = self.homePath
|
self.lastPath = self.homePath
|
||||||
self.appPath = path.dirname(__file__)
|
self.appPath = getattr(sys, "_MEIPASS", path.abspath(path.dirname(__file__)))
|
||||||
self.appRoot = path.join(self.appPath,path.pardir)
|
self.appRoot = path.join(self.appPath,path.pardir)
|
||||||
self.helpPath = path.join(self.appRoot,"help","en_GB")
|
self.helpPath = path.join(self.appRoot,"help","en_GB")
|
||||||
self.guiPath = path.join(self.appPath,"gui")
|
self.guiPath = path.join(self.appPath,"gui")
|
||||||
self.themeRoot = path.join(self.appPath,"themes")
|
self.themeRoot = path.join(self.appPath,"themes")
|
||||||
self.themePath = path.join(self.themeRoot)
|
self.graphPath = path.join(self.appPath,"graphics")
|
||||||
self.appIcon = path.join(self.appRoot, nwFiles.APP_ICON)
|
self.appIcon = path.join(self.graphPath, nwFiles.APP_ICON)
|
||||||
|
|
||||||
# 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.
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
@@ -43,7 +43,7 @@ class GuiConfigEditor(QDialog):
|
|||||||
|
|
||||||
self.setWindowTitle("Preferences")
|
self.setWindowTitle("Preferences")
|
||||||
|
|
||||||
self.gradPath = path.abspath(path.join(self.mainConf.appPath,"graphics","gear.svg"))
|
self.gradPath = path.abspath(path.join(self.mainConf.graphPath,"gear.svg"))
|
||||||
self.svgGradient = QSvgWidget(path.join(self.gradPath))
|
self.svgGradient = QSvgWidget(path.join(self.gradPath))
|
||||||
self.svgGradient.setFixedSize(QSize(64,64))
|
self.svgGradient.setFixedSize(QSize(64,64))
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -55,7 +55,7 @@ class GuiExport(QDialog):
|
|||||||
self.setWindowTitle("Export Project")
|
self.setWindowTitle("Export Project")
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
self.gradPath = path.abspath(path.join(self.mainConf.appPath,"graphics","export.svg"))
|
self.gradPath = path.abspath(path.join(self.mainConf.graphPath,"export.svg"))
|
||||||
self.svgGradient = QSvgWidget(self.gradPath)
|
self.svgGradient = QSvgWidget(self.gradPath)
|
||||||
self.svgGradient.setFixedSize(QSize(64,64))
|
self.svgGradient.setFixedSize(QSize(64,64))
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class GuiItemEditor(QDialog):
|
|||||||
|
|
||||||
self.setWindowTitle("Item Settings")
|
self.setWindowTitle("Item Settings")
|
||||||
|
|
||||||
self.gradPath = path.abspath(path.join(self.mainConf.appPath,"graphics","gear.svg"))
|
self.gradPath = path.abspath(path.join(self.mainConf.graphPath,"gear.svg"))
|
||||||
self.svgGradient = QSvgWidget(self.gradPath)
|
self.svgGradient = QSvgWidget(self.gradPath)
|
||||||
self.svgGradient.setFixedSize(QSize(64,64))
|
self.svgGradient.setFixedSize(QSize(64,64))
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class GuiProjectEditor(QDialog):
|
|||||||
self.setWindowTitle("Project Settings")
|
self.setWindowTitle("Project Settings")
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
self.gradPath = path.abspath(path.join(self.mainConf.appPath,"graphics","gear.svg"))
|
self.gradPath = path.abspath(path.join(self.mainConf.graphPath,"gear.svg"))
|
||||||
self.svgGradient = QSvgWidget(self.gradPath)
|
self.svgGradient = QSvgWidget(self.gradPath)
|
||||||
self.svgGradient.setFixedSize(QSize(64,64))
|
self.svgGradient.setFixedSize(QSize(64,64))
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ class NWSpellEnchant(NWSpellCheck):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def setLanguage(self, theLang, projectDict=None):
|
def setLanguage(self, theLang, projectDict=None):
|
||||||
|
"""Load a dictionary for the language specified in the config. If that fails, we load a
|
||||||
|
dummy dictionary so that lookups don't crash.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
if projectDict is None:
|
if projectDict is None:
|
||||||
self.theDict = enchant.Dict(theLang)
|
self.theDict = enchant.Dict(theLang)
|
||||||
@@ -40,7 +43,8 @@ class NWSpellEnchant(NWSpellCheck):
|
|||||||
logger.debug("Enchant spell checking for language %s loaded" % theLang)
|
logger.debug("Enchant spell checking for language %s loaded" % theLang)
|
||||||
except:
|
except:
|
||||||
logger.error("Failed to load enchant spell checking for language %s" % theLang)
|
logger.error("Failed to load enchant spell checking for language %s" % theLang)
|
||||||
self.theDict = None
|
self.theDict = NWSpellEnchantDummy()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def checkWord(self, theWord):
|
def checkWord(self, theWord):
|
||||||
@@ -73,3 +77,19 @@ class NWSpellEnchant(NWSpellCheck):
|
|||||||
return retList
|
return retList
|
||||||
|
|
||||||
# END Class NWSpellEnchant
|
# END Class NWSpellEnchant
|
||||||
|
|
||||||
|
class NWSpellEnchantDummy:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
return
|
||||||
|
|
||||||
|
def check(self, theWord):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def suggest(self, theWord):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def add_to_pwl(self, theWord):
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class NWSpellEnchantDummy
|
||||||
|
|||||||
Reference in New Issue
Block a user