问题
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.
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()
回答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!
来源:https://stackoverflow.com/questions/61099365/use-recursion-to-avoid-writing-nested-loop-number-equal-to-the-number-of-layers