|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2009 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a thread class populating and updating the QtHelp |
|
8 documentation database. |
|
9 """ |
|
10 |
|
11 import os |
|
12 |
|
13 from PyQt5.QtCore import ( |
|
14 pyqtSignal, QThread, Qt, QMutex, QDateTime, QDir, QLibraryInfo, QFileInfo |
|
15 ) |
|
16 from PyQt5.QtHelp import QHelpEngineCore |
|
17 |
|
18 from eric6config import getConfig |
|
19 |
|
20 from Globals import qVersionTuple |
|
21 |
|
22 |
|
23 class HelpDocsInstaller(QThread): |
|
24 """ |
|
25 Class implementing the worker thread populating and updating the QtHelp |
|
26 documentation database. |
|
27 |
|
28 @signal errorMessage(str) emitted, if an error occurred during |
|
29 the installation of the documentation |
|
30 @signal docsInstalled(bool) emitted after the installation has finished |
|
31 """ |
|
32 errorMessage = pyqtSignal(str) |
|
33 docsInstalled = pyqtSignal(bool) |
|
34 |
|
35 def __init__(self, collection): |
|
36 """ |
|
37 Constructor |
|
38 |
|
39 @param collection full pathname of the collection file (string) |
|
40 """ |
|
41 super().__init__() |
|
42 |
|
43 self.__abort = False |
|
44 self.__collection = collection |
|
45 self.__mutex = QMutex() |
|
46 |
|
47 def stop(self): |
|
48 """ |
|
49 Public slot to stop the installation procedure. |
|
50 """ |
|
51 if not self.isRunning(): |
|
52 return |
|
53 |
|
54 self.__mutex.lock() |
|
55 self.__abort = True |
|
56 self.__mutex.unlock() |
|
57 self.wait() |
|
58 |
|
59 def installDocs(self): |
|
60 """ |
|
61 Public method to start the installation procedure. |
|
62 """ |
|
63 self.start(QThread.Priority.LowPriority) |
|
64 |
|
65 def run(self): |
|
66 """ |
|
67 Public method executed by the thread. |
|
68 """ |
|
69 engine = QHelpEngineCore(self.__collection) |
|
70 changes = False |
|
71 |
|
72 qt5Docs = [ |
|
73 "activeqt", "qdoc", "qmake", "qt3d", "qt3drenderer", |
|
74 "qtandroidextras", "qtassistant", "qtbluetooth", "qtcanvas3d", |
|
75 "qtcharts", "qtconcurrent", "qtcore", "qtdatavisualization", |
|
76 "qtdbus", "qtdesigner", "qtdistancefieldgenerator", "qtdoc", |
|
77 "qtenginio", "qtenginiooverview", "qtenginoqml", "qtgamepad", |
|
78 "qtgraphicaleffects", "qtgui", "qthelp", "qtimageformats", |
|
79 "qtlabscalendar", "qtlabsplatform", "qtlabscontrols", "qtlinguist", |
|
80 "qtlocation", "qtlottieanimation", "qtmaxextras", "qtmultimedia", |
|
81 "qtmultimediawidgets", "qtnetwork", "qtnetworkauth", "qtnfc", |
|
82 "qtopengl", "qtplatformheaders", "qtpositioning", "qtprintsupport", |
|
83 "qtpurchasing", "qtqml", "qtqmltest", "qtquick", "qtquickcontrols", |
|
84 "qtquickcontrols1", "qtquickdialogs", "qtquickextras", |
|
85 "qtquicklayouts", "qtremoteobjects", "qtscript", "qtscripttools", |
|
86 "qtscxml", "qtsensors", "qtserialbus", "qtserialport", "qtspeech", |
|
87 "qtsql", "qtsvg", "qttest", "qttestlib", "qtuitools", |
|
88 "qtvirtualkeyboard", "qtwaylandcompositor", "qtwebchannel", |
|
89 "qtwebengine", "qtwebenginewidgets", "qtwebkit", |
|
90 "qtwebkitexamples", "qtwebsockets", "qtwebview", "qtwidgets", |
|
91 "qtwinextras", "qtx11extras", "qtxml", "qtxmlpatterns"] |
|
92 for qtDocs, version in [(qt5Docs, 5)]: |
|
93 for doc in qtDocs: |
|
94 changes |= self.__installQtDoc(doc, version, engine) |
|
95 self.__mutex.lock() |
|
96 if self.__abort: |
|
97 engine = None |
|
98 self.__mutex.unlock() |
|
99 return |
|
100 self.__mutex.unlock() |
|
101 |
|
102 changes |= self.__installEric6Doc(engine) |
|
103 engine = None |
|
104 del engine |
|
105 self.docsInstalled.emit(changes) |
|
106 |
|
107 def __installQtDoc(self, name, version, engine): |
|
108 """ |
|
109 Private method to install/update a Qt help document. |
|
110 |
|
111 @param name name of the Qt help document (string) |
|
112 @param version Qt version of the help documens (integer) |
|
113 @param engine reference to the help engine (QHelpEngineCore) |
|
114 @return flag indicating success (boolean) |
|
115 """ |
|
116 versionKey = "qt_version_{0}@@{1}".format(version, name) |
|
117 info = engine.customValue(versionKey, "") |
|
118 lst = info.split('|') |
|
119 |
|
120 dt = QDateTime() |
|
121 if len(lst) and lst[0]: |
|
122 dt = QDateTime.fromString(lst[0], Qt.DateFormat.ISODate) |
|
123 |
|
124 qchFile = "" |
|
125 if len(lst) == 2: |
|
126 qchFile = lst[1] |
|
127 |
|
128 if version == 4: |
|
129 docsPath = QDir( |
|
130 QLibraryInfo.location( |
|
131 QLibraryInfo.LibraryLocation.DocumentationPath) + |
|
132 QDir.separator() + "qch") |
|
133 elif version == 5: |
|
134 docsPath = QLibraryInfo.location( |
|
135 QLibraryInfo.LibraryLocation.DocumentationPath) |
|
136 if ( |
|
137 not os.path.isdir(docsPath) or |
|
138 len(QDir(docsPath).entryList(["*.qch"])) == 0 |
|
139 ): |
|
140 docsPathList = QDir.fromNativeSeparators(docsPath).split("/") |
|
141 docsPath = os.sep.join( |
|
142 docsPathList[:-3] + |
|
143 ["Docs", "Qt-{0}.{1}".format(*qVersionTuple())]) |
|
144 docsPath = QDir(docsPath) |
|
145 else: |
|
146 # unsupported Qt version |
|
147 return False |
|
148 |
|
149 files = docsPath.entryList(["*.qch"]) |
|
150 if not files: |
|
151 engine.setCustomValue( |
|
152 versionKey, |
|
153 QDateTime().toString(Qt.DateFormat.ISODate) + '|') |
|
154 return False |
|
155 |
|
156 for f in files: |
|
157 if f.startswith(name + "."): |
|
158 fi = QFileInfo(docsPath.absolutePath() + QDir.separator() + f) |
|
159 namespace = QHelpEngineCore.namespaceName( |
|
160 fi.absoluteFilePath()) |
|
161 if not namespace: |
|
162 continue |
|
163 |
|
164 if ( |
|
165 dt.isValid() and |
|
166 namespace in engine.registeredDocumentations() and |
|
167 (fi.lastModified().toString(Qt.DateFormat.ISODate) == |
|
168 dt.toString(Qt.DateFormat.ISODate)) and |
|
169 qchFile == fi.absoluteFilePath() |
|
170 ): |
|
171 return False |
|
172 |
|
173 if namespace in engine.registeredDocumentations(): |
|
174 engine.unregisterDocumentation(namespace) |
|
175 |
|
176 if not engine.registerDocumentation(fi.absoluteFilePath()): |
|
177 self.errorMessage.emit( |
|
178 self.tr( |
|
179 """<p>The file <b>{0}</b> could not be""" |
|
180 """ registered. <br/>Reason: {1}</p>""") |
|
181 .format(fi.absoluteFilePath, engine.error()) |
|
182 ) |
|
183 return False |
|
184 |
|
185 engine.setCustomValue( |
|
186 versionKey, |
|
187 fi.lastModified().toString(Qt.DateFormat.ISODate) + '|' + |
|
188 fi.absoluteFilePath()) |
|
189 return True |
|
190 |
|
191 return False |
|
192 |
|
193 def __installEric6Doc(self, engine): |
|
194 """ |
|
195 Private method to install/update the eric help documentation. |
|
196 |
|
197 @param engine reference to the help engine (QHelpEngineCore) |
|
198 @return flag indicating success (boolean) |
|
199 """ |
|
200 versionKey = "eric6_ide" |
|
201 info = engine.customValue(versionKey, "") |
|
202 lst = info.split('|') |
|
203 |
|
204 dt = QDateTime() |
|
205 if len(lst) and lst[0]: |
|
206 dt = QDateTime.fromString(lst[0], Qt.DateFormat.ISODate) |
|
207 |
|
208 qchFile = "" |
|
209 if len(lst) == 2: |
|
210 qchFile = lst[1] |
|
211 |
|
212 docsPath = QDir(getConfig("ericDocDir") + QDir.separator() + "Help") |
|
213 |
|
214 files = docsPath.entryList(["*.qch"]) |
|
215 if not files: |
|
216 engine.setCustomValue( |
|
217 versionKey, QDateTime().toString(Qt.DateFormat.ISODate) + '|') |
|
218 return False |
|
219 |
|
220 for f in files: |
|
221 if f == "source.qch": |
|
222 fi = QFileInfo(docsPath.absolutePath() + QDir.separator() + f) |
|
223 namespace = QHelpEngineCore.namespaceName( |
|
224 fi.absoluteFilePath()) |
|
225 if not namespace: |
|
226 continue |
|
227 |
|
228 if ( |
|
229 dt.isValid() and |
|
230 namespace in engine.registeredDocumentations() and |
|
231 (fi.lastModified().toString(Qt.DateFormat.ISODate) == |
|
232 dt.toString(Qt.DateFormat.ISODate)) and |
|
233 qchFile == fi.absoluteFilePath() |
|
234 ): |
|
235 return False |
|
236 |
|
237 if namespace in engine.registeredDocumentations(): |
|
238 engine.unregisterDocumentation(namespace) |
|
239 |
|
240 if not engine.registerDocumentation(fi.absoluteFilePath()): |
|
241 self.errorMessage.emit( |
|
242 self.tr( |
|
243 """<p>The file <b>{0}</b> could not be""" |
|
244 """ registered. <br/>Reason: {1}</p>""") |
|
245 .format(fi.absoluteFilePath, engine.error()) |
|
246 ) |
|
247 return False |
|
248 |
|
249 engine.setCustomValue( |
|
250 versionKey, |
|
251 fi.lastModified().toString(Qt.DateFormat.ISODate) + '|' + |
|
252 fi.absoluteFilePath()) |
|
253 return True |
|
254 |
|
255 return False |