Recursive Tkinter Notebook

I try to build an app with a notebook in which I don’t really know the number of things that would go into it. I mean by things, tabs and subtabs and subtabs of subtabs. I decided to code a custom widget that will allow me to only give a dictionary in argument and voilà. I maybe do not seem clear so hear my example. Graphically it is fully working but when we look deeply into the notebook.tabs dictionary, well, it is not.

from tkinter import ttk
# Custom Tkinter Widget
class NotebookPlus(ttk.Notebook):
    def __init__(self, *args, schema=None, **kwargs):
        ttk.Notebook.__init__(self, *args, **kwargs)
        self.schema = schema
        self.tabs = {}

        def superiterdict(dictionnary, lastK=None, lastN=None):

            for key, value in dictionnary.items():
                if lastN == None: lastN = self

                # With multiple layer of notebook
                if isinstance(value, dict):

                    # Create a new principal notebook
                    self.tabs[key] = {}

                    # Define the frame where the new notebook will be and where tabs are going to be
                    self.tabs[key]['frame'] = ttk.Frame(lastN)
                    self.tabs[key]['frame'].pack(fill='both')

                    # Define the new notebook
                    self.tabs[key]['notebook'] = ttk.Notebook(self.tabs[key]['frame'])
                    self.tabs[key]['notebook'].pack(fill='both')

                    lastN.add(self.tabs[key]['frame'], text=key)

                    # Define tabs
                    self.tabs[key]['tabs'] = {}

                    superiterdict(dictionnary=value, lastK=key, lastN=self.tabs[key]['notebook'])

                else:
                    if self.tabs == {} and lastK == None:
                        self.tabs[lastK] = {'frame':None, 'notebook':lastN, 'tabs':{}}
                    self.tabs[lastK]['tabs'][key] = ttk.Frame(lastN)
                    self.tabs[lastK]['tabs'][key].pack(fill='both')
                    self.tabs[lastK]['notebook'].add(self.tabs[lastK]['tabs'][key], text=key)

        superiterdict(self.schema)


root = tk.Tk()
frame = tk.Frame(root)
tabsSchema = {'TAB1': {'subtab11': None, 'subtab12': None},
              'TAB2': {'subtab21': {'ssubtab211': None, 'ssubtab212': None}, 'subtab22': None}
              }
notebook = NotebookPlus(root,schema=tabsSchema)
notebook.pack(fill='both')


root.mainloop()

My problem is that each time that the recursive function superiterdict is in front with a dictionary, it is treated as a completely new tab but sometimes it is not. To see that, here is the dictionary where everything is thrown in.

{
    'TAB1': {
        'frame': 'blob',
        'notebook': 'blob',
        'tabs': {
                'subtab11': 'blob',
                'subtab12': 'blob'
                }
        },
    'TAB2': {
            'frame': 'blob',
            'notebook': 'blob',
            'tabs': {
                    'subtab22':'blob'
                    }
        },
    'subtab21': {
                'frame': 'blob',
                'notebook': 'blob',
                'tabs': {
                        'ssubtab211': 'blob',
                        'ssubtab212': 'blob'
                        }
                    }
}

I use ‘blob’ instead of ‘<tkinter.ttk.Frame object .!notebookplus.!frame2.!notebook.!frame2>’ for readability. We can notice that the ‘subtab21’ is not in the tab dictionary of TAB2.

Answer

The problem is that you are using self.tabs to add to the dictionary each time. This means that every time a key has a dictionary as it’s value, that key is getting added to self.tabs instead of the tabs dictionary of the parent. You want to add to the tabs dictionary of the parent instead.
Here is a working superiterdict function:

def superiterdict(dictionary, lastN = self, lastK = self.tabs):
            # lastN: The widget to use as the parent.
            # lastK: The "tabs" dictionary of the parent.
            #        If not given, it defaults to self.tabs.
            for key, value in dictionary.items():
                if isinstance(value, dict):
                    lastK[key] = {}
                    lastK[key]["frame"] = ttk.Frame(lastN)
                    lastK[key]["frame"].pack(fill = "both")
                    lastN.add(lastK[key]["frame"], text = key)
                    lastK[key]["notebook"] = ttk.Notebook(lastK[key]["frame"])
                    lastK[key]["notebook"].pack(fill = "both")
                    lastK[key]["tabs"] = {}
                    superiterdict(value, lastN = lastK[key]["notebook"], lastK = lastK[key]["tabs"])
                else:
                    lastK[key] = ttk.Frame(lastN)
                    lastK[key].pack(fill = "both")
                    lastN.add(lastK[key], text = key)

This provides self.tabs in the desired format.