eric6/DocumentationTools/QtHelpGenerator.py

changeset 6942
2602857055c5
parent 6666
06f6e0f24e89
child 7229
53054eb5b15a
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the QtHelp generator for the builtin documentation
8 generator.
9 """
10
11 from __future__ import unicode_literals
12
13 import sys
14 import os
15 import shutil
16 import subprocess
17
18 from Utilities import joinext, relpath, html_encode, getQtBinariesPath, \
19 generateQtToolName, isExecutable
20
21 HelpCollection = r"""<?xml version="1.0" encoding="utf-8" ?>
22 <QHelpCollectionProject version="1.0">
23 <docFiles>
24 <register>
25 <file>{helpfile}</file>
26 </register>
27 </docFiles>
28 </QHelpCollectionProject>
29 """
30
31 HelpProject = r"""<?xml version="1.0" encoding="UTF-8"?>
32 <QtHelpProject version="1.0">
33 <namespace>{namespace}</namespace>
34 <virtualFolder>{folder}</virtualFolder>
35 <customFilter name="{filter_name}">
36 {filter_attributes}
37 </customFilter>
38 <filterSection>
39 {filter_attributes}
40 <toc>
41 {sections}
42 </toc>
43 <keywords>
44 {keywords}
45 </keywords>
46 <files>
47 {files}
48 </files>
49 </filterSection>
50 </QtHelpProject>
51 """
52
53 HelpProjectFile = 'source.qhp'
54 HelpHelpFile = 'source.qch'
55 HelpCollectionProjectFile = 'source.qhcp'
56 HelpCollectionFile = 'collection.qhc'
57
58
59 class QtHelpGenerator(object):
60 """
61 Class implementing the QtHelp generator for the builtin documentation
62 generator.
63 """
64 def __init__(self, htmlDir,
65 outputDir, namespace, virtualFolder, filterName,
66 filterAttributes, title, createCollection):
67 """
68 Constructor
69
70 @param htmlDir directory containing the HTML files (string)
71 @param outputDir output directory for the files (string)
72 @param namespace namespace to be used (string)
73 @param virtualFolder virtual folder to be used (string)
74 @param filterName name of the custom filter (string)
75 @param filterAttributes ':' separated list of filter attributes
76 (string)
77 @param title title to be used for the generated help (string)
78 @param createCollection flag indicating the generation of the
79 collection files (boolean)
80 """
81 self.htmlDir = htmlDir
82 self.outputDir = outputDir
83 self.namespace = namespace
84 self.virtualFolder = virtualFolder
85 self.filterName = filterName
86 self.filterAttributes = \
87 filterAttributes and filterAttributes.split(':') or []
88 self.relPath = 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)
179 s += '\n'
180 for mod in sorted(self.packages[package]["modules"]):
181 s += indent1 + '<section title="{0}" ref="{1}" />\n'.format(
182 mod, joinext(mod, ".html"))
183 s += indent + '</section>'
184 return s
185
186 def __convertEol(self, txt, newline):
187 """
188 Private method to convert the newline characters.
189
190 @param txt text to be converted (string)
191 @param newline newline character to be used (string)
192 @return converted text (string)
193 """
194 # step 1: normalize eol to '\n'
195 txt = txt.replace("\r\n", "\n").replace("\r", "\n")
196
197 # step 2: convert to the target eol
198 if newline is None:
199 return txt.replace("\n", os.linesep)
200 elif newline in ["\r", "\r\n"]:
201 return txt.replace("\n", newline)
202 else:
203 return txt
204
205 def generateFiles(self, basename="", newline=None):
206 """
207 Public method to generate all index files.
208
209 @param basename The basename of the file hierarchy to be documented.
210 The basename is stripped off the filename if it starts with
211 the basename.
212 @param newline newline character to be used (string)
213 """
214 if not self.remembered:
215 sys.stderr.write("No QtHelp to generate.\n")
216 return
217
218 if basename:
219 basename = basename.replace(os.sep, ".")
220 if not basename.endswith("."):
221 basename = "{0}.".format(basename)
222
223 sections = self.__generateSections("00index", 3)
224 filesList = sorted(e for e in os.listdir(self.htmlDir)
225 if e.endswith('.html'))
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 f = open(os.path.join(self.outputDir, HelpProjectFile), "w",
248 encoding="utf-8", newline=newline)
249 f.write(txt)
250 f.close()
251
252 if self.createCollection and \
253 not os.path.exists(
254 os.path.join(self.outputDir, HelpCollectionProjectFile)):
255 collectionAttribs = {
256 "helpfile": HelpHelpFile,
257 }
258
259 txt = self.__convertEol(
260 HelpCollection.format(**collectionAttribs), newline)
261 f = open(os.path.join(self.outputDir, HelpCollectionProjectFile),
262 "w", encoding="utf-8", newline=newline)
263 f.write(txt)
264 f.close()
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(), generateQtToolName("qhelpgenerator")
275 )
276 shutil.copy(
277 os.path.join(self.outputDir, HelpProjectFile), self.htmlDir)
278 os.chdir(self.htmlDir)
279 subprocess.call([
280 qhelpgeneratorExe,
281 HelpProjectFile, "-o", os.path.join(self.outputDir, HelpHelpFile)])
282 os.remove(HelpProjectFile)
283
284 if self.createCollection:
285 qcollectiongeneratorExe = os.path.join(
286 getQtBinariesPath(), generateQtToolName("qcollectiongenerator")
287 )
288 if not isExecutable(qcollectiongeneratorExe):
289 # assume Qt >= 5.12.0
290 qcollectiongeneratorExe = qhelpgeneratorExe
291 sys.stdout.write("Generating QtHelp collection...\n")
292 sys.stdout.flush()
293 sys.stderr.flush()
294 os.chdir(self.outputDir)
295 subprocess.call([
296 qcollectiongeneratorExe,
297 HelpCollectionProjectFile, "-o", HelpCollectionFile])
298
299 os.chdir(cwd)

eric ide

mercurial