src/eric7/DocumentationTools/QtHelpGenerator.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 9154
e8ca7b41a7d8
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the QtHelp generator for the builtin documentation
8 generator.
9 """
10
11 import sys
12 import os
13 import shutil
14 import subprocess # secok
15
16 from Utilities import (
17 joinext, html_encode, getQtBinariesPath, generateQtToolName
18 )
19
20 HelpCollection = r"""<?xml version="1.0" encoding="utf-8" ?>
21 <QHelpCollectionProject version="1.0">
22 <docFiles>
23 <register>
24 <file>{helpfile}</file>
25 </register>
26 </docFiles>
27 </QHelpCollectionProject>
28 """
29
30 HelpProject = r"""<?xml version="1.0" encoding="UTF-8"?>
31 <QtHelpProject version="1.0">
32 <namespace>{namespace}</namespace>
33 <virtualFolder>{folder}</virtualFolder>
34 <customFilter name="{filter_name}">
35 {filter_attributes}
36 </customFilter>
37 <filterSection>
38 {filter_attributes}
39 <toc>
40 {sections}
41 </toc>
42 <keywords>
43 {keywords}
44 </keywords>
45 <files>
46 {files}
47 </files>
48 </filterSection>
49 </QtHelpProject>
50 """
51
52 HelpProjectFile = 'source.qhp'
53 HelpHelpFile = 'source.qch'
54 HelpCollectionProjectFile = 'source.qhcp'
55 HelpCollectionFile = 'collection.qhc'
56
57
58 class QtHelpGenerator:
59 """
60 Class implementing the QtHelp generator for the builtin documentation
61 generator.
62 """
63 def __init__(self, htmlDir,
64 outputDir, namespace, virtualFolder, filterName,
65 filterAttributes, title, createCollection):
66 """
67 Constructor
68
69 @param htmlDir directory containing the HTML files (string)
70 @param outputDir output directory for the files (string)
71 @param namespace namespace to be used (string)
72 @param virtualFolder virtual folder to be used (string)
73 @param filterName name of the custom filter (string)
74 @param filterAttributes ':' separated list of filter attributes
75 (string)
76 @param title title to be used for the generated help (string)
77 @param createCollection flag indicating the generation of the
78 collection files (boolean)
79 """
80 self.htmlDir = htmlDir
81 self.outputDir = outputDir
82 self.namespace = namespace
83 self.virtualFolder = virtualFolder
84 self.filterName = filterName
85 self.filterAttributes = (
86 filterAttributes and filterAttributes.split(':') or []
87 )
88 self.relPath = os.path.relpath(self.htmlDir, self.outputDir)
89 self.title = title
90 self.createCollection = createCollection
91
92 self.packages = {
93 "00index": {
94 "subpackages": {},
95 "modules": {}
96 }
97 }
98 self.remembered = False
99 self.keywords = []
100
101 def remember(self, file, moduleDocument, basename=""):
102 """
103 Public method to remember a documentation file.
104
105 @param file The filename to be remembered. (string)
106 @param moduleDocument The ModuleDocument object containing the
107 information for the file.
108 @param basename The basename of the file hierarchy to be documented.
109 The basename is stripped off the filename if it starts with
110 the basename.
111 """
112 self.remembered = True
113 if basename:
114 file = file.replace(basename, "")
115
116 if "__init__" in file:
117 dirName = os.path.dirname(file)
118 udir = os.path.dirname(dirName)
119 if udir:
120 upackage = udir.replace(os.sep, ".")
121 try:
122 elt = self.packages[upackage]
123 except KeyError:
124 elt = self.packages["00index"]
125 else:
126 elt = self.packages["00index"]
127 package = dirName.replace(os.sep, ".")
128 elt["subpackages"][package] = moduleDocument.name()
129
130 self.packages[package] = {
131 "subpackages": {},
132 "modules": {}
133 }
134
135 kwEntry = ("{0} (Package)".format(package.split('.')[-1]),
136 joinext("index-{0}".format(package), ".html"))
137 if kwEntry not in self.keywords:
138 self.keywords.append(kwEntry)
139
140 if moduleDocument.isEmpty():
141 return
142
143 package = os.path.dirname(file).replace(os.sep, ".")
144 try:
145 elt = self.packages[package]
146 except KeyError:
147 elt = self.packages["00index"]
148 elt["modules"][moduleDocument.name()] = moduleDocument.name()
149
150 if "__init__" not in file:
151 kwEntry = (
152 "{0} (Module)".format(moduleDocument.name().split('.')[-1]),
153 joinext(moduleDocument.name(), ".html"))
154 if kwEntry not in self.keywords:
155 self.keywords.append(kwEntry)
156 for kw in moduleDocument.getQtHelpKeywords():
157 kwEntry = (kw[0], "{0}{1}".format(
158 joinext(moduleDocument.name(), ".html"), kw[1]))
159 if kwEntry not in self.keywords:
160 self.keywords.append(kwEntry)
161
162 def __generateSections(self, package, level):
163 """
164 Private method to generate the sections part.
165
166 @param package name of the package to process (string)
167 @param level indentation level (integer)
168 @return sections part (string)
169 """
170 indent = level * ' '
171 indent1 = indent + ' '
172 s = indent + '<section title="{0}" ref="{1}">\n'.format(
173 package == "00index" and self.title or package,
174 package == "00index" and
175 joinext("index", ".html") or
176 joinext("index-{0}".format(package), ".html"))
177 for subpack in sorted(self.packages[package]["subpackages"]):
178 s += self.__generateSections(subpack, level + 1) + '\n'
179 for mod in sorted(self.packages[package]["modules"]):
180 s += indent1 + '<section title="{0}" ref="{1}" />\n'.format(
181 mod, joinext(mod, ".html"))
182 s += indent + '</section>'
183 return s
184
185 def __convertEol(self, txt, newline):
186 """
187 Private method to convert the newline characters.
188
189 @param txt text to be converted (string)
190 @param newline newline character to be used (string)
191 @return converted text (string)
192 """
193 # step 1: normalize eol to '\n'
194 txt = txt.replace("\r\n", "\n").replace("\r", "\n")
195
196 # step 2: convert to the target eol
197 if newline is None:
198 return txt.replace("\n", os.linesep)
199 elif newline in ["\r", "\r\n"]:
200 return txt.replace("\n", newline)
201 else:
202 return txt
203
204 def generateFiles(self, basename="", newline=None):
205 """
206 Public method to generate all index files.
207
208 @param basename The basename of the file hierarchy to be documented.
209 The basename is stripped off the filename if it starts with
210 the basename.
211 @param newline newline character to be used (string)
212 """
213 if not self.remembered:
214 sys.stderr.write("No QtHelp to generate.\n")
215 return
216
217 if basename:
218 basename = basename.replace(os.sep, ".")
219 if not basename.endswith("."):
220 basename = "{0}.".format(basename)
221
222 sections = self.__generateSections("00index", level=3)
223 filesList = sorted(e for e in os.listdir(self.htmlDir)
224 if e.endswith('.html'))
225 filesList.append("styles.css")
226 files = "\n".join(
227 [" <file>{0}</file>".format(f) for f in filesList])
228 filterAttribs = "\n".join(
229 [" <filterAttribute>{0}</filterAttribute>".format(a)
230 for a in sorted(self.filterAttributes)])
231 keywords = "\n".join(
232 [' <keyword name="{0}" id="{1}" ref="{2}" />'.format(
233 html_encode(kw[0]), html_encode(kw[0]), html_encode(kw[1]))
234 for kw in sorted(self.keywords)])
235
236 helpAttribs = {
237 "namespace": self.namespace,
238 "folder": self.virtualFolder,
239 "filter_name": self.filterName,
240 "filter_attributes": filterAttribs,
241 "sections": sections,
242 "keywords": keywords,
243 "files": files,
244 }
245
246 txt = self.__convertEol(HelpProject.format(**helpAttribs), newline)
247 with open(os.path.join(self.outputDir, HelpProjectFile), "w",
248 encoding="utf-8", newline=newline) as f:
249 f.write(txt)
250
251 if (
252 self.createCollection and
253 not os.path.exists(os.path.join(
254 self.outputDir, HelpCollectionProjectFile))
255 ):
256 collectionAttribs = {
257 "helpfile": HelpHelpFile,
258 }
259
260 txt = self.__convertEol(
261 HelpCollection.format(**collectionAttribs), newline)
262 with open(os.path.join(self.outputDir, HelpCollectionProjectFile),
263 "w", encoding="utf-8", newline=newline) as f:
264 f.write(txt)
265
266 sys.stdout.write("QtHelp files written.\n")
267 sys.stdout.write("Generating QtHelp documentation...\n")
268 sys.stdout.flush()
269 sys.stderr.flush()
270
271 cwd = os.getcwd()
272 # generate the compressed files
273 qhelpgeneratorExe = os.path.join(
274 getQtBinariesPath(libexec=True),
275 generateQtToolName("qhelpgenerator")
276 )
277 if not os.path.exists(qhelpgeneratorExe):
278 qhelpgeneratorExe = os.path.join(
279 getQtBinariesPath(libexec=False),
280 generateQtToolName("qhelpgenerator")
281 )
282 shutil.copy(
283 os.path.join(self.outputDir, HelpProjectFile), self.htmlDir)
284 os.chdir(self.htmlDir)
285 subprocess.run([ # secok
286 qhelpgeneratorExe,
287 HelpProjectFile, "-o", os.path.join(self.outputDir, HelpHelpFile)])
288 os.remove(HelpProjectFile)
289
290 if self.createCollection:
291 sys.stdout.write("Generating QtHelp collection...\n")
292 sys.stdout.flush()
293 sys.stderr.flush()
294 os.chdir(self.outputDir)
295 subprocess.run([ # secok
296 qhelpgeneratorExe,
297 HelpCollectionProjectFile, "-o", HelpCollectionFile])
298
299 os.chdir(cwd)

eric ide

mercurial