mantid
mantid copied to clipboard
Remove qtassistant and reduce size of mantiddocs package
The current mantid help system uses qtassistant to show the help documentation. This method of including the docs is always in agreement with the code. However, there are some problems with this approach:
- The mantiddocs package is ~200MB. A large portion of this is because qtassistant wants the docs to be in a compressed archive and unpacked.
- qtassistant doesn't support javascript. javascript support would allow rendering equations in the application (rather than converting them to png via latex) and allow for sphinx plugins.
- The online docs are a separate build, with separate artifacts, from the offline docs
Describe the solution you'd like
Moving to qwebengine is the preferred approach. This will allow for richer HTML pages rendered in the help window which gets rid of a ton of images in the docs package.
There is a desire to make the mantiddocs package optional and use the versioned online help if it isn't installed.
Describe alternatives you've considered
qtwebview might be able to do it
This should take care of #34739
A Case for QtWebView
As per Pete's request, I will be do a brief overview of qtwebview to showcase its features, it's limitations, and give an example of a very basic implementation within another software package called SNAPRed.
Documentation Reference
Here is a link to the qtwebview doc page for Qt version 5.15.xx:
https://doc.qt.io/qt-5/qtwebview-index.html
Qtwebview is also supported within Qt version 6.7.x and here is a link to that documentation page:
https://doc.qt.io/qt-6/qtwebview-index.html
Prerequisites for Integration:
Addition of PyQtWebEngine within the conda environment:
- pyqtwebengine
Basic Overview of QtWebView:
QtWebView is a component within the Qt framework that enables the display of web content within QML applications. This viewer is lightweight and has no requirement of a complete web browser setup. QtWebView can be employed using either local built html files or web based URL addresses. Found at this ink is an overview of all the members available.
Two particularly notable methods include:
runJavaScript(string script, variant callback)
- "Runs the specified JavaScript. In case a callback function is provided, it will be invoked after the script finished running."
loadHtml(string html, url baseUrl)
- "Loads the specified html content to the web view."
Note: WebView does not support loading content through the Qt Resource System.
Basic Implementation within SNAPRed:
from PyQt5.QtCore import QUrl
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QHBoxLayout, QPushButton, QWidget
from snapred.meta.Config import Config
class UserDocsButton(QWidget):
def __init__(self, parent=None):
super(UserDocsButton, self).__init__(parent)
self.setStyleSheet("background-color: #F5E9E2;")
self.initUI()
def initUI(self):
# Layout setup
layout = QHBoxLayout(self)
self.setLayout(layout)
# Button to launch the web view
self.button = QPushButton("User Documentation", self)
self.button.setStyleSheet("background-color: #F5E9E2;")
self.button.clicked.connect(self.launchWebView)
layout.addWidget(self.button)
def launchWebView(self):
# Create and configure the web view
self.webView = QWebEngineView()
self.webView.setWindowTitle("Documentation")
self.webView.resize(800, 600)
# Point to the specific file on the filesystem
url = QUrl.fromLocalFile(str(Config["docs.user.path"]))
self.webView.setUrl(url)
# Show the web view
self.webView.show()
In the example above a widget is implemented within the PyQt framework to enable the users to access the documentation through the UI. This script defines a simple button and connects it to a method which launches an instance of QWebEngineView with a specified target pointing to a local system html file. Loading from a web based URL would be very similar and should be as simple as changing a single line.
Result:
Additional UI features for the web viewer can be added.
QtWebEngine needs to get imported before the QApplication is created. This will have to happen in the startup code for workbench. To see the error
from qtpy import QtWidgets, QtCore
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
app = QtWidgets.QApplication([''])
from qtpy.QtWebEngineWidgets import QtWebChannel, QWebEngineView
reversing the order of the last two lines fixes the issue.
Here is a prototype for this implementation. Please look at the attached files. There is still some work to be done to find the solution between good rendering of the equations and retaining the layout of the doc pages.
Please take a look at these:
Script
import sys
import os
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QPushButton
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor
from http.server import SimpleHTTPRequestHandler, HTTPServer
import threading
class MyRequestInterceptor(QWebEngineUrlRequestInterceptor):
def interceptRequest(self, info):
url = info.requestUrl()
if url.host() == "cdn.jsdelivr.net":
info.setHttpHeader(b"Access-Control-Allow-Origin", b"*")
class HelpWindow(QWidget):
def __init__(self, rootDir, parent=None):
super().__init__(parent)
self.setWindowTitle('Mantid Help Window')
self.setGeometry(300, 100, 1200, 800)
# Assuming the HTML files are directly in the rootDir
self.convertDiffcalPath = "http://localhost:8000/ConvertDiffCal.html"
self.anotherPagePath = "http://localhost:8000/EstimateResolutionDiffraction.html"
layout = QVBoxLayout(self)
self.webView = QWebEngineView()
# Create and set the request interceptor
interceptor = MyRequestInterceptor()
self.webView.page().profile().setUrlRequestInterceptor(interceptor)
layout.addWidget(self.webView)
self.toggleButton = QPushButton('Go to another page')
self.toggleButton.clicked.connect(self.togglePage)
layout.addWidget(self.toggleButton)
self.currentPage = 'convertDiffcal'
self.loadPage(self.convertDiffcalPath)
def loadPage(self, url):
self.webView.setUrl(QUrl(url))
def togglePage(self):
if self.currentPage == 'convertDiffcal':
self.loadPage(self.anotherPagePath)
self.currentPage = 'anotherPage'
self.toggleButton.setText('Go back to first page')
else:
self.loadPage(self.convertDiffcalPath)
self.currentPage = 'convertDiffcal'
self.toggleButton.setText('Go to another page')
def startLocalServer(rootDir):
os.chdir(rootDir)
handler = SimpleHTTPRequestHandler
httpd = HTTPServer(('localhost', 8000), handler)
httpd.serve_forever()
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: python script.py /path/to/docs/html/")
sys.exit(1)
ROOT_DIR = sys.argv[1]
# Start the local server in a separate thread
serverThread = threading.Thread(target=startLocalServer, args=(ROOT_DIR,), daemon=True)
serverThread.start()
app = QApplication(sys.argv)
# Launch the Help Window
mainWin = HelpWindow(ROOT_DIR)
mainWin.show()
sys.exit(app.exec_())
boost::python 1.84 docs for calling python functions and methods
This isn't finished