aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorCristian Maureira-Fredes <Cristian.Maureira-Fredes@qt.io>2021-02-08 16:16:19 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-02-09 16:14:29 +0000
commit1481fc31f6500d899e2b8ea4f5ec5c80cfe36fbf (patch)
treed433da057d060aef65b3c4b8c7bac8cf21e31a79 /tools
parent720a4040bef4e614e4f03f4ae8b3c0f058d86be9 (diff)
doc: add tool to generate examples gallery
This script generates a gallery for all the example currently in pyside-setup/examples. Using this tool will overwrite the index rst file for the examples located in 'sources/pyside6/doc/examples/index.rst Additionally, to display the code of each example, this will generate one extra .rst file for each example that contains a .pyproject file, for example: 'sources/pysides6/doc/examples/example_widgets__tetrix.rst' Currently, the usage of this tool is not incorporated in the documentation building process. Task-number: PYSIDE-1490 Change-Id: I78546d4c7905fd8b521f4112457980b4d1d56860 Reviewed-by: Christian Tismer <tismer@stackless.com> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> (cherry picked from commit e8eac85a5db8386689ffe174694f82c1e5dad854) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/example_gallery/main.py256
1 files changed, 256 insertions, 0 deletions
diff --git a/tools/example_gallery/main.py b/tools/example_gallery/main.py
new file mode 100644
index 000000000..bdb983014
--- /dev/null
+++ b/tools/example_gallery/main.py
@@ -0,0 +1,256 @@
+#############################################################################
+##
+## Copyright (C) 2021 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qt for Python.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+###############
+
+
+"""
+This tool reads all the examples from the main repository that have a
+'.pyproject' file, and generates a special table/gallery in the documentation
+page.
+
+For the usage, simply run:
+ python tools/example_gallery/main.py
+since there is no special requirements.
+"""
+
+import json
+import math
+from pathlib import Path
+from textwrap import dedent
+
+
+def ind(x):
+ return " " * 4 * x
+
+
+def get_colgroup(columns, indent=2):
+ width = 80 # percentage
+ width_column = width // columns
+ return f'{ind(indent)}<col style="width: {width_column}%" />\n' * columns
+
+
+def add_indent(s, level):
+ new_s = ""
+ for line in s.splitlines():
+ if line.strip():
+ new_s += f"{ind(level)}{line}\n"
+ else:
+ new_s += "\n"
+ return new_s
+
+
+def get_module_gallery(examples):
+ """
+ This function takes a list of dictionaries, that contain examples
+ information, from one specific module.
+ """
+
+ gallery = dedent(f"""\
+ <table class="special">
+ <colgroup>
+{get_colgroup(columns, indent=3)}
+ </colgroup>
+ """
+ )
+
+ # Iteration per rows
+ for i in range(math.ceil(len(examples) / columns)):
+ gallery += f"{ind(1)}<tr>\n"
+ # Iteration per columns
+ for j in range(columns):
+ # We use a 'try-except' to handle when the examples are
+ # not an exact 'rows x columns', meaning that some cells
+ # will be empty.
+ try:
+ e = examples[i * columns + j]
+ url = e["rst"].replace(".rst", ".html")
+ name = e["example"]
+ underline = f'{e["module"]}'
+ if e["extra"]:
+ underline += f'/{e["extra"]}'
+ gallery += (
+ f'{ind(2)}<td><a href="{url}"><p><strong>{name}</strong><br/>'
+ f"({underline})</p></a></td>\n"
+ )
+ except IndexError:
+ # We use display:none to hide the cell
+ gallery += f'{ind(2)}<td style="display: none;"></td>\n'
+ gallery += f"{ind(1)}</tr>\n"
+
+ gallery += dedent("""\
+ </table>
+ """
+ )
+ return gallery
+
+
+def remove_licenses(s):
+ new_s = []
+ for line in s.splitlines():
+ if line.strip().startswith(("/*", "**", "##")):
+ continue
+ new_s.append(line)
+ return "\n".join(new_s)
+
+
+if __name__ == "__main__":
+ # Only examples with a '.pyproject' file will be listed.
+ DIR = Path(__file__).parent
+ EXAMPLES_DOC = f"{DIR}/../../sources/pyside6/doc/examples"
+ EXAMPLES_DIR = Path(f"{DIR}/../../examples/")
+ columns = 5
+ gallery = ""
+
+
+ # This main loop will be in charge of:
+ # * Getting all the .pyproject files,
+ # * Gather the information of the examples and store them in 'examples'
+ # * Read the .pyproject file to output the content of each file
+ # on the final .rst file for that specific example.
+ examples = {}
+ for f_path in EXAMPLES_DIR.glob("**/*.pyproject"):
+ if str(f_path).endswith("examples.pyproject"):
+ continue
+
+ parts = f_path.parts[len(EXAMPLES_DIR.parts) : -1]
+
+ module_name = parts[0]
+ example_name = parts[-1]
+ # handling subdirectories besides the module level and the example
+ extra_names = "" if len(parts) == 2 else "_".join(parts[1:-1])
+
+ rst_file = f"example_{module_name}_{extra_names}_{example_name}.rst"
+
+ if module_name not in examples:
+ examples[module_name] = []
+
+ examples[module_name].append(
+ {
+ "example": example_name,
+ "module": module_name,
+ "extra": extra_names,
+ "rst": rst_file,
+ "abs_path": str(f_path),
+ }
+ )
+
+ pyproject = ""
+ with open(str(f_path), "r") as pyf:
+ pyproject = json.load(pyf)
+
+ if pyproject:
+ with open(f"{EXAMPLES_DOC}/{rst_file}", "w") as out_f:
+ content_f = (
+ "..\n This file was auto-generated by the 'examples_gallery' "
+ "script.\n Any change will be lost!\n\n"
+ )
+ for project_file in pyproject["files"]:
+ if project_file.split(".")[-1] in ("png", "pyc"):
+ continue
+ length = len(project_file)
+ content_f += f"{project_file}\n{'=' * length}\n\n::\n\n"
+
+ _path = f_path.resolve().parents[0] / project_file
+ _content = ""
+ with open(_path, "r") as _f:
+ _content = remove_licenses(_f.read())
+
+ content_f += add_indent(_content, 1)
+ content_f += "\n\n"
+ out_f.write(content_f)
+ print(f"Written: {EXAMPLES_DOC}/{rst_file}")
+ else:
+ print("Empty '.pyproject' file, skipping")
+
+ base_content = dedent("""\
+ ..
+ This file was auto-generated from the 'pyside-setup/tools/example_gallery'
+ All editions in this file will be lost.
+
+ |project| Examples
+ ===================
+
+ A collection of examples are provided with |project| to help new users
+ to understand different use cases of the module.
+
+ .. toctree::
+ :maxdepth: 1
+
+ tabbedbrowser.rst
+ ../pyside-examples/all-pyside-examples.rst
+
+ Gallery
+ -------
+
+ You can find all these examples inside the ``pyside-setup`` on the ``examples``
+ directory, or you can access them after installing |pymodname| from ``pip``
+ inside the ``site-packages/PySide6/examples`` directory.
+
+ .. raw:: html
+
+ """
+ )
+
+ # We generate a 'toctree' at the end of the file, to include the new
+ # 'example' rst files, so we get no warnings, and also that users looking
+ # for them will be able to, since they are indexed.
+ # Notice that :hidden: will not add the list of files by the end of the
+ # main examples HTML page.
+ footer_index = dedent("""\
+ .. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ """
+ )
+
+ # Writing the main example rst file.
+ index_files = []
+ with open(f"{EXAMPLES_DOC}/index.rst", "w") as f:
+ f.write(base_content)
+ for module_name, e in sorted(examples.items()):
+ for i in e:
+ index_files.append(i["rst"])
+ f.write(f"{ind(1)}<h3>{module_name.title()}</h3>\n")
+ f.write(add_indent(get_module_gallery(e), 1))
+ f.write("\n\n")
+ f.write(footer_index)
+ for i in index_files:
+ f.write(f" {i}\n")
+
+ print(f"Written index: {EXAMPLES_DOC}/index.rst")