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:
Veronica Berglyd Olsen
2021-08-18 00:13:28 +02:00
committed by GitHub
parent a9368904e5
commit eee0f5e4c9
7 changed files with 111 additions and 102 deletions
+1
View File
@@ -17,6 +17,7 @@ i18n/*.qph
# Documentation # Documentation
/docs/build/ /docs/build/
/nw/assets/help/html/
*.qch *.qch
*.qhc *.qhc
+7 -14
View File
@@ -141,13 +141,12 @@ generated. It requires the following Python packages on Debian and Ubuntu.
* ``python3-sphinx`` * ``python3-sphinx``
* ``python3-sphinx-rtd-theme`` * ``python3-sphinx-rtd-theme``
* ``python3-sphinxcontrib.qthelp``
Or from PyPi: Or from PyPi:
.. code-block:: console .. 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: 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 If successful, the documentation should be available in the ``docs/build/html`` folder and you can
open the ``index.html`` file in your browser. 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 You can also build the documentation by using the setup script:
documentation source, run the following from the root source folder:
.. code-block:: console .. 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 This does the same as the ``make help`` command, but in addition it copies the documentation into
will detect the presence of the files and redirect the menu help entry to open help locally instead novelWriter's assets folder. This will make it possible to open the documentation from inside of
of sending the user to the website. Pressing the :kbd:`F1` key will in any case try to open help novelWriter by pressing :kbd:`F1` even without an internet connection, as it will instead open the
locally first, then send you to the website as a fallback. local copy.
.. 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.
+1 -17
View File
@@ -26,7 +26,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import os import os
import sys import sys
import json import json
import shutil
import logging import logging
from time import time from time import time
@@ -72,11 +71,9 @@ class Config:
self.themeRoot = None # The full path to the nw/assets/themes folder 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.dictPath = None # The full path to the nw/assets/dict folder
self.iconPath = None # The full path to the nw/assets/icons 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 # Runtime Settings and Variables
self.confChanged = False # True whenever the config has chenged, false after save 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 # General
self.guiTheme = "default" self.guiTheme = "default"
@@ -230,8 +227,7 @@ class Config:
self.kernelVer = "Unknown" self.kernelVer = "Unknown"
# Packages # Packages
self.hasEnchant = False # The pyenchant package self.hasEnchant = False # The pyenchant package
self.hasAssistant = False # The Qt Assistant executable
# Recent Cache # Recent Cache
self.recentProj = {} self.recentProj = {}
@@ -360,11 +356,6 @@ class Config:
if self.spellLanguage is None: if self.spellLanguage is None:
self.spellLanguage = "en" 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") logger.debug("Config initialisation complete")
return True return True
@@ -930,13 +921,6 @@ class Config:
self.hasEnchant = False self.hasEnchant = False
logger.debug("Checking package 'pyenchant': Missing") 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 return
# END Class Config # END Class Config
+38 -57
View File
@@ -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/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
import os
import nw import nw
import logging 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.QtGui import QDesktopServices
from PyQt5.QtWidgets import QMenuBar, QAction from PyQt5.QtWidgets import QMenuBar, QAction
@@ -46,9 +51,6 @@ class GuiMainMenu(QMenuBar):
self.theParent = theParent self.theParent = theParent
self.theProject = theParent.theProject self.theProject = theParent.theProject
# Internals
self._assistProc = None
# Build Menu # Build Menu
self._buildProjectMenu() self._buildProjectMenu()
self._buildDocumentMenu() self._buildDocumentMenu()
@@ -89,25 +91,6 @@ class GuiMainMenu(QMenuBar):
) )
return 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 # Update Menu on Settings Changed
## ##
@@ -148,28 +131,38 @@ class GuiMainMenu(QMenuBar):
self.theProject.setAutoOutline(theMode) self.theProject.setAutoOutline(theMode)
return True 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): def _openWebsite(self, theUrl):
"""Open a URL in the system's default browser. """Open a URL in the system's default browser.
""" """
QDesktopServices.openUrl(QUrl(theUrl)) QDesktopServices.openUrl(QUrl(theUrl))
return True 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 # Menu Builders
## ##
@@ -1113,24 +1106,12 @@ class GuiMainMenu(QMenuBar):
# Help > Separator # Help > Separator
self.helpMenu.addSeparator() self.helpMenu.addSeparator()
# Document > Documentation # Help > Documentation
if self.mainConf.hasHelp and self.mainConf.hasAssistant: self.aHelpDocs = QAction(self.tr("Documentation"), self)
self.aHelpLoc = QAction(self.tr("Documentation (Local)"), self) self.aHelpDocs.setStatusTip(self.tr("Open documentation in browser"))
self.aHelpLoc.setStatusTip(self.tr("View local documentation with Qt Assistant")) self.aHelpDocs.triggered.connect(self._openDocumentation)
self.aHelpLoc.triggered.connect(self._openAssistant) self.aHelpDocs.setShortcut("F1")
self.aHelpLoc.setShortcut("F1") self.helpMenu.addAction(self.aHelpDocs)
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 > Separator # Help > Separator
self.helpMenu.addSeparator() self.helpMenu.addSeparator()
+1 -4
View File
@@ -1190,7 +1190,6 @@ class GuiMain(QMainWindow):
self.mainConf.saveConfig() self.mainConf.saveConfig()
self.reportConfErr() self.reportConfErr()
self.mainMenu.closeHelp()
qApp.quit() qApp.quit()
@@ -1373,9 +1372,7 @@ class GuiMain(QMainWindow):
self.addAction(self.mainMenu.aPreferences) self.addAction(self.mainMenu.aPreferences)
# Help # Help
if self.mainConf.hasHelp and self.mainConf.hasAssistant: self.addAction(self.mainMenu.aHelpDocs)
self.addAction(self.mainMenu.aHelpLoc)
self.addAction(self.mainMenu.aHelpWeb)
return True return True
+63
View File
@@ -221,6 +221,56 @@ def buildQtDocs():
return 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) # Qt Linguist QM Builder (qtlrelease)
## ##
@@ -349,6 +399,14 @@ def makeMinimalPackage(targetOS):
print(str(e)) print(str(e))
sys.exit(1) sys.exit(1)
# Build docs
try:
buildHtmlDocs()
except Exception as e:
print("Failed with error:")
print(str(e))
sys.exit(1)
# Make translation files # Make translation files
try: try:
buildQtI18n() buildQtI18n()
@@ -1137,6 +1195,7 @@ if __name__ == "__main__":
"", "",
"Additional Builds:", "Additional Builds:",
"", "",
" docs Build the help documentation as HTML."
" qthelp Build the help documentation for use with the Qt Assistant.", " qthelp Build the help documentation for use with the Qt Assistant.",
" qtlupdate Update the translation files for internationalisation.", " qtlupdate Update the translation files for internationalisation.",
" qtlrelease Build the language files for internationalisation.", " qtlrelease Build the language files for internationalisation.",
@@ -1203,6 +1262,10 @@ if __name__ == "__main__":
# Additional Builds # Additional Builds
# ================= # =================
if "docs" in sys.argv:
sys.argv.remove("docs")
buildHtmlDocs()
if "qthelp" in sys.argv: if "qthelp" in sys.argv:
sys.argv.remove("qthelp") sys.argv.remove("qthelp")
buildQtDocs() buildQtDocs()
-10
View File
@@ -544,14 +544,4 @@ def testBaseConfig_Internal(monkeypatch, tmpConf):
tmpConf._checkOptionalPackages() tmpConf._checkOptionalPackages()
assert tmpConf.hasEnchant is False 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 # END Test testBaseConfig_Internal