From eee0f5e4c928ab932fd56a601c7385884787f051 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Wed, 18 Aug 2021 00:13:28 +0200
Subject: [PATCH] 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
---
.gitignore | 1 +
docs/source/int_started.rst | 21 +++----
nw/config.py | 18 +-----
nw/gui/mainmenu.py | 95 ++++++++++++-----------------
nw/guimain.py | 5 +-
setup.py | 63 +++++++++++++++++++
tests/test_base/test_base_config.py | 10 ---
7 files changed, 111 insertions(+), 102 deletions(-)
diff --git a/.gitignore b/.gitignore
index 5ad24bbb..1b03d0dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,7 @@ i18n/*.qph
# Documentation
/docs/build/
+/nw/assets/help/html/
*.qch
*.qhc
diff --git a/docs/source/int_started.rst b/docs/source/int_started.rst
index cf48a76b..51384019 100644
--- a/docs/source/int_started.rst
+++ b/docs/source/int_started.rst
@@ -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.
diff --git a/nw/config.py b/nw/config.py
index ccead708..031449fe 100644
--- a/nw/config.py
+++ b/nw/config.py
@@ -26,7 +26,6 @@ along with this program. If not, see .
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
diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py
index 608c7c61..c80ffa46 100644
--- a/nw/gui/mainmenu.py
+++ b/nw/gui/mainmenu.py
@@ -23,10 +23,15 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
+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()
diff --git a/nw/guimain.py b/nw/guimain.py
index 0e79a89e..add555f4 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -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
diff --git a/setup.py b/setup.py
index 238590b7..590c0f4a 100755
--- a/setup.py
+++ b/setup.py
@@ -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()
diff --git a/tests/test_base/test_base_config.py b/tests/test_base/test_base_config.py
index 6dff0402..9ac9c488 100644
--- a/tests/test_base/test_base_config.py
+++ b/tests/test_base/test_base_config.py
@@ -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