Coverage for /home/deng/Projects/ete4/hackathon/ete4/ete4/smartview/renderer/layouts/staple_layouts.py: 16%
102 statements
« prev ^ index » next coverage.py v7.2.7, created at 2024-03-21 09:19 +0100
« prev ^ index » next coverage.py v7.2.7, created at 2024-03-21 09:19 +0100
1from ..treelayout import TreeLayout
2from ..faces import TextFace, RectFace, ScaleFace
3from ...utils import InvalidUsage
4from ....utils import random_color
7__all__ = [ "LayoutBarplot" ]
10def interpolate_colors(c1, c2, mix=0):
11 """Return a color between c1 (for mix=0) and c2 (for mix=1)."""
12 # '#ff5500', '#001122', 0.5 -> '#7f3311'
13 assert c1.startswith('#') and c2.startswith('#') and len(c1) == len(c2) == 7
14 return '#' + ''.join('%02x' % int((1-mix) * int(c1[1+2*i:3+2*i], 16) +
15 mix * int(c2[1+2*i:3+2*i], 16)) for i in range(3))
16 # NOTE: The original solution (depending on numpy and matplotlib -- not great), was:
17 # https://stackoverflow.com/questions/25668828/how-to-create-colour-gradient-in-python
20class LayoutPlot(TreeLayout):
21 def __init__(self, name=None, width=200, size_prop=None, color_prop=None,
22 position="aligned", column=0,
23 color_gradient=None, color="red", colors=None,
24 padding_x=10, scale=True, legend=True, active=True):
25 super().__init__(name,
26 aligned_faces=True if position == "aligned" else False,
27 legend=legend, active=active)
29 self.width = width
30 self.position = position
31 self.column = column
33 self.scale = scale
34 self.padding_x = padding_x
36 # if not (size_prop or color_prop):
37 # raise InvalidUsage("Either size_prop or color_prop required")
39 self.size_prop = size_prop
40 self.color_prop = color_prop
42 self.size_range = None
43 self.color_range = None
45 self.color = color
46 self.colors = colors
47 self.color_gradient = color_gradient
48 if self.color_prop and not self.color_gradient:
49 self.color_gradient = ("#FFF", self.color)
51 def set_tree_style(self, tree, tree_style):
52 super().set_tree_style(tree, tree_style)
53 def update_vals(metric, node):
54 p, minval, maxval, uniqvals = vals[metric]
55 prop = node.props.get(p)
56 try:
57 prop = float(prop)
58 vals[metric][1] = min(minval, prop)
59 vals[metric][2] = max(maxval, prop)
60 uniqvals.add(prop)
61 except:
62 return
64 vals = {
65 "size": [ self.size_prop, 0, 0, set() ], # min, max, unique
66 "color": [ self.color_prop, 0, 0, set() ] # min, max, unique
67 }
69 for node in tree.traverse():
70 if self.size_prop:
71 update_vals("size", node)
73 if self.color_prop:
74 update_vals("color", node)
76 if self.size_prop:
77 self.size_range = vals["size"][1:3]
79 if self.color_prop:
80 unique = vals["color"][3]
81 if len(unique):
82 colors = self.colors or random_color(num=len(unique))
83 if type(colors) == dict:
84 self.colors = colors.copy()
85 else:
86 colors = list(colors)
87 self.colors = {}
88 for idx, value in enumerate(unique):
89 self.colors[value] = colors[idx % len(colors)]
90 if self.legend:
91 tree_style.add_legend(title=self.name,
92 variable="discrete",
93 colormap=self.colors)
94 else:
95 self.color_range = vals["color"][1:3]
96 if self.legend:
97 tree_style.add_legend(title=self.name,
98 variable="continuous",
99 value_range=self.color_range,
100 color_range=self.color_gradient)
102 def get_size(self, node):
103 if not self.size_prop:
104 return self.width
105 minval, maxval = self.size_range
106 return float(node.props.get(self.size_prop, 0)) / float(maxval) * self.width
108 def get_color(self, node):
109 if not self.color_prop:
110 return self.color
112 prop = node.props.get(self.color_prop)
113 if prop is None:
114 return None
116 if self.color_range:
117 minval, maxval = self.color_range
118 mix = (prop - minval) / (maxval - minval)
119 return interpolate_colors(*self.color_gradient, mix)
120 else:
121 return self.colors.get(prop)
123 def get_legend(self):
124 return self.legend
127class LayoutBarplot(LayoutPlot):
128 def __init__(self, name=None, width=200, size_prop=None,
129 color_prop=None, position="aligned", column=0,
130 color_gradient=None, color="red", colors=None,
131 padding_x=10, scale=True, legend=True, active=True):
133 name = name or f'Barplot_{size_prop}_{color_prop}'
134 super().__init__(name=name, width=width, size_prop=size_prop,
135 color_prop=color_prop, position=position, column=column,
136 color_gradient=color_gradient, color=color, colors=colors,
137 padding_x=padding_x, scale=scale, legend=legend, active=active)
139 def set_tree_style(self, tree, tree_style):
140 super().set_tree_style(tree, tree_style)
142 if self.scale and self.size_range:
143 scale = ScaleFace(width=self.width, scale_range=self.size_range,
144 formatter='%.2f',
145 padding_x=self.padding_x, padding_y=2)
146 text = TextFace(self.name, max_fsize=11, padding_x=self.padding_x)
147 tree_style.aligned_panel_header.add_face(scale, column=self.column)
148 tree_style.aligned_panel_header.add_face(text, column=self.column)
150 def set_node_style(self, node):
151 width = self.get_size(node)
152 color = self.get_color(node)
153 if width and color:
154 tooltip = ""
155 if node.name:
156 tooltip += f'<b>{node.name}</b><br>'
157 if self.size_prop:
158 tooltip += f'<br>{self.size_prop}: {width}<br>'
159 if self.color_prop:
160 tooltip += f'<br>{self.color_prop}: {color}<br>'
161 face = RectFace(width, None, color=color,
162 tooltip=tooltip, padding_x=self.padding_x)
163 node.add_face(face, position=self.position, column=self.column,
164 collapsed_only=not node.is_leaf)