|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2009 - 2022 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 datetime |
|
12 import pathlib |
|
13 |
|
14 from PyQt6.QtCore import pyqtSignal, QThread, QMutex, QLibraryInfo |
|
15 from PyQt6.QtHelp import QHelpEngineCore |
|
16 |
|
17 from eric7config import getConfig |
|
18 |
|
19 from Globals import qVersionTuple |
|
20 |
|
21 |
|
22 class HelpDocsInstaller(QThread): |
|
23 """ |
|
24 Class implementing the worker thread populating and updating the QtHelp |
|
25 documentation database. |
|
26 |
|
27 @signal errorMessage(str) emitted, if an error occurred during |
|
28 the installation of the documentation |
|
29 @signal docsInstalled(bool) emitted after the installation has finished |
|
30 """ |
|
31 errorMessage = pyqtSignal(str) |
|
32 docsInstalled = pyqtSignal(bool) |
|
33 |
|
34 def __init__(self, collection): |
|
35 """ |
|
36 Constructor |
|
37 |
|
38 @param collection full pathname of the collection file |
|
39 @type str |
|
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", "qtcmake", "qtconcurrent", "qtcore", "qtdatavis3d", |
|
76 "qtdatavisualization", "qtdbus", "qtdesigner", |
|
77 "qtdistancefieldgenerator", "qtdoc", "qtenginio", |
|
78 "qtenginiooverview", "qtenginoqml", "qtgamepad", |
|
79 "qtgraphicaleffects", "qtgui", "qthelp", "qtimageformats", |
|
80 "qtlabscalendar", "qtlabsplatform", "qtlabscontrols", "qtlinguist", |
|
81 "qtlocation", "qtlottieanimation", "qtmaxextras", "qtmultimedia", |
|
82 "qtmultimediawidgets", "qtnetwork", "qtnetworkauth", "qtnfc", |
|
83 "qtopengl", "qtplatformheaders", "qtpositioning", "qtprintsupport", |
|
84 "qtpurchasing", "qtqml", "qtqmlmodels", "qtqmltest", "qtquick", |
|
85 "qtquick3d", "qtquickcontrols", "qtquickcontrols1", |
|
86 "qtquickdialogs", "qtquickextras", "qtquicklayouts", |
|
87 "qtquicktimeline", "qtremoteobjects", "qtscript", "qtscripttools", |
|
88 "qtscxml", "qtsensors", "qtserialbus", "qtserialport", |
|
89 "qtshadertools", "qtspeech", "qtsql", "qtsvg", "qttest", |
|
90 "qttestlib", "qtuitools", "qtvirtualkeyboard", |
|
91 "qtwaylandcompositor", "qtwebchannel", "qtwebengine", |
|
92 "qtwebenginewidgets", "qtwebkit", "qtwebkitexamples", |
|
93 "qtwebsockets", "qtwebview", "qtwidgets", "qtwinextras", |
|
94 "qtx11extras", "qtxml", "qtxmlpatterns"] |
|
95 for qtDocs, version in [(qt5Docs, 5)]: |
|
96 for doc in qtDocs: |
|
97 changes |= self.__installQtDoc(doc, version, engine) |
|
98 self.__mutex.lock() |
|
99 if self.__abort: |
|
100 engine = None |
|
101 self.__mutex.unlock() |
|
102 return |
|
103 self.__mutex.unlock() |
|
104 |
|
105 changes |= self.__installEric7Doc(engine) |
|
106 engine = None |
|
107 del engine |
|
108 self.docsInstalled.emit(changes) |
|
109 |
|
110 def __installQtDoc(self, name, version, engine): |
|
111 """ |
|
112 Private method to install/update a Qt help document. |
|
113 |
|
114 @param name name of the Qt help document |
|
115 @type str |
|
116 @param version Qt version of the help documents |
|
117 @type int |
|
118 @param engine reference to the help engine |
|
119 @type QHelpEngineCore |
|
120 @return flag indicating success |
|
121 @rtype bool |
|
122 """ |
|
123 versionKey = "qt_version_{0}@@{1}".format(version, name) |
|
124 info = engine.customValue(versionKey, "") |
|
125 lst = info.split('|') |
|
126 |
|
127 dt = None |
|
128 if len(lst) and lst[0]: |
|
129 dt = datetime.datetime.fromisoformat(lst[0]) |
|
130 |
|
131 qchFile = "" |
|
132 if len(lst) == 2: |
|
133 qchFile = lst[1] |
|
134 |
|
135 if version == 5: |
|
136 docsPath = pathlib.Path(QLibraryInfo.path( |
|
137 QLibraryInfo.LibraryPath.DocumentationPath)) |
|
138 if ( |
|
139 not docsPath.is_dir() or |
|
140 len(docsPath.glob("*.qch")) == 0 |
|
141 ): |
|
142 docsPath = ( |
|
143 docsPath.parents[2] / "Docs" / |
|
144 "Qt-{0}.{1}".format(*qVersionTuple()) |
|
145 ) |
|
146 else: |
|
147 # unsupported Qt version |
|
148 return False |
|
149 |
|
150 files = docsPath.glob("*.qch") |
|
151 if not files: |
|
152 engine.setCustomValue(versionKey, '|') |
|
153 return False |
|
154 |
|
155 for f in files: |
|
156 if f.stem == name: |
|
157 namespace = QHelpEngineCore.namespaceName(str(f.resolve())) |
|
158 if not namespace: |
|
159 continue |
|
160 |
|
161 if ( |
|
162 dt is not None and |
|
163 namespace in engine.registeredDocumentations() and |
|
164 (datetime.datetime.fromtimestamp(f.stat().st_mtime) == |
|
165 dt) and |
|
166 qchFile == str(f.resolve()) |
|
167 ): |
|
168 return False |
|
169 |
|
170 if namespace in engine.registeredDocumentations(): |
|
171 engine.unregisterDocumentation(namespace) |
|
172 |
|
173 if not engine.registerDocumentation(str(f.resolve())): |
|
174 self.errorMessage.emit( |
|
175 self.tr( |
|
176 """<p>The file <b>{0}</b> could not be""" |
|
177 """ registered. <br/>Reason: {1}</p>""") |
|
178 .format(f, engine.error()) |
|
179 ) |
|
180 return False |
|
181 |
|
182 engine.setCustomValue( |
|
183 versionKey, |
|
184 datetime.datetime.fromtimestamp(f.stat().st_mtime) |
|
185 .isoformat() + '|' + str(f.resolve())) |
|
186 return True |
|
187 |
|
188 return False |
|
189 |
|
190 def __installEric7Doc(self, engine): |
|
191 """ |
|
192 Private method to install/update the eric help documentation. |
|
193 |
|
194 @param engine reference to the help engine |
|
195 @type QHelpEngineCore |
|
196 @return flag indicating success |
|
197 @rtype bool |
|
198 """ |
|
199 versionKey = "eric7_ide" |
|
200 info = engine.customValue(versionKey, "") |
|
201 lst = info.split('|') |
|
202 |
|
203 dt = None |
|
204 if len(lst) and lst[0]: |
|
205 dt = datetime.datetime.fromisoformat(lst[0]) |
|
206 |
|
207 qchFile = "" |
|
208 if len(lst) == 2: |
|
209 qchFile = lst[1] |
|
210 |
|
211 docsPath = pathlib.Path(getConfig("ericDocDir")) / "Help" |
|
212 |
|
213 files = docsPath.glob("*.qch") |
|
214 if not files: |
|
215 engine.setCustomValue(versionKey, '|') |
|
216 return False |
|
217 |
|
218 for f in files: |
|
219 if f.name == "source.qch": |
|
220 namespace = QHelpEngineCore.namespaceName(str(f.resolve())) |
|
221 if not namespace: |
|
222 continue |
|
223 |
|
224 if ( |
|
225 dt is not None and |
|
226 namespace in engine.registeredDocumentations() and |
|
227 (datetime.datetime.fromtimestamp(f.stat().st_mtime) == |
|
228 dt) and |
|
229 qchFile == str(f.resolve()) |
|
230 ): |
|
231 return False |
|
232 |
|
233 if namespace in engine.registeredDocumentations(): |
|
234 engine.unregisterDocumentation(namespace) |
|
235 |
|
236 if not engine.registerDocumentation(str(f.resolve())): |
|
237 self.errorMessage.emit( |
|
238 self.tr( |
|
239 """<p>The file <b>{0}</b> could not be""" |
|
240 """ registered. <br/>Reason: {1}</p>""") |
|
241 .format(f, engine.error()) |
|
242 ) |
|
243 return False |
|
244 |
|
245 engine.setCustomValue( |
|
246 versionKey, |
|
247 datetime.datetime.fromtimestamp(f.stat().st_mtime) |
|
248 .isoformat() + '|' + str(f.resolve())) |
|
249 return True |
|
250 |
|
251 return False |