I found a better way, but there is still room for improvement. It currently perfectly works to display the whole content of my app if I click on the “coverPage” link, or only the content of a specific chapter if I click on this chapter/subchapter.
At this stage the automatic page numbering works like a charm; but I will still add a “type” key to the dash_registry so that I can display the title numbering inside this kind of page (chapter, subchapter, etc)
I’m also currently working on a func returning the table of content nicely.
def gen_wrapper_of_wrapper_page_and_toolbar(type=None, sectionTitle=None, coverPageDict=None, footerContentLeft=None,
footerContentRight=None, pageNumber=None, chapterDict=None,
subChapterDict=None, bioDict=None, quoteDict=None, innerTitle=None,
innerContent_spg="", innerContent_stb="", callerName=None):
print("*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*_*")
print("1 - callerName is: {}".format(callerName))
wrapper_of_wrapper_page_and_toolbar = [
gen_wrapper_page_and_toolbar(
type=type, sectionTitle=sectionTitle, coverPageDict=coverPageDict, chapterDict=chapterDict,
subChapterDict=subChapterDict, bioDict=bioDict, quoteDict=quoteDict,
innerTitle=innerTitle, # in the inBetween (top)
innerContent_spg=innerContent_spg, #in the inBetween (middle)
pageNumber=list(dash.page_registry.keys()).index(callerName),
footerContentLeft=footerContentLeft, footerContentRight=footerContentRight,
innerContent_stb=innerContent_stb
)
]
########################################################
pg_reg = dash.page_registry
# reminder: callerName is the name of the module from which the func is called. It is a str like "pages.0_coverPage"
print("0 - pg_reg[callerName][show_childrens] is: {}".format(pg_reg[callerName]["show_childrens"]))
if pg_reg[callerName]["show_childrens"]:
# if the flag "show_childrens" is True, then the layout of the page from which the function is called must
# contain 1) the typical layout of that kind of page, 2) the layouts of the other pages at the same level
# and 3) its relevant direct childrens.
# For instance, a page type "coverPage" shows its sisters (table of contents) and childrens (chapter1_title)
for pg in pg_reg:
# for each page in the page_registry - Note: pg is a key (in the dict pg_reg), and the key
# are inferred from the module name. "pg" is therefore a string like "pages.0_coverPage"
if pg != callerName:
print("pg ({}) was not ignored because it is different from the caller page ({})".format(pg, callerName))
# if the key - which is inferred from the module name, for each page - is different from
# the callerName, then we *might* have to add the page.
# rationale: the layout of the page calling the function has already been built, above,
# by calling "wrapper_of_wrapper_page_and_toolbar". Therefore, we don't include it again
print("pg path is: {}".format(os.path.dirname(pg_reg[pg]["path"])))
print("caller page path is: {}".format(os.path.dirname(pg_reg[callerName]["path"])))
if os.path.dirname(pg_reg[callerName]["path"]) in os.path.dirname(pg_reg[pg]["path"]):
print("{} is in {}".format(os.path.dirname(pg_reg[callerName]["path"]), os.path.dirname(pg_reg[pg]["path"])))
# check that the path of the calling page is in the path of "pg""
# Obviously, if pg_reg[callerName]["path"] is '/' because the "page" from which the function is
# called is "pages.0_coverPage" (i.e, in the root folder), then '/' will be in the path of each pg.
# But, if the function is called from a chapter title, such as "pages.chapter_4.title", then it
# enables us to exclude a page such as "0_coverPage", whose path would be '/', while the path of the
# page from which the function is called would be '/chapter4', which is not in '/'.
# Therefore, coverPage would not be included in the returned layout, if the function is called from
# a chapter. In short: if "pg" is not in the chapter from which the page calling the function is,
# then it wont be included into the returned layout.
print("re.findall on pg path is: {}".format(re.findall("/.+?(?=/|$)",os.path.dirname(pg_reg[pg]["path"]))))
print("re.findall on caller page is: {}".format(re.findall("/.+?(?=/|$)",os.path.dirname(pg_reg[callerName]["path"]))))
if len(re.findall("/.+?(?=/|$)",os.path.dirname(pg_reg[pg]["path"])))-len(re.findall("/.+?(?=/|$)",os.path.dirname(pg_reg[callerName]["path"])))==0:
# Warning: a simple .split("/") would returns ["",""] if we splitted a string like "/",
# therefore "/" and "/4-sectionContent" would both return a list of length 2 if we splitted them
# Rather, we use re.findall to look for all the matches "starting with a /, followed by several
# characters until another / or an end of string". This way, re.findall on a "/" returns an
# empty list, re.findall on "/section4" returns a list with 1 match, and re.findall on
# "/section4/subsection4" returns a list of 2 matches
# This checks that the "pg" page is at the same level as the calling page. If it is, then "pg"
# page is a sister of the calling page. E.g: "table of contents" would typically be a sister
# of the "coverPage". Both path would be "/" and both would have the same length".
# Therefore, the "tableOfContents" page layout is added to the returned layout
wrapper_of_wrapper_page_and_toolbar.extend(pg_reg[pg]['layout']().children)
elif len(re.findall("/.+?(?=/|$)",os.path.dirname(pg_reg[pg]["path"])))-len(re.findall("/.+?(?=/|$)",os.path.dirname(pg_reg[callerName]["path"])))==1:
# If the path of the considered "pg" page has exactly one level more than the path of the
# calling page, (i.e is one level lower in the hierarchy/folder tree),
# then we *might* have to add the pg
if pg_reg[pg]["show_childrens"]:
# if that pg has the "show_childrens" flag, then it means it is itself a page showing its
# sisters and childrens. Therefore, we add it to the returned layout.
# That way, when a page must return its childrens, it always shows 1) itself, then 2) adds
# its sister, and 3) adds its *direct* children if and only if this children also shows its
# children.
# E.g: "coverPage" calls the function,
# which adds the layout of the "table of contents" (sister, in the same folder)
# and then the layout of "chapter1_title" which is in "/chapter1" folder, because
# "chapter1_title" must itself shows its childrens
wrapper_of_wrapper_page_and_toolbar.extend(pg_reg[pg]['layout']().children)
else:
pass
else:
# If the path of the considered "pg" page has more than one level more than the path of the
# calling page, then this page is too far away in the hierarchy (and will be called by another
# "chapter" or "subchapter" title page.
pass
else:
print("path of caller page is not in the current g. Current pg path is:".format(os.path.dirname(pg_reg[pg]["path"])))
pass
else:
print("pg ({}) was ignored because it is the caller page ({})".format(pg, callerName))
# if the key is equal to the module name from which the function is called, then the layout of
# this key must be ignored. (Otherwise the page would appear twice)
pass
else:
pass
return html.Div(children=wrapper_of_wrapper_page_and_toolbar, className="wrapper-of-wrapper-page-and-toolbar")
Assuming an app structure like this:
The func returning the layout of coverPage, when called from that module, returns the layout of coverPage + tableOfContent+chapter_1_title.py (because the show_childrens flag is True), and also chapter_2_title.py (for the same reason).
The function itself does not return the subchapter page because there are too “deep” in the hierarchy. However, when the function generating the layout of chapter_1_title is called, it will include chapter_1_intro and the subchapter_1_title.py.
Which, in turns, will return the subchapter page.
Therefore, everything is returned by the function called from coverPage, and I dont need to add any “all.py” as a workaround 
Note that your solution helped me in understanding how to use the dash_registry in a func, I’m thankful for that 