1# -*- coding: utf-8 -*-2# Copyright 2011 Takeshi KOMIYA3#4# Licensed under the Apache License, Version 2.0 (the "License");5# you may not use this file except in compliance with the License.6# You may obtain a copy of the License at7#8# http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing, software11# distributed under the License is distributed on an "AS IS" BASIS,12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13# See the License for the specific language governing permissions and14# limitations under the License.1516import blockdiag.elements17from blockdiag.utils import XY, Size, images18from blockdiag.utils.logging import warning192021class NodeGroup(blockdiag.elements.NodeGroup):22 pass232425class DiagramNode(blockdiag.elements.DiagramNode):26 def __init__(self, _id):27 super(DiagramNode, self).__init__(_id)2829 self.activated = False30 self.activity = []31 self.activities = []3233 def set_activated(self, value):34 self.activated = True3536 def activate(self, height, index):37 if len(self.activity) <= index:38 self.activity.insert(index, [])3940 if (len(self.activity[index]) > 0 and41 (self.activity[index][-1] != height - 1)):42 self.deactivate(index)4344 self.activity[index].append(height)4546 def deactivate(self, index=None):47 if index is None:48 for i in range(len(self.activity)):49 self.deactivate(i)50 return5152 if self.activity[index]:53 attr = {'lifetime': self.activity[index],54 'level': index}55 self.activities.append(attr)5657 self.activity[index] = []585960class EdgeSeparator(blockdiag.elements.Base):61 basecolor = (208, 208, 208)62 linecolor = (0, 0, 0)636465 def clear(cls):66 super(EdgeSeparator, cls).clear()67 cls.basecolor = (208, 208, 208)68 cls.linecolor = (0, 0, 0)6970 def __init__(self, _type, label):71 super(EdgeSeparator, self).__init__()72 self.label = label73 self.group = None74 self.style = None75 self.color = self.basecolor76 self.order = 07778 if _type == '===':79 self.type = 'divider'80 elif _type == '...':81 self.type = 'delay'828384class DiagramEdge(blockdiag.elements.DiagramEdge):85 notecolor = (255, 182, 193) # LightPink86 label_margin = 28788 # name -> (dir, style, asynchronous)89 ARROW_DEF = {90 'both': ('both', None, False),91 '=>': ('both', None, False),92 'forward': ('forward', None, False),93 '->': ('forward', None, False),94 '-->': ('forward', 'dashed', False),95 '->>': ('forward', None, True),96 '-->>': ('forward', 'dashed', True),97 'back': ('back', None, False),98 '<-': ('back', None, False),99 '<--': ('back', 'dashed', False),100 '<<-': ('back', None, True),101 '<<--': ('back', 'dashed', True)102 }103104105 def clear(cls):106 super(DiagramEdge, cls).clear()107 cls.notecolor = (255, 182, 193)108 cls.label_margin = 2109110111 def set_default_note_color(cls, color):112 color = images.color_to_rgb(color)113 cls.notecolor = color114115116 def set_default_label_margin(cls, margin):117 cls.label_margin = int(margin)118119 def __init__(self, node1, node2):120 super(DiagramEdge, self).__init__(node1, node2)121122 self.leftnote = None123 self.leftnotesize = Size(0, 0)124 self.rightnote = None125 self.rightnotesize = Size(0, 0)126 self.textwidth = 0127 self.textheight = 0128 self.order = 0129 self.activate = True130 self.asynchronous = False131 self.diagonal = False132 self.failed = False133 self.return_label = ''134135136 def left_node(self):137 if self.node1.xy.x <= self.node2.xy.x:138 return self.node1139 else:140 return self.node2141142143 def right_node(self):144 if self.node1.xy.x > self.node2.xy.x:145 return self.node1146 else:147 return self.node2148149150 def direction(self):151 if self.node1.xy.x == self.node2.xy.x:152 direction = 'self'153 elif self.node1.xy.x < self.node2.xy.x:154 # n1 .. n2155 if self.dir == 'forward':156 direction = 'right'157 else:158 direction = 'left'159 else:160 # n2 .. n1161 if self.dir == 'forward':162 direction = 'left'163 else:164 direction = 'right'165166 return direction167168 def set_note(self, value):169 self.rightnote = value170171 def set_diagonal(self, value):172 self.diagonal = True173174 def set_async(self, value):175 self.dir = 'forward'176177 def set_return(self, value):178 self.return_label = value179180 def set_failed(self, value):181 self.failed = True182 self.activate = False183184 def set_activate(self, value):185 self.activate = True186187 def set_noactivate(self, value):188 self.activate = False189190 def set_dir(self, value):191 params = self.ARROW_DEF.get(value.lower())192 if params is None:193 warning("unknown edge dir: %s", value)194 else:195 self.dir, self.style, self.asynchronous = params196197 if self.node1 == self.node2 and self.dir in ('forward', 'back'):198 self.activate = False199200 def set_label_margin(self, margin):201 self.label_margin = int(margin)202203 def to_desctable(self):204 params = (self.dir, self.style, self.asynchronous)205 for arrow_type, settings in self.ARROW_DEF.items():206 if params == settings and not arrow_type.isalpha():207 label = "%s %s %s" % (self.node1.label,208 arrow_type,209 self.node2.label)210 return [label, self.description]211212213class AltBlock(blockdiag.elements.Base):214 basecolor = (0, 0, 0)215 linecolor = (0, 0, 0)216 width = None217 height = None218219220 def clear(cls):221 super(EdgeSeparator, cls).clear()222 cls.basecolor = (0, 0, 0)223 cls.linecolor = (0, 0, 0)224225226 def set_default_linecolor(cls, color):227 color = images.color_to_rgb(color)228 cls.linecolor = color229230 def __init__(self, _type, _id):231 self.type = _type232 self.id = _id233 self.xlevel = 1234 self.ylevel_top = 1235 self.ylevel_bottom = 1236 self.edges = []237 self.color = self.basecolor238239240 def xy(self):241 if len(self.edges) == 0:242 return XY(0, 0)243 else:244 x = min(e.left_node.xy.x for e in self.edges)245 y = min(e.order for e in self.edges) + 1246 return XY(x, y)247248249 def colwidth(self):250 if len(self.edges) == 0:251 return 1252 else:253 x2 = max(e.right_node.xy.x for e in self.edges)254 return x2 - self.xy.x + 1255256257 def colheight(self):258 if len(self.edges) == 0:259 return 1260 else:261 y2 = max(e.order for e in self.edges) + 1262 return y2 - self.xy.y + 1263264265class Diagram(blockdiag.elements.Diagram):266 _DiagramNode = DiagramNode267 _DiagramEdge = DiagramEdge268269 def __init__(self):270 super(Diagram, self).__init__()271272 self.int_attrs.append('edge_length')273274 self.activation = True275 self.autonumber = False276 self.edge_length = None277 self.groups = []278 self.separators = []279 self.altblocks = []280281 def traverse_groups(self, preorder=False):282 return self.groups283284 def set_default_textcolor(self, color):285 super(Diagram, self).set_default_textcolor(color)286287 EdgeSeparator.set_default_text_color(color)288289 def set_default_linecolor(self, color):290 super(Diagram, self).set_default_linecolor(color)291292 color = images.color_to_rgb(color)293 AltBlock.set_default_linecolor(color)294295 def set_default_note_color(self, color):296 color = images.color_to_rgb(color)297 self._DiagramEdge.set_default_note_color(color)298299 def set_default_edge_label_margin(self, margin):300 self._DiagramEdge.set_default_label_margin(margin)301302 def set_default_fontfamily(self, fontfamily):303 super(Diagram, self).set_default_fontfamily(fontfamily)304 EdgeSeparator.set_default_fontfamily(fontfamily)305306 def set_default_fontsize(self, fontsize):307 super(Diagram, self).set_default_fontsize(fontsize)308 EdgeSeparator.set_default_fontsize(fontsize)309310 def set_activation(self, value):311 value = value.lower()312 if value == 'none':313 self.activation = value314 else:315 warning("unknown activation style: %s", value)316317 def set_autonumber(self, value):318 if value.lower() == 'false':319 self.autonumber = False320 else:321 self.autonumber = True322323 def set_edge_height(self, value):324 warning("edge_height is obsoleted; use span_height")325 self.span_height = int(value)