#############################################################################
##
## 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)}
\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"""\
{get_colgroup(columns, indent=3)}
"""
)
# Iteration per rows
for i in range(math.ceil(len(examples) / columns)):
gallery += f"{ind(1)}\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)}{name} '
f"({underline}) | \n"
)
except IndexError:
# We use display:none to hide the cell
gallery += f'{ind(2)} | \n'
gallery += f"{ind(1)}
\n"
gallery += dedent("""\
"""
)
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)}{module_name.title()}
\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")