--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric7/WebBrowser/QtHelp/HelpDocsInstaller.py Sat May 15 18:45:04 2021 +0200 @@ -0,0 +1,255 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2009 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a thread class populating and updating the QtHelp +documentation database. +""" + +import os + +from PyQt5.QtCore import ( + pyqtSignal, QThread, Qt, QMutex, QDateTime, QDir, QLibraryInfo, QFileInfo +) +from PyQt5.QtHelp import QHelpEngineCore + +from eric6config import getConfig + +from Globals import qVersionTuple + + +class HelpDocsInstaller(QThread): + """ + Class implementing the worker thread populating and updating the QtHelp + documentation database. + + @signal errorMessage(str) emitted, if an error occurred during + the installation of the documentation + @signal docsInstalled(bool) emitted after the installation has finished + """ + errorMessage = pyqtSignal(str) + docsInstalled = pyqtSignal(bool) + + def __init__(self, collection): + """ + Constructor + + @param collection full pathname of the collection file (string) + """ + super().__init__() + + self.__abort = False + self.__collection = collection + self.__mutex = QMutex() + + def stop(self): + """ + Public slot to stop the installation procedure. + """ + if not self.isRunning(): + return + + self.__mutex.lock() + self.__abort = True + self.__mutex.unlock() + self.wait() + + def installDocs(self): + """ + Public method to start the installation procedure. + """ + self.start(QThread.Priority.LowPriority) + + def run(self): + """ + Public method executed by the thread. + """ + engine = QHelpEngineCore(self.__collection) + changes = False + + qt5Docs = [ + "activeqt", "qdoc", "qmake", "qt3d", "qt3drenderer", + "qtandroidextras", "qtassistant", "qtbluetooth", "qtcanvas3d", + "qtcharts", "qtconcurrent", "qtcore", "qtdatavisualization", + "qtdbus", "qtdesigner", "qtdistancefieldgenerator", "qtdoc", + "qtenginio", "qtenginiooverview", "qtenginoqml", "qtgamepad", + "qtgraphicaleffects", "qtgui", "qthelp", "qtimageformats", + "qtlabscalendar", "qtlabsplatform", "qtlabscontrols", "qtlinguist", + "qtlocation", "qtlottieanimation", "qtmaxextras", "qtmultimedia", + "qtmultimediawidgets", "qtnetwork", "qtnetworkauth", "qtnfc", + "qtopengl", "qtplatformheaders", "qtpositioning", "qtprintsupport", + "qtpurchasing", "qtqml", "qtqmltest", "qtquick", "qtquickcontrols", + "qtquickcontrols1", "qtquickdialogs", "qtquickextras", + "qtquicklayouts", "qtremoteobjects", "qtscript", "qtscripttools", + "qtscxml", "qtsensors", "qtserialbus", "qtserialport", "qtspeech", + "qtsql", "qtsvg", "qttest", "qttestlib", "qtuitools", + "qtvirtualkeyboard", "qtwaylandcompositor", "qtwebchannel", + "qtwebengine", "qtwebenginewidgets", "qtwebkit", + "qtwebkitexamples", "qtwebsockets", "qtwebview", "qtwidgets", + "qtwinextras", "qtx11extras", "qtxml", "qtxmlpatterns"] + for qtDocs, version in [(qt5Docs, 5)]: + for doc in qtDocs: + changes |= self.__installQtDoc(doc, version, engine) + self.__mutex.lock() + if self.__abort: + engine = None + self.__mutex.unlock() + return + self.__mutex.unlock() + + changes |= self.__installEric6Doc(engine) + engine = None + del engine + self.docsInstalled.emit(changes) + + def __installQtDoc(self, name, version, engine): + """ + Private method to install/update a Qt help document. + + @param name name of the Qt help document (string) + @param version Qt version of the help documens (integer) + @param engine reference to the help engine (QHelpEngineCore) + @return flag indicating success (boolean) + """ + versionKey = "qt_version_{0}@@{1}".format(version, name) + info = engine.customValue(versionKey, "") + lst = info.split('|') + + dt = QDateTime() + if len(lst) and lst[0]: + dt = QDateTime.fromString(lst[0], Qt.DateFormat.ISODate) + + qchFile = "" + if len(lst) == 2: + qchFile = lst[1] + + if version == 4: + docsPath = QDir( + QLibraryInfo.location( + QLibraryInfo.LibraryLocation.DocumentationPath) + + QDir.separator() + "qch") + elif version == 5: + docsPath = QLibraryInfo.location( + QLibraryInfo.LibraryLocation.DocumentationPath) + if ( + not os.path.isdir(docsPath) or + len(QDir(docsPath).entryList(["*.qch"])) == 0 + ): + docsPathList = QDir.fromNativeSeparators(docsPath).split("/") + docsPath = os.sep.join( + docsPathList[:-3] + + ["Docs", "Qt-{0}.{1}".format(*qVersionTuple())]) + docsPath = QDir(docsPath) + else: + # unsupported Qt version + return False + + files = docsPath.entryList(["*.qch"]) + if not files: + engine.setCustomValue( + versionKey, + QDateTime().toString(Qt.DateFormat.ISODate) + '|') + return False + + for f in files: + if f.startswith(name + "."): + fi = QFileInfo(docsPath.absolutePath() + QDir.separator() + f) + namespace = QHelpEngineCore.namespaceName( + fi.absoluteFilePath()) + if not namespace: + continue + + if ( + dt.isValid() and + namespace in engine.registeredDocumentations() and + (fi.lastModified().toString(Qt.DateFormat.ISODate) == + dt.toString(Qt.DateFormat.ISODate)) and + qchFile == fi.absoluteFilePath() + ): + return False + + if namespace in engine.registeredDocumentations(): + engine.unregisterDocumentation(namespace) + + if not engine.registerDocumentation(fi.absoluteFilePath()): + self.errorMessage.emit( + self.tr( + """<p>The file <b>{0}</b> could not be""" + """ registered. <br/>Reason: {1}</p>""") + .format(fi.absoluteFilePath, engine.error()) + ) + return False + + engine.setCustomValue( + versionKey, + fi.lastModified().toString(Qt.DateFormat.ISODate) + '|' + + fi.absoluteFilePath()) + return True + + return False + + def __installEric6Doc(self, engine): + """ + Private method to install/update the eric help documentation. + + @param engine reference to the help engine (QHelpEngineCore) + @return flag indicating success (boolean) + """ + versionKey = "eric6_ide" + info = engine.customValue(versionKey, "") + lst = info.split('|') + + dt = QDateTime() + if len(lst) and lst[0]: + dt = QDateTime.fromString(lst[0], Qt.DateFormat.ISODate) + + qchFile = "" + if len(lst) == 2: + qchFile = lst[1] + + docsPath = QDir(getConfig("ericDocDir") + QDir.separator() + "Help") + + files = docsPath.entryList(["*.qch"]) + if not files: + engine.setCustomValue( + versionKey, QDateTime().toString(Qt.DateFormat.ISODate) + '|') + return False + + for f in files: + if f == "source.qch": + fi = QFileInfo(docsPath.absolutePath() + QDir.separator() + f) + namespace = QHelpEngineCore.namespaceName( + fi.absoluteFilePath()) + if not namespace: + continue + + if ( + dt.isValid() and + namespace in engine.registeredDocumentations() and + (fi.lastModified().toString(Qt.DateFormat.ISODate) == + dt.toString(Qt.DateFormat.ISODate)) and + qchFile == fi.absoluteFilePath() + ): + return False + + if namespace in engine.registeredDocumentations(): + engine.unregisterDocumentation(namespace) + + if not engine.registerDocumentation(fi.absoluteFilePath()): + self.errorMessage.emit( + self.tr( + """<p>The file <b>{0}</b> could not be""" + """ registered. <br/>Reason: {1}</p>""") + .format(fi.absoluteFilePath, engine.error()) + ) + return False + + engine.setCustomValue( + versionKey, + fi.lastModified().toString(Qt.DateFormat.ISODate) + '|' + + fi.absoluteFilePath()) + return True + + return False