Merge pull request #91 from vkbo/pyinstaller_fixes

Pyinstaller Fixes
This commit is contained in:
Veronica K. Berglyd Olsen
2019-10-30 00:40:14 +01:00
committed by GitHub
14 changed files with 221 additions and 15 deletions
+3 -1
View File
@@ -1,7 +1,9 @@
# Setup
# Setup/Install
MANIFEST
dist/
build/
deploy/
*.spec
# Documentation
docs/build/
Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

+1
View File
@@ -10,6 +10,7 @@ This is the documentation for novelWriter |version|.
:maxdepth: 2
introduction
started
interface
projects
structure
+97
View File
@@ -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
View File
@@ -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
View File
@@ -10,8 +10,9 @@
"""
import logging
import sys
import getopt
import logging
from os import path, remove, rename
from PyQt5.QtWidgets import QApplication
@@ -126,15 +127,15 @@ def main(sysArgs):
inOpts, inArgs = getopt.getopt(sysArgs,shortOpt,longOpt)
except getopt.GetoptError:
print(helpMsg)
exit(2)
sys.exit(2)
for inOpt, inArg in inOpts:
if inOpt in ("-h","--help"):
if inOpt in ("-h","--help"):
print(helpMsg)
exit()
sys.exit()
elif inOpt in ("-v", "--version"):
print("%s %s Version %s" % (__package__,__status__,__version__))
exit()
sys.exit()
elif inOpt in ("-d", "--debug"):
debugLevel = logging.DEBUG
debugStr = "{name:>22}:{lineno:<4d} {levelname:8} {message:}"
@@ -196,6 +197,6 @@ def main(sysArgs):
else:
nwApp = QApplication([__package__])
nwGUI = GuiMain()
exit(nwApp.exec_())
sys.exit(nwApp.exec_())
return
+3 -3
View File
@@ -158,13 +158,13 @@ class Config:
self.confFile = self.appHandle+".conf"
self.homePath = path.expanduser("~")
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.helpPath = path.join(self.appRoot,"help","en_GB")
self.guiPath = path.join(self.appPath,"gui")
self.themeRoot = path.join(self.appPath,"themes")
self.themePath = path.join(self.themeRoot)
self.appIcon = path.join(self.appRoot, nwFiles.APP_ICON)
self.graphPath = path.join(self.appPath,"graphics")
self.appIcon = path.join(self.graphPath, nwFiles.APP_ICON)
# If config folder does not exist, make it.
# 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

+1 -1
View File
@@ -43,7 +43,7 @@ class GuiConfigEditor(QDialog):
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.setFixedSize(QSize(64,64))
+1 -1
View File
@@ -55,7 +55,7 @@ class GuiExport(QDialog):
self.setWindowTitle("Export Project")
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.setFixedSize(QSize(64,64))
+1 -1
View File
@@ -41,7 +41,7 @@ class GuiItemEditor(QDialog):
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.setFixedSize(QSize(64,64))
+1 -1
View File
@@ -43,7 +43,7 @@ class GuiProjectEditor(QDialog):
self.setWindowTitle("Project Settings")
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.setFixedSize(QSize(64,64))
+21 -1
View File
@@ -32,6 +32,9 @@ class NWSpellEnchant(NWSpellCheck):
return
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:
if projectDict is None:
self.theDict = enchant.Dict(theLang)
@@ -40,7 +43,8 @@ class NWSpellEnchant(NWSpellCheck):
logger.debug("Enchant spell checking for language %s loaded" % theLang)
except:
logger.error("Failed to load enchant spell checking for language %s" % theLang)
self.theDict = None
self.theDict = NWSpellEnchantDummy()
return
def checkWord(self, theWord):
@@ -73,3 +77,19 @@ class NWSpellEnchant(NWSpellCheck):
return retList
# 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