0

This excellent answer to Return a list of all objects vertically stacked above a given object? started me on tree traversal, now I need to start building a complex control panel using wxPython.

I'm learning about sizers but for this abstracted script I've used matplotlib to generate what the panel will look like.

The part I need help with is only the bit near the end with the comments First layer, Second layer and Third layer. What I need is to use recursion so that I don't have to have the correct number of nested loops equal to the number of layers.

Once I get better at wxPython I'll use the same recursion to build the real control panel.

Each heavy black rectangle will ultimately be a cluster of wxPython widgets, and each thing red rectangle will be the enclosing sizer.

mockup of wxPython with widgets inside sizers

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.lines as lines

class DG():
    def __init__(self, name):

        self.dgs = []
        self.name = str(name)

    def add_dg(self, name):
        dg = DG(name=name)
        self.dgs.append(dg)
        return dg

    def __repr__(self):
        return ('{self.name}'.format(self=self))

    def find_trees(self):
        # https://stackoverflow.com/questions/60339232/return-a-list-of-all-objects-vertically-stacked-above-a-given-object
        # https://en.wikipedia.org/wiki/Tree_traversal
        self.trees = []
        def __fill_dg_trees(dg_trees, dg, path):
            for th in dg.dgs:
                __fill_dg_trees(dg_trees, th, path + [dg])
            if not dg.dgs:
                self.trees.append(path + [dg])
        __fill_dg_trees(self.trees, self, [])
        self.n_trees = len(self.trees)

class Sub():
    def __init__(self, name):
        self.width = 1.0 
        self.x0 = 0.0 
        self.name = name
        self.dgs = []

    def add_dg(self, name):
        dg = DG(name=name)
        self.dgs.append(dg)
        return dg

    def find_trees(self):
        # https://stackoverflow.com/questions/60339232/return-a-list-of-all-objects-vertically-stacked-above-a-given-object
        # https://en.wikipedia.org/wiki/Tree_traversal
        self.trees = []
        def __fill_dg_trees(dg_trees, dg, path):
            for th in dg.dgs:
                __fill_dg_trees(dg_trees, th, path + [dg])
            if not dg.dgs:
                self.trees.append(path + [dg])
        __fill_dg_trees(self.trees, self, [])
        self.n_trees = len(self.trees)

    def __repr__(self):
        return ('{self.name}'.format(self=self))

#  -----------
# |  C  |  D  |
#  -----------------------------
# |      B    |  F  |  G  |  H  |
#  -----------------------------
# |      A    |     E           |
#  -----------------------------
# |       Substrate             |
#  -----------------------------

sub = Sub(name='My Substrate')
A = sub.add_dg(name='A')
B = A.add_dg(name='B')
C = B.add_dg(name='C')
D = B.add_dg(name='D')
E = sub.add_dg('E')
F = E.add_dg('F')
G = E.add_dg('G')
H = E.add_dg('H')

sub.find_trees()
sub.goodies = set(sum(sub.trees, [])).difference(set([sub]))
for thing in sub.goodies:
    thing.find_trees()
sub.tree_height = max([len(tree) for tree in sub.trees]) - 1
sub.n_trees = len(sub.trees)
sub.n_goodies = len(sub.goodies)
print('sub.tree_height: ', sub.tree_height)
print('sub.n_trees: ', sub.n_trees)
print('sub.n_goodies: ', sub.n_goodies)
print('sub.goodies: ', sub.goodies)

for i, tree in enumerate(sub.trees):
    print(i, tree)

def squareit(thing, nh, dh, dd, hw, hh):
    x0 = thing.x0
    linez, texts = [], []
    print('called thing: ', thing)
    print('thing.width, thing.n_trees: ', thing.width, thing.n_trees)
    for i, dg in enumerate(thing.dgs):
        print('i, dg: ', i, dg)
        print('dg.n_trees: ', dg.n_trees)
        dg.width = float(dg.n_trees) * thing.width / thing.n_trees
        dg.x0 = x0
        print('dg.width: ', dg.width)
        x1, x2 = x0+dd, x0 + dg.width - dd
        y1, y2 = nh*dh + dd, ((nh+1)*dh) - dd
        xx = np.array([x1, x2, x2, x1, x1])
        yy = np.array([y1, y1, y2, y2, y1])
        outline = lines.Line2D(xx, yy, lw=1., color='r', alpha=1.0,
                              transform=fig.transFigure, figure=fig) # https://stackoverflow.com/a/5022412/3904031
        xt, yt = x0+1.5*dd, ((nh+0.5)*dh)-dd
        texts.append((xt, yt, dg.name))

        x1, x2 = x0 + 0.5*dg.width - hw, x0 + 0.5*dg.width + hw
        y1, y2 = ((nh+0.5)*dh) - hh, ((nh+0.5)*dh) + hh
        xx = np.array([x1, x2, x2, x1, x1])
        yy = np.array([y1, y1, y2, y2, y1])
        control_pannel_line = lines.Line2D(xx, yy, lw=3., color='k', alpha=1.0,
                              transform=fig.transFigure, figure=fig) # https://stackoverflow.com/a/5022412/3904031
        linez += [outline, control_pannel_line]
        x0 += dg.width
    return linez, texts

if True:
    fig = plt.figure()
    x0 = 0.
    dd = 0.01
    dh = 0.2
    hw, hh = 0.05, 0.075
    # linez, texts = [], []

    # draw the substrate first
    nh = 0
    x1, x2 = x0+dd, x0 + sub.width - dd
    y1, y2 = nh*dh + dd, ((nh+1)*dh) - dd
    xx = np.array([x1, x2, x2, x1, x1])
    yy = np.array([y1, y1, y2, y2, y1])
    outline = lines.Line2D(xx, yy, lw=1., color='r', alpha=1.0,
                          transform=fig.transFigure, figure=fig)

    xt, yt = x0+1.5*dd, ((nh+0.5)*dh)-dd
    texts = [(xt, yt, sub.name)]

    x1, x2 = x0 + 0.5*sub.width - hw, x0 + 0.5*sub.width + hw
    y1, y2 = ((nh+0.5)*dh) - hh, ((nh+0.5)*dh) + hh
    xx = np.array([x1, x2, x2, x1, x1])
    yy = np.array([y1, y1, y2, y2, y1])
    control_pannel_line = lines.Line2D(xx, yy, lw=3., color='k', alpha=1.0,
                          transform=fig.transFigure, figure=fig)
    linez = [outline, control_pannel_line]

    # now iterate through the whole thing

    # first layer:    
    a, b = squareit(sub, nh=1, dh=dh, dd=dd, hw=hw, hh=hh)
    linez += a
    texts += b

    # second layer:    
    for dg in sub.dgs:
        a, b = squareit(dg, nh=2, dh=dh, dd=dd, hw=hw, hh=hh)
        linez += a
        texts += b

        # third layer:    
        for dgg in dg.dgs:
            a, b = squareit(dgg, nh=3, dh=dh, dd=dd, hw=hw, hh=hh)
            linez += a
            texts += b

    fig.lines.extend(linez) # https://matplotlib.org/3.1.0/gallery/pyplots/fig_x.html

    for (x, y, text) in texts:
        fig.text(x, y, text, fontsize=14)
    plt.show()
uhoh
  • 3,713
  • 6
  • 42
  • 95

1 Answers1

1

I use dfs and only use one class Sub (since I think Sub and DG is redundant). Here is the code:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.lines as lines

class Sub():
    def __init__(self, name):
        self.width = 1.0 
        self.x0 = 0.0 
        self.name = name
        self.dgs = []

    def add_dg(self, name):
        dg = Sub(name=name)
        self.dgs.append(dg)
        return dg

    def find_trees(self):
        # https://stackoverflow.com/questions/60339232/return-a-list-of-all-objects-vertically-stacked-above-a-given-object
        # https://en.wikipedia.org/wiki/Tree_traversal
        self.trees = []
        def __fill_dg_trees(dg_trees, dg, path):
            for th in dg.dgs:
                __fill_dg_trees(dg_trees, th, path + [dg])
            if not dg.dgs:
                self.trees.append(path + [dg])
        __fill_dg_trees(self.trees, self, [])
        self.n_trees = len(self.trees)

    def __repr__(self):
        return ('{self.name}'.format(self=self))

#  -----------
# |  C  |  D  |
#  -----------------------------
# |      B    |  F  |  G  |  H  |
#  -----------------------------
# |      A    |     E           |
#  -----------------------------
# |       Substrate             |
#  -----------------------------

sub = Sub(name='My Substrate')
A = sub.add_dg(name='A')
B = A.add_dg(name='B')
C = B.add_dg(name='C')
D = B.add_dg(name='D')
E = sub.add_dg('E')
F = E.add_dg('F')
G = E.add_dg('G')
H = E.add_dg('H')

sub.find_trees()
sub.goodies = set(sum(sub.trees, [])).difference(set([sub]))
for thing in sub.goodies:
    thing.find_trees()
sub.tree_height = max([len(tree) for tree in sub.trees]) - 1
sub.n_trees = len(sub.trees)
sub.n_goodies = len(sub.goodies)
print('sub.tree_height: ', sub.tree_height)
print('sub.n_trees: ', sub.n_trees)
print('sub.n_goodies: ', sub.n_goodies)
print('sub.goodies: ', sub.goodies)

for i, tree in enumerate(sub.trees):
    print(i, tree)

def squareit(thing, nh, dh, dd, hw, hh):
    x0 = thing.x0
    linez, texts = [], []
    print('called thing: ', thing)
    print('thing.width, thing.n_trees: ', thing.width, thing.n_trees)
    for i, dg in enumerate(thing.dgs):
        print('i, dg: ', i, dg)
        print('dg.n_trees: ', dg.n_trees)
        dg.width = float(dg.n_trees) * thing.width / thing.n_trees
        dg.x0 = x0
        print('dg.width: ', dg.width)
        x1, x2 = x0+dd, x0 + dg.width - dd
        y1, y2 = nh*dh + dd, ((nh+1)*dh) - dd
        xx = np.array([x1, x2, x2, x1, x1])
        yy = np.array([y1, y1, y2, y2, y1])
        outline = lines.Line2D(xx, yy, lw=1., color='r', alpha=1.0,
                              transform=fig.transFigure, figure=fig) # https://stackoverflow.com/a/5022412/3904031
        xt, yt = x0+1.5*dd, ((nh+0.5)*dh)-dd
        texts.append((xt, yt, dg.name))

        x1, x2 = x0 + 0.5*dg.width - hw, x0 + 0.5*dg.width + hw
        y1, y2 = ((nh+0.5)*dh) - hh, ((nh+0.5)*dh) + hh
        xx = np.array([x1, x2, x2, x1, x1])
        yy = np.array([y1, y1, y2, y2, y1])
        control_pannel_line = lines.Line2D(xx, yy, lw=3., color='k', alpha=1.0,
                              transform=fig.transFigure, figure=fig) # https://stackoverflow.com/a/5022412/3904031
        linez += [outline, control_pannel_line]
        x0 += dg.width
    return linez, texts

if True:
    fig = plt.figure()
    x0 = 0.
    dd = 0.01
    dh = 0.2
    hw, hh = 0.05, 0.075
    # linez, texts = [], []

    # draw the substrate first
    nh = 0
    x1, x2 = x0+dd, x0 + sub.width - dd
    y1, y2 = nh*dh + dd, ((nh+1)*dh) - dd
    xx = np.array([x1, x2, x2, x1, x1])
    yy = np.array([y1, y1, y2, y2, y1])
    outline = lines.Line2D(xx, yy, lw=1., color='r', alpha=1.0,
                          transform=fig.transFigure, figure=fig)

    xt, yt = x0+1.5*dd, ((nh+0.5)*dh)-dd
    texts = [(xt, yt, sub.name)]

    x1, x2 = x0 + 0.5*sub.width - hw, x0 + 0.5*sub.width + hw
    y1, y2 = ((nh+0.5)*dh) - hh, ((nh+0.5)*dh) + hh
    xx = np.array([x1, x2, x2, x1, x1])
    yy = np.array([y1, y1, y2, y2, y1])
    control_pannel_line = lines.Line2D(xx, yy, lw=3., color='k', alpha=1.0,
                          transform=fig.transFigure, figure=fig)
    linez = [outline, control_pannel_line]

    # Using DFS:
    def dfs(node, nh, linez, texts):
        a, b = squareit(node, nh=nh, dh=dh, dd=dd, hw=hw, hh=hh)
        linez += a
        texts += b
        for child in node.dgs:
            dfs(child, nh+1, linez, texts)
    dfs(sub, nh=1, linez=linez, texts=texts)

    fig.lines.extend(linez) # https://matplotlib.org/3.1.0/gallery/pyplots/fig_x.html

    for (x, y, text) in texts:
        fig.text(x, y, text, fontsize=14)
    plt.show()

Notice the part with comment # Using DFS.

I have tried it on my jupyter and seems to output the same thing as your code. Hope this help!

Yosua
  • 411
  • 3
  • 7