eric6/DocumentationTools/QtHelpGenerator.py

changeset 6942
2602857055c5
parent 6666
06f6e0f24e89
child 7229
53054eb5b15a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/DocumentationTools/QtHelpGenerator.py	Sun Apr 14 15:09:21 2019 +0200
@@ -0,0 +1,299 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the QtHelp generator for the builtin documentation
+generator.
+"""
+
+from __future__ import unicode_literals
+
+import sys
+import os
+import shutil
+import subprocess
+
+from Utilities import joinext, relpath, html_encode, getQtBinariesPath, \
+    generateQtToolName, isExecutable
+
+HelpCollection = r"""<?xml version="1.0" encoding="utf-8" ?>
+<QHelpCollectionProject version="1.0">
+  <docFiles>
+    <register>
+      <file>{helpfile}</file>
+    </register>
+  </docFiles>
+</QHelpCollectionProject>
+"""
+
+HelpProject = r"""<?xml version="1.0" encoding="UTF-8"?>
+<QtHelpProject version="1.0">
+  <namespace>{namespace}</namespace>
+  <virtualFolder>{folder}</virtualFolder>
+  <customFilter name="{filter_name}">
+{filter_attributes}
+  </customFilter>
+  <filterSection>
+{filter_attributes}
+    <toc>
+{sections}
+    </toc>
+    <keywords>
+{keywords}
+    </keywords>
+    <files>
+{files}
+    </files>
+  </filterSection>
+</QtHelpProject>
+"""
+
+HelpProjectFile = 'source.qhp'
+HelpHelpFile = 'source.qch'
+HelpCollectionProjectFile = 'source.qhcp'
+HelpCollectionFile = 'collection.qhc'
+
+
+class QtHelpGenerator(object):
+    """
+    Class implementing the QtHelp generator for the builtin documentation
+    generator.
+    """
+    def __init__(self, htmlDir,
+                 outputDir, namespace, virtualFolder, filterName,
+                 filterAttributes, title, createCollection):
+        """
+        Constructor
+        
+        @param htmlDir directory containing the HTML files (string)
+        @param outputDir output directory for the files (string)
+        @param namespace namespace to be used (string)
+        @param virtualFolder virtual folder to be used (string)
+        @param filterName name of the custom filter (string)
+        @param filterAttributes ':' separated list of filter attributes
+            (string)
+        @param title title to be used for the generated help (string)
+        @param createCollection flag indicating the generation of the
+            collection files (boolean)
+        """
+        self.htmlDir = htmlDir
+        self.outputDir = outputDir
+        self.namespace = namespace
+        self.virtualFolder = virtualFolder
+        self.filterName = filterName
+        self.filterAttributes = \
+            filterAttributes and filterAttributes.split(':') or []
+        self.relPath = relpath(self.htmlDir, self.outputDir)
+        self.title = title
+        self.createCollection = createCollection
+        
+        self.packages = {
+            "00index": {
+                "subpackages": {},
+                "modules": {}
+            }
+        }
+        self.remembered = False
+        self.keywords = []
+    
+    def remember(self, file, moduleDocument, basename=""):
+        """
+        Public method to remember a documentation file.
+        
+        @param file The filename to be remembered. (string)
+        @param moduleDocument The ModuleDocument object containing the
+            information for the file.
+        @param basename The basename of the file hierarchy to be documented.
+            The basename is stripped off the filename if it starts with
+            the basename.
+        """
+        self.remembered = True
+        if basename:
+            file = file.replace(basename, "")
+        
+        if "__init__" in file:
+            dirName = os.path.dirname(file)
+            udir = os.path.dirname(dirName)
+            if udir:
+                upackage = udir.replace(os.sep, ".")
+                try:
+                    elt = self.packages[upackage]
+                except KeyError:
+                    elt = self.packages["00index"]
+            else:
+                elt = self.packages["00index"]
+            package = dirName.replace(os.sep, ".")
+            elt["subpackages"][package] = moduleDocument.name()
+            
+            self.packages[package] = {
+                "subpackages": {},
+                "modules": {}
+            }
+            
+            kwEntry = ("{0} (Package)".format(package.split('.')[-1]),
+                       joinext("index-{0}".format(package), ".html"))
+            if kwEntry not in self.keywords:
+                self.keywords.append(kwEntry)
+            
+            if moduleDocument.isEmpty():
+                return
+        
+        package = os.path.dirname(file).replace(os.sep, ".")
+        try:
+            elt = self.packages[package]
+        except KeyError:
+            elt = self.packages["00index"]
+        elt["modules"][moduleDocument.name()] = moduleDocument.name()
+        
+        if "__init__" not in file:
+            kwEntry = (
+                "{0} (Module)".format(moduleDocument.name().split('.')[-1]),
+                joinext(moduleDocument.name(), ".html"))
+            if kwEntry not in self.keywords:
+                self.keywords.append(kwEntry)
+        for kw in moduleDocument.getQtHelpKeywords():
+            kwEntry = (kw[0], "{0}{1}".format(
+                joinext(moduleDocument.name(), ".html"), kw[1]))
+            if kwEntry not in self.keywords:
+                self.keywords.append(kwEntry)
+    
+    def __generateSections(self, package, level):
+        """
+        Private method to generate the sections part.
+        
+        @param package name of the package to process (string)
+        @param level indentation level (integer)
+        @return sections part (string)
+        """
+        indent = level * '  '
+        indent1 = indent + '  '
+        s = indent + '<section title="{0}" ref="{1}">\n'.format(
+            package == "00index" and self.title or package,
+            package == "00index" and
+            joinext("index", ".html") or
+            joinext("index-{0}".format(package), ".html"))
+        for subpack in sorted(self.packages[package]["subpackages"]):
+            s += self.__generateSections(subpack, level + 1)
+            s += '\n'
+        for mod in sorted(self.packages[package]["modules"]):
+            s += indent1 + '<section title="{0}" ref="{1}" />\n'.format(
+                mod, joinext(mod, ".html"))
+        s += indent + '</section>'
+        return s
+    
+    def __convertEol(self, txt, newline):
+        """
+        Private method to convert the newline characters.
+        
+        @param txt text to be converted (string)
+        @param newline newline character to be used (string)
+        @return converted text (string)
+        """
+        # step 1: normalize eol to '\n'
+        txt = txt.replace("\r\n", "\n").replace("\r", "\n")
+        
+        # step 2: convert to the target eol
+        if newline is None:
+            return txt.replace("\n", os.linesep)
+        elif newline in ["\r", "\r\n"]:
+            return txt.replace("\n", newline)
+        else:
+            return txt
+    
+    def generateFiles(self, basename="", newline=None):
+        """
+        Public method to generate all index files.
+        
+        @param basename The basename of the file hierarchy to be documented.
+            The basename is stripped off the filename if it starts with
+            the basename.
+        @param newline newline character to be used (string)
+        """
+        if not self.remembered:
+            sys.stderr.write("No QtHelp to generate.\n")
+            return
+        
+        if basename:
+            basename = basename.replace(os.sep, ".")
+            if not basename.endswith("."):
+                basename = "{0}.".format(basename)
+        
+        sections = self.__generateSections("00index", 3)
+        filesList = sorted(e for e in os.listdir(self.htmlDir)
+                           if e.endswith('.html'))
+        files = "\n".join(
+            ["      <file>{0}</file>".format(f) for f in filesList])
+        filterAttribs = "\n".join(
+            ["    <filterAttribute>{0}</filterAttribute>".format(a)
+             for a in sorted(self.filterAttributes)])
+        keywords = "\n".join(
+            ['      <keyword name="{0}" id="{1}" ref="{2}" />'.format(
+             html_encode(kw[0]), html_encode(kw[0]), html_encode(kw[1]))
+             for kw in sorted(self.keywords)])
+        
+        helpAttribs = {
+            "namespace": self.namespace,
+            "folder": self.virtualFolder,
+            "filter_name": self.filterName,
+            "filter_attributes": filterAttribs,
+            "sections": sections,
+            "keywords": keywords,
+            "files": files,
+        }
+        
+        txt = self.__convertEol(HelpProject.format(**helpAttribs), newline)
+        f = open(os.path.join(self.outputDir, HelpProjectFile), "w",
+                 encoding="utf-8", newline=newline)
+        f.write(txt)
+        f.close()
+        
+        if self.createCollection and \
+           not os.path.exists(
+                os.path.join(self.outputDir, HelpCollectionProjectFile)):
+            collectionAttribs = {
+                "helpfile": HelpHelpFile,
+            }
+            
+            txt = self.__convertEol(
+                HelpCollection.format(**collectionAttribs), newline)
+            f = open(os.path.join(self.outputDir, HelpCollectionProjectFile),
+                     "w", encoding="utf-8", newline=newline)
+            f.write(txt)
+            f.close()
+        
+        sys.stdout.write("QtHelp files written.\n")
+        sys.stdout.write("Generating QtHelp documentation...\n")
+        sys.stdout.flush()
+        sys.stderr.flush()
+        
+        cwd = os.getcwd()
+        # generate the compressed files
+        qhelpgeneratorExe = os.path.join(
+            getQtBinariesPath(), generateQtToolName("qhelpgenerator")
+        )
+        shutil.copy(
+            os.path.join(self.outputDir, HelpProjectFile), self.htmlDir)
+        os.chdir(self.htmlDir)
+        subprocess.call([
+            qhelpgeneratorExe,
+            HelpProjectFile, "-o", os.path.join(self.outputDir, HelpHelpFile)])
+        os.remove(HelpProjectFile)
+        
+        if self.createCollection:
+            qcollectiongeneratorExe = os.path.join(
+                getQtBinariesPath(), generateQtToolName("qcollectiongenerator")
+            )
+            if not isExecutable(qcollectiongeneratorExe):
+                # assume Qt >= 5.12.0
+                qcollectiongeneratorExe = qhelpgeneratorExe
+            sys.stdout.write("Generating QtHelp collection...\n")
+            sys.stdout.flush()
+            sys.stderr.flush()
+            os.chdir(self.outputDir)
+            subprocess.call([
+                qcollectiongeneratorExe,
+                HelpCollectionProjectFile, "-o", HelpCollectionFile])
+        
+        os.chdir(cwd)

eric ide

mercurial