Include HTML docs in minimal installs (#856)
* Link to stable docs for stable release, otherwise latest docs * Add build command to setup for making HTML docs * Change the documentation menu entry to open either online or local HTML * Update docs build instructions in docs
This commit is contained in:
committed by
GitHub
parent
a9368904e5
commit
eee0f5e4c9
@@ -17,6 +17,7 @@ i18n/*.qph
|
||||
|
||||
# Documentation
|
||||
/docs/build/
|
||||
/nw/assets/help/html/
|
||||
*.qch
|
||||
*.qhc
|
||||
|
||||
|
||||
@@ -141,13 +141,12 @@ generated. It requires the following Python packages on Debian and Ubuntu.
|
||||
|
||||
* ``python3-sphinx``
|
||||
* ``python3-sphinx-rtd-theme``
|
||||
* ``python3-sphinxcontrib.qthelp``
|
||||
|
||||
Or from PyPi:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install sphinx sphinx-rtd-theme sphinxcontrib-qthelp
|
||||
pip install sphinx sphinx-rtd-theme
|
||||
|
||||
The documentation can then be built from the ``docs`` folder in the source code by running:
|
||||
|
||||
@@ -158,19 +157,13 @@ The documentation can then be built from the ``docs`` folder in the source code
|
||||
If successful, the documentation should be available in the ``docs/build/html`` folder and you can
|
||||
open the ``index.html`` file in your browser.
|
||||
|
||||
The documentation can also be built for the Qt Assistant. To build the help packages from the
|
||||
documentation source, run the following from the root source folder:
|
||||
You can also build the documentation by using the setup script:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
python setup.py qthelp
|
||||
python setup.py docs
|
||||
|
||||
The setup script will copy the generated files into the ``nw/assets/help`` folder, and novelWriter
|
||||
will detect the presence of the files and redirect the menu help entry to open help locally instead
|
||||
of sending the user to the website. Pressing the :kbd:`F1` key will in any case try to open help
|
||||
locally first, then send you to the website as a fallback.
|
||||
|
||||
.. note::
|
||||
In order for the local version of help to work, the Qt Assistant must be installed on the local
|
||||
computer. If it isn't available, or novelWriter cannot find it, the help feature will fall back
|
||||
to redirecting you to the documentation website.
|
||||
This does the same as the ``make help`` command, but in addition it copies the documentation into
|
||||
novelWriter's assets folder. This will make it possible to open the documentation from inside of
|
||||
novelWriter by pressing :kbd:`F1` even without an internet connection, as it will instead open the
|
||||
local copy.
|
||||
|
||||
+1
-17
@@ -26,7 +26,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import logging
|
||||
|
||||
from time import time
|
||||
@@ -72,11 +71,9 @@ class Config:
|
||||
self.themeRoot = None # The full path to the nw/assets/themes folder
|
||||
self.dictPath = None # The full path to the nw/assets/dict folder
|
||||
self.iconPath = None # The full path to the nw/assets/icons folder
|
||||
self.helpPath = None # The full path to the novelwriter .qhc help file
|
||||
|
||||
# Runtime Settings and Variables
|
||||
self.confChanged = False # True whenever the config has chenged, false after save
|
||||
self.hasHelp = False # True if the Qt help files are present in the assets folder
|
||||
|
||||
# General
|
||||
self.guiTheme = "default"
|
||||
@@ -230,8 +227,7 @@ class Config:
|
||||
self.kernelVer = "Unknown"
|
||||
|
||||
# Packages
|
||||
self.hasEnchant = False # The pyenchant package
|
||||
self.hasAssistant = False # The Qt Assistant executable
|
||||
self.hasEnchant = False # The pyenchant package
|
||||
|
||||
# Recent Cache
|
||||
self.recentProj = {}
|
||||
@@ -360,11 +356,6 @@ class Config:
|
||||
if self.spellLanguage is None:
|
||||
self.spellLanguage = "en"
|
||||
|
||||
# Check if local help files exist
|
||||
self.helpPath = os.path.join(self.assetPath, "help", "novelWriter.qhc")
|
||||
self.hasHelp = os.path.isfile(self.helpPath)
|
||||
self.hasHelp &= os.path.isfile(os.path.join(self.assetPath, "help", "novelWriter.qch"))
|
||||
|
||||
logger.debug("Config initialisation complete")
|
||||
|
||||
return True
|
||||
@@ -930,13 +921,6 @@ class Config:
|
||||
self.hasEnchant = False
|
||||
logger.debug("Checking package 'pyenchant': Missing")
|
||||
|
||||
assistPath = shutil.which("assistant")
|
||||
self.hasAssistant = assistPath is not None
|
||||
if self.hasAssistant:
|
||||
logger.debug("Checking executable 'assistant': OK")
|
||||
else:
|
||||
logger.debug("Checking executable 'assistant': Missing")
|
||||
|
||||
return
|
||||
|
||||
# END Class Config
|
||||
|
||||
+38
-57
@@ -23,10 +23,15 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import os
|
||||
import nw
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import QUrl, QProcess
|
||||
from http.client import HTTPSConnection
|
||||
from urllib.parse import urljoin
|
||||
from urllib.request import pathname2url
|
||||
|
||||
from PyQt5.QtCore import QUrl
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
from PyQt5.QtWidgets import QMenuBar, QAction
|
||||
|
||||
@@ -46,9 +51,6 @@ class GuiMainMenu(QMenuBar):
|
||||
self.theParent = theParent
|
||||
self.theProject = theParent.theProject
|
||||
|
||||
# Internals
|
||||
self._assistProc = None
|
||||
|
||||
# Build Menu
|
||||
self._buildProjectMenu()
|
||||
self._buildDocumentMenu()
|
||||
@@ -89,25 +91,6 @@ class GuiMainMenu(QMenuBar):
|
||||
)
|
||||
return
|
||||
|
||||
def closeHelp(self):
|
||||
"""Close the process used for the Qt Assistant, if it is open.
|
||||
"""
|
||||
if self._assistProc is None:
|
||||
return
|
||||
|
||||
if self._assistProc.state() == QProcess.Starting:
|
||||
if self._assistProc.waitForStarted(10000):
|
||||
self._assistProc.terminate()
|
||||
else:
|
||||
self._assistProc.kill()
|
||||
|
||||
elif self._assistProc.state() == QProcess.Running:
|
||||
self._assistProc.terminate()
|
||||
if not self._assistProc.waitForFinished(10000):
|
||||
self._assistProc.kill()
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Update Menu on Settings Changed
|
||||
##
|
||||
@@ -148,28 +131,38 @@ class GuiMainMenu(QMenuBar):
|
||||
self.theProject.setAutoOutline(theMode)
|
||||
return True
|
||||
|
||||
def _openAssistant(self, isChecked=False):
|
||||
"""Open the documentation in Qt Assistant.
|
||||
"""
|
||||
if not self.mainConf.hasHelp:
|
||||
self._openWebsite(nw.__docurl__)
|
||||
return False
|
||||
|
||||
self._assistProc = QProcess(self)
|
||||
self._assistProc.start("assistant", ["-collectionFile", self.mainConf.helpPath])
|
||||
|
||||
if not self._assistProc.waitForStarted(10000):
|
||||
self._openWebsite(nw.__docurl__)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _openWebsite(self, theUrl):
|
||||
"""Open a URL in the system's default browser.
|
||||
"""
|
||||
QDesktopServices.openUrl(QUrl(theUrl))
|
||||
return True
|
||||
|
||||
def _openDocumentation(self):
|
||||
"""Open the documentation, and select whether it should be the
|
||||
stable or latest version. If no internet connection, open
|
||||
local.
|
||||
"""
|
||||
if nw.__hexversion__[-2] == "f":
|
||||
docsPath = "/en/stable/"
|
||||
else:
|
||||
docsPath = "/en/latest/"
|
||||
|
||||
try:
|
||||
conn = HTTPSConnection(nw.__docurl__.replace("https://", ""))
|
||||
conn.request("HEAD", docsPath)
|
||||
resp = conn.getresponse()
|
||||
hasAccess = resp.code == 200
|
||||
except Exception:
|
||||
hasAccess = False
|
||||
|
||||
docsFile = os.path.join(self.mainConf.assetPath, "help", "html", "index.html")
|
||||
if hasAccess or not os.path.isfile(docsFile):
|
||||
self._openWebsite(nw.__docurl__ + docsPath)
|
||||
else:
|
||||
self._openWebsite(urljoin("file:", pathname2url(docsFile)))
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Menu Builders
|
||||
##
|
||||
@@ -1113,24 +1106,12 @@ class GuiMainMenu(QMenuBar):
|
||||
# Help > Separator
|
||||
self.helpMenu.addSeparator()
|
||||
|
||||
# Document > Documentation
|
||||
if self.mainConf.hasHelp and self.mainConf.hasAssistant:
|
||||
self.aHelpLoc = QAction(self.tr("Documentation (Local)"), self)
|
||||
self.aHelpLoc.setStatusTip(self.tr("View local documentation with Qt Assistant"))
|
||||
self.aHelpLoc.triggered.connect(self._openAssistant)
|
||||
self.aHelpLoc.setShortcut("F1")
|
||||
self.helpMenu.addAction(self.aHelpLoc)
|
||||
|
||||
self.aHelpWeb = QAction(self.tr("Documentation (Online)"), self)
|
||||
self.aHelpWeb.setStatusTip(
|
||||
self.tr("View online documentation at {0}").format(nw.__docurl__)
|
||||
)
|
||||
self.aHelpWeb.triggered.connect(lambda: self._openWebsite(nw.__docurl__))
|
||||
if self.mainConf.hasHelp and self.mainConf.hasAssistant:
|
||||
self.aHelpWeb.setShortcut("Shift+F1")
|
||||
else:
|
||||
self.aHelpWeb.setShortcuts(["F1", "Shift+F1"])
|
||||
self.helpMenu.addAction(self.aHelpWeb)
|
||||
# Help > Documentation
|
||||
self.aHelpDocs = QAction(self.tr("Documentation"), self)
|
||||
self.aHelpDocs.setStatusTip(self.tr("Open documentation in browser"))
|
||||
self.aHelpDocs.triggered.connect(self._openDocumentation)
|
||||
self.aHelpDocs.setShortcut("F1")
|
||||
self.helpMenu.addAction(self.aHelpDocs)
|
||||
|
||||
# Help > Separator
|
||||
self.helpMenu.addSeparator()
|
||||
|
||||
+1
-4
@@ -1190,7 +1190,6 @@ class GuiMain(QMainWindow):
|
||||
|
||||
self.mainConf.saveConfig()
|
||||
self.reportConfErr()
|
||||
self.mainMenu.closeHelp()
|
||||
|
||||
qApp.quit()
|
||||
|
||||
@@ -1373,9 +1372,7 @@ class GuiMain(QMainWindow):
|
||||
self.addAction(self.mainMenu.aPreferences)
|
||||
|
||||
# Help
|
||||
if self.mainConf.hasHelp and self.mainConf.hasAssistant:
|
||||
self.addAction(self.mainMenu.aHelpLoc)
|
||||
self.addAction(self.mainMenu.aHelpWeb)
|
||||
self.addAction(self.mainMenu.aHelpDocs)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -221,6 +221,56 @@ def buildQtDocs():
|
||||
return
|
||||
|
||||
|
||||
##
|
||||
# Html Documentation Builder (docs)
|
||||
##
|
||||
|
||||
def buildHtmlDocs():
|
||||
"""This function will build the Sphinx HTML documentation. The files
|
||||
are then copied into the nw/assets/help/html directory and can be
|
||||
included in builds.
|
||||
"""
|
||||
buildDir = os.path.join("docs", "build", "html")
|
||||
helpDir = os.path.join("nw", "assets", "help", "html")
|
||||
|
||||
print("")
|
||||
print("Building Documentation")
|
||||
print("======================")
|
||||
print("")
|
||||
|
||||
buildFail = False
|
||||
try:
|
||||
subprocess.call(["make", "-C", "docs", "clean"])
|
||||
subprocess.call(["make", "-C", "docs", "html"])
|
||||
except Exception as e:
|
||||
print("Docs Build Error:")
|
||||
print(str(e))
|
||||
buildFail = True
|
||||
|
||||
try:
|
||||
if os.path.isdir(helpDir):
|
||||
shutil.rmtree(helpDir)
|
||||
shutil.copytree(buildDir, helpDir)
|
||||
except Exception as e:
|
||||
print("Docs Build Error:")
|
||||
print(str(e))
|
||||
buildFail = True
|
||||
|
||||
print("")
|
||||
if buildFail:
|
||||
print("Documentation build: FAILED")
|
||||
print("")
|
||||
print("Dependencies:")
|
||||
print(" * pip install sphinx")
|
||||
print(" * pip install sphinx-rtd-theme")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("Documentation build: OK")
|
||||
print("")
|
||||
|
||||
return
|
||||
|
||||
|
||||
##
|
||||
# Qt Linguist QM Builder (qtlrelease)
|
||||
##
|
||||
@@ -349,6 +399,14 @@ def makeMinimalPackage(targetOS):
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
# Build docs
|
||||
try:
|
||||
buildHtmlDocs()
|
||||
except Exception as e:
|
||||
print("Failed with error:")
|
||||
print(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
# Make translation files
|
||||
try:
|
||||
buildQtI18n()
|
||||
@@ -1137,6 +1195,7 @@ if __name__ == "__main__":
|
||||
"",
|
||||
"Additional Builds:",
|
||||
"",
|
||||
" docs Build the help documentation as HTML."
|
||||
" qthelp Build the help documentation for use with the Qt Assistant.",
|
||||
" qtlupdate Update the translation files for internationalisation.",
|
||||
" qtlrelease Build the language files for internationalisation.",
|
||||
@@ -1203,6 +1262,10 @@ if __name__ == "__main__":
|
||||
# Additional Builds
|
||||
# =================
|
||||
|
||||
if "docs" in sys.argv:
|
||||
sys.argv.remove("docs")
|
||||
buildHtmlDocs()
|
||||
|
||||
if "qthelp" in sys.argv:
|
||||
sys.argv.remove("qthelp")
|
||||
buildQtDocs()
|
||||
|
||||
@@ -544,14 +544,4 @@ def testBaseConfig_Internal(monkeypatch, tmpConf):
|
||||
tmpConf._checkOptionalPackages()
|
||||
assert tmpConf.hasEnchant is False
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("shutil.which", lambda *a: "stuff")
|
||||
tmpConf._checkOptionalPackages()
|
||||
assert tmpConf.hasAssistant is True
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("shutil.which", lambda *a: None)
|
||||
tmpConf._checkOptionalPackages()
|
||||
assert tmpConf.hasAssistant is False
|
||||
|
||||
# END Test testBaseConfig_Internal
|
||||
|
||||
Reference in New Issue
Block a user