Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 1 | #! /usr/bin/python |
| 2 | |
| 3 | # Copyright 2015 Linaro Limited |
| 4 | # |
| 5 | # This program is free software; you can redistribute it and/or modify |
| 6 | # it under the terms of the GNU General Public License as published by |
| 7 | # the Free Software Foundation; either version 2 of the License, or |
| 8 | # (at your option) any later version. |
| 9 | # |
| 10 | # This program is distributed in the hope that it will be useful, |
| 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | # GNU General Public License for more details. |
| 14 | # |
| 15 | # You should have received a copy of the GNU General Public License |
| 16 | # along with this program; if not, write to the Free Software |
| 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
| 18 | # MA 02110-1301, USA. |
| 19 | # |
| 20 | # Visualisation graphics module for VLANd |
| 21 | # |
| 22 | # This code uses python-gd to generate graphics ready for insertion |
| 23 | # into our web interface. Example code in the self-test at the |
| 24 | # bottom. |
| 25 | |
| 26 | import gd, os, sys |
| 27 | |
| 28 | if __name__ == '__main__': |
| 29 | vlandpath = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0]))) |
| 30 | sys.path.insert(0, vlandpath) |
| 31 | sys.path.insert(0, "%s/.." % vlandpath) |
| 32 | |
| 33 | from errors import InputError |
| 34 | |
| 35 | class Graphics: |
| 36 | """ Code and config for the visualisation graphics module """ |
| 37 | |
| 38 | font = None |
| 39 | |
| 40 | # Default font size for the small labels |
| 41 | small_font_size = 12 |
| 42 | |
| 43 | # And the size for the top-level label |
| 44 | label_font_size = 24 |
| 45 | |
| 46 | # Size in pixels of that font, calculated later |
| 47 | twocharwidth = 0 |
| 48 | charheight = 0 |
| 49 | |
| 50 | # How big a gap to leave between trunk connections |
| 51 | trunk_gap = 8 |
| 52 | |
| 53 | # Details of the legend |
| 54 | legend_width = 0 |
| 55 | legend_height = 0 |
| 56 | legend_text_width = 0 |
| 57 | legend_text_height = 0 |
| 58 | legend_total_width = 0 |
Steve McIntyre | 9ff96bf | 2015-09-23 18:54:53 +0100 | [diff] [blame] | 59 | legend_box_width = 0 |
| 60 | legend_box_height = 0 |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 61 | |
| 62 | # Basic colour definitions used later |
| 63 | colour_defs = {} |
| 64 | colour_defs['black'] = (0, 0, 0) |
| 65 | colour_defs['white'] = (255, 255, 255) |
| 66 | colour_defs['purple'] = (255, 0, 255) |
| 67 | colour_defs['blue'] = (0, 0, 255) |
| 68 | colour_defs['darkgrey'] = (60, 60, 60) |
| 69 | colour_defs['yellow'] = (255, 255, 0) |
| 70 | colour_defs['red'] = (255, 0, 0) |
| 71 | colour_defs['aqua'] = (0, 255, 255) |
| 72 | |
| 73 | pallette = {} |
| 74 | |
| 75 | # colours for the background |
| 76 | pallette['bg_colour'] = 'purple' |
| 77 | pallette['transparent_colour'] = 'purple' |
| 78 | pallette['graphic_label_colour'] = 'black' |
| 79 | |
| 80 | # switch colours |
| 81 | pallette['switch_outline_colour'] = 'black' |
| 82 | pallette['switch_fill_colour'] = 'darkgrey' |
| 83 | pallette['switch_label_colour'] = 'white' |
| 84 | |
| 85 | # verious sets of port colours, matching the 'highlight' options in |
| 86 | # draw_port() |
| 87 | port_pallette = {} |
| 88 | port_pallette['normal'] = {} |
| 89 | port_pallette['normal']['port_box'] = 'white' |
| 90 | port_pallette['normal']['port_bg'] = 'black' |
| 91 | port_pallette['normal']['port_label'] = 'white' |
| 92 | port_pallette['normal']['trace'] = 'black' |
| 93 | |
| 94 | port_pallette['trunk'] = {} |
| 95 | port_pallette['trunk']['port_box'] = 'white' |
| 96 | port_pallette['trunk']['port_bg'] = 'blue' |
| 97 | port_pallette['trunk']['port_label'] = 'yellow' |
| 98 | port_pallette['trunk']['trace'] = 'blue' |
| 99 | |
| 100 | port_pallette['locked'] = {} |
| 101 | port_pallette['locked']['port_box'] = 'white' |
| 102 | port_pallette['locked']['port_bg'] = 'red' |
| 103 | port_pallette['locked']['port_label'] = 'yellow' |
| 104 | port_pallette['locked']['trace'] = 'red' |
| 105 | |
| 106 | port_pallette['VLAN'] = {} |
| 107 | port_pallette['VLAN']['port_box'] = 'white' |
| 108 | port_pallette['VLAN']['port_bg'] = 'aqua' |
| 109 | port_pallette['VLAN']['port_label'] = 'black' |
| 110 | port_pallette['VLAN']['trace'] = 'aqua' |
| 111 | |
| 112 | im = None |
| 113 | |
| 114 | # TODO: make colours configurable, add maybe parsing for |
| 115 | # /etc/X11/rgb.txt to allow people to use arbitrary names? |
| 116 | |
| 117 | # Choose a font for our graphics to use. Pass in a list of fonts |
| 118 | # to be tried, in priority order. |
| 119 | def set_font(self, fontlist): |
| 120 | for font in fontlist: |
| 121 | if os.path.exists(font): |
| 122 | self.font = os.path.abspath(font) |
| 123 | break |
| 124 | |
| 125 | # Work out how big we need to be for the biggest possible text |
| 126 | # in a 2-digit number. Grotty, but we need to know this later. |
| 127 | for value in range (0, 100): |
| 128 | (width, height) = self.get_label_size(repr(value), self.small_font_size) |
| 129 | self.twocharwidth = max(self.twocharwidth, width) |
| 130 | self.charheight = max(self.charheight, height) |
| 131 | |
| 132 | # Now we can also calulate other stuff |
| 133 | self._calc_legend_size() |
| 134 | |
| 135 | # Create a canvas and set things up ready for use |
| 136 | def create_canvas(self, x, y): |
| 137 | im = gd.image((x, y)) |
Steve McIntyre | 9ff96bf | 2015-09-23 18:54:53 +0100 | [diff] [blame] | 138 | |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 139 | # Allocate our colours in the image's colour map |
| 140 | for key in self.colour_defs.iterkeys(): |
| 141 | im.colorAllocate((self.colour_defs[key][0], |
| 142 | self.colour_defs[key][1], |
| 143 | self.colour_defs[key][2])) |
| 144 | |
| 145 | im.fill((0,0), im.colorExact(self.colour_defs[self.pallette['bg_colour']])) |
| 146 | im.colorTransparent(im.colorExact(self.colour_defs[self.pallette['transparent_colour']])) |
| 147 | im.interlace(0) |
| 148 | self.im = im |
| 149 | |
| 150 | # Using our selected font, what dimensions will a particular piece |
| 151 | # of text take? |
| 152 | def get_label_size(self, label, font_size): |
| 153 | tmp_im = gd.image((200, 200)) |
| 154 | (llx, lly, lrx, lry, urx, ury, ulx, uly) = tmp_im.get_bounding_rect(self.font, |
| 155 | font_size, |
| 156 | 0.0, |
| 157 | (10, 100), label) |
| 158 | width = max(lrx, urx) - min(llx, ulx) |
| 159 | height = max(lly, lry) - min(uly, ury) |
| 160 | return (width, height) |
| 161 | |
| 162 | # Draw a trunk connection between two ports |
| 163 | # |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 164 | # Ports are defined as (ulx,uly),(lrx,lry), top): x, y |
| 165 | # co-ordinates of UL and LR corners, and whether the port is on |
| 166 | # the top or bottom row of a switch, i.e. does the wire come up or |
| 167 | # down when it leaves the port. |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 168 | def draw_trunk(self, trunknum, node1, node2, colour): |
| 169 | for node in (node1, node2): |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 170 | ((ulx,uly),(lrx,lry),top) = node |
| 171 | |
| 172 | # Work out the co-ordinates for a line vertically up or |
| 173 | # down from the edge of the port |
| 174 | x1 = int((ulx + lrx) / 2) |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 175 | x2 = x1 |
| 176 | if (top): |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 177 | y1 = uly |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 178 | y2 = y1 - (self.trunk_gap * (trunknum + 1)) |
| 179 | else: |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 180 | y1 = lry |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 181 | y2 = y1 + (self.trunk_gap * (trunknum + 1)) |
| 182 | # Quick hack - use 2-pixel wide rectangles as thick lines :-) |
| 183 | # First line, vertically up/down from the port |
| 184 | self.im.rectangle((x1-1,y1), (x2,y2), self.im.colorExact(self.colour_defs[colour])) |
| 185 | # Now draw horizontally across to the left margin space |
| 186 | x3 = self.trunk_gap * (trunknum + 1) |
| 187 | self.im.rectangle((x3, y2), (x2,y2+1), self.im.colorExact(self.colour_defs[colour])) |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 188 | |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 189 | # Now join up the trunks vertically |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 190 | ((ulx1,uly1),(lrx1,lry1),top1) = node1 |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 191 | if (top1): |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 192 | y1 = uly1 - self.trunk_gap * (trunknum + 1) |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 193 | else: |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 194 | y1 = lry1 + self.trunk_gap * (trunknum + 1) |
| 195 | ((ulx2,uly2),(lrx2,lry2),top2) = node2 |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 196 | if (top2): |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 197 | y2 = uly2 - self.trunk_gap * (trunknum + 1) |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 198 | else: |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 199 | y2 = lry2 + self.trunk_gap * (trunknum + 1) |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 200 | x1 = self.trunk_gap * (trunknum + 1) |
| 201 | self.im.rectangle((x1, y1), (x1+1,y2), self.im.colorExact(self.colour_defs[colour])) |
| 202 | |
| 203 | # How big is the legend? |
| 204 | def _calc_legend_size(self): |
| 205 | max_width = 0 |
| 206 | max_height = 0 |
| 207 | |
| 208 | for value in self.port_pallette.iterkeys(): |
| 209 | (width, height) = self.get_label_size(value, self.small_font_size) |
| 210 | max_width = max(max_width, width) |
| 211 | max_height = max(max_height, height) |
| 212 | |
| 213 | (width, height) = self.get_label_size('##', self.small_font_size) |
| 214 | self.legend_box_width = width + 6 |
Steve McIntyre | 9ff96bf | 2015-09-23 18:54:53 +0100 | [diff] [blame] | 215 | self.legend_box_height = height + 6 |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 216 | self.legend_width = max_width + self.legend_box_width + 10 |
| 217 | self.legend_height = 3 + self.legend_box_height + 3 |
| 218 | self.legend_text_width = max_width |
| 219 | self.legend_text_height = max_height |
| 220 | self.legend_total_width = 6 + (len(self.port_pallette) * self.legend_width) |
| 221 | |
| 222 | # Return the legend dimensions |
| 223 | def get_legend_dimensions(self): |
| 224 | return (self.legend_total_width, self.legend_height) |
| 225 | |
| 226 | # Draw the legend using (left, top) as the top left corner |
| 227 | def draw_legend(self, left, top): |
| 228 | lrx = left + self.legend_total_width - 1 |
| 229 | lry = top + self.legend_height - 1 |
| 230 | self.im.rectangle((left, top), (lrx, lry), |
| 231 | self.im.colorExact(self.colour_defs[self.pallette['switch_outline_colour']]), |
| 232 | self.im.colorExact(self.colour_defs[self.pallette['switch_fill_colour']])) |
| 233 | curr_x = left + 3 |
| 234 | curr_y = top + 3 |
| 235 | |
| 236 | for value in sorted(self.port_pallette): |
| 237 | box_colour = self.im.colorExact(self.colour_defs[self.port_pallette[value]['port_box']]) |
| 238 | box_bg_colour = self.im.colorExact(self.colour_defs[self.port_pallette[value]['port_bg']]) |
| 239 | text_colour = self.im.colorExact(self.colour_defs[self.port_pallette[value]['port_label']]) |
| 240 | lrx = curr_x + self.legend_box_width - 1 |
| 241 | lry = curr_y + self.legend_box_height - 1 |
| 242 | self.im.rectangle((curr_x,curr_y), (lrx,lry), box_colour, box_bg_colour) |
Steve McIntyre | 9ff96bf | 2015-09-23 18:54:53 +0100 | [diff] [blame] | 243 | |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 244 | llx = curr_x + 4 |
| 245 | lly = curr_y + self.legend_box_height - 4 |
| 246 | self.im.string_ttf(self.font, self.small_font_size, 0.0, (llx, lly), '##', text_colour) |
| 247 | curr_x += self.legend_box_width |
| 248 | self.im.string_ttf(self.font, self.small_font_size, 0.0, (curr_x + 3, lly), value, |
| 249 | self.im.colorExact(self.colour_defs[self.pallette['switch_label_colour']])) |
| 250 | curr_x += self.legend_text_width + 10 |
| 251 | |
| 252 | # Draw the graphic's label using (left, top) as the top left |
| 253 | # corner with a box around |
| 254 | def draw_label(self, left, top, label, gap): |
| 255 | box_colour = self.im.colorExact(self.colour_defs[self.pallette['switch_label_colour']]) |
| 256 | box_bg_colour = self.im.colorExact(self.colour_defs[self.pallette['switch_fill_colour']]) |
| 257 | text_colour = self.im.colorExact(self.colour_defs[self.pallette['switch_label_colour']]) |
| 258 | (width, height) = self.get_label_size(label, self.label_font_size) |
| 259 | curr_x = left |
| 260 | curr_y = top |
| 261 | lrx = curr_x + width + gap |
| 262 | lry = curr_y + height + 20 |
| 263 | self.im.rectangle((curr_x,curr_y), (lrx,lry), box_colour, box_bg_colour) |
| 264 | curr_x = left + 10 |
| 265 | curr_y = top + height + 6 |
| 266 | self.im.string_ttf(self.font, self.label_font_size, 0.0, (curr_x, curr_y), label, text_colour) |
| 267 | |
| 268 | |
| 269 | class Switch: |
| 270 | """ Code and config for dealing with a switch """ |
| 271 | port_width = 0 |
| 272 | port_height = 0 |
| 273 | text_width = 0 |
| 274 | text_height = 0 |
| 275 | label_left = 0 |
| 276 | label_bot = 0 |
| 277 | total_width = 0 |
| 278 | total_height = 0 |
| 279 | num_ports = 0 |
| 280 | left = None |
| 281 | top = None |
| 282 | name = None |
| 283 | |
| 284 | # Set up a new switch instance; calculate all the sizes so we can |
| 285 | # size our canvas |
| 286 | def __init__(self, g, num_ports, name): |
| 287 | self.num_ports = num_ports |
| 288 | self.name = name |
| 289 | self._calc_port_size(g) |
| 290 | self._calc_switch_size(g) |
| 291 | |
| 292 | # How big is a port and the text within it? |
| 293 | def _calc_port_size(self, g): |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 294 | self.text_width = g.twocharwidth |
| 295 | self.text_height = g.charheight |
| 296 | # Leave enough space around the text for a nice clear box |
| 297 | self.port_width = self.text_width + 6 |
| 298 | self.port_height = self.text_height + 6 |
| 299 | |
| 300 | # How big is the full switch, including all the ports and the |
| 301 | # switch name label? |
| 302 | def _calc_switch_size(self, g): |
| 303 | (label_width, label_height) = g.get_label_size(self.name, g.small_font_size) |
| 304 | num_ports = self.num_ports |
| 305 | # Make sure we have an even number for 2 rows |
| 306 | if (self.num_ports & 1): |
| 307 | num_ports += 1 |
| 308 | self.label_left = 3 + (num_ports * self.port_width / 2) + 3 |
| 309 | self.label_bot = self.port_height - 2 |
| 310 | self.total_width = self.label_left + label_width + 3 |
| 311 | self.total_height = 3 + max(label_height, (2 * self.port_height)) + 3 |
| 312 | |
| 313 | # Return the switch dimensions |
| 314 | def get_dimensions(self): |
| 315 | return (self.total_width, self.total_height) |
| 316 | |
| 317 | # Draw the basic switch outline and label using (left, top) as the |
| 318 | # top left corner. The switch object will remember this origin for |
| 319 | # later use when drawing ports. |
| 320 | def draw_switch(self, g, left, top): |
| 321 | self.left = left |
| 322 | self.top = top |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 323 | lrx = left + self.total_width -1 |
| 324 | lry = top + self.total_height - 1 |
| 325 | g.im.rectangle((left, top), (lrx, lry), |
| 326 | g.im.colorExact(g.colour_defs[g.pallette['switch_outline_colour']]), |
| 327 | g.im.colorExact(g.colour_defs[g.pallette['switch_fill_colour']])) |
| 328 | llx = left + self.label_left |
| 329 | lly = top + self.label_bot |
| 330 | g.im.string_ttf(g.font, g.small_font_size, 0.0, (llx, lly), self.name, |
| 331 | g.im.colorExact(g.colour_defs[g.pallette['switch_label_colour']])) |
| 332 | |
| 333 | # Draw a port inside the switch, using a specified colour scheme |
| 334 | # to denote its type. The switch outline must have been drawn |
| 335 | # first, for its origin to be set. |
| 336 | def draw_port(self, g, portnum, highlight): |
| 337 | if portnum < 1 or portnum > self.num_ports: |
| 338 | raise InputError('port number out of range') |
| 339 | if not self.left or not self.top: |
| 340 | raise InputError('cannot draw ports before switch is drawn') |
| 341 | if highlight not in g.port_pallette.iterkeys(): |
| 342 | raise InputError('unknown highlight type \"%s\"' % highlight) |
| 343 | |
| 344 | box_colour = g.im.colorExact(g.colour_defs[g.port_pallette[highlight]['port_box']]) |
| 345 | box_bg_colour = g.im.colorExact(g.colour_defs[g.port_pallette[highlight]['port_bg']]) |
| 346 | text_colour = g.im.colorExact(g.colour_defs[g.port_pallette[highlight]['port_label']]) |
| 347 | |
| 348 | if (portnum & 1): # odd port number, so top row |
| 349 | ulx = self.left + 3 + ((portnum-1) * self.port_width / 2) |
| 350 | uly = self.top + 3 |
| 351 | else: # bottom row |
| 352 | ulx = self.left + 3 + ((portnum-2) * self.port_width / 2) |
| 353 | uly = self.top + 3 + self.port_height |
| 354 | lrx = ulx + self.port_width - 1 |
| 355 | lry = uly + self.port_height - 1 |
| 356 | g.im.rectangle((ulx,uly), (lrx,lry), box_colour, box_bg_colour) |
| 357 | |
| 358 | # centre the text |
| 359 | (width, height) = g.get_label_size(repr(portnum), g.small_font_size) |
| 360 | llx = ulx + 3 + (self.text_width - width) / 2 |
| 361 | lly = uly + max(height, self.text_height) + 1 |
| 362 | g.im.string_ttf(g.font, g.small_font_size, |
| 363 | 0.0, (llx, lly), repr(portnum), text_colour) |
| 364 | |
| 365 | # Quick helper: draw all the ports for a switch in the default |
| 366 | # colour scheme. |
| 367 | def draw_default_ports(self, g): |
| 368 | for portnum in range(1, self.num_ports + 1): |
| 369 | self.draw_port(g, portnum, 'normal') |
| 370 | |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 371 | # Get the (x,y) co-ordinates of the UL and LR edges of the port |
| 372 | # box, and if it's upper row. This lets us so useful things such |
| 373 | # as draw a connection to that point for a trunk. |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 374 | def get_port_location(self, portnum): |
| 375 | if portnum > self.num_ports: |
| 376 | raise InputError('port number out of range') |
| 377 | |
| 378 | if (portnum & 1): # odd port number, so top row |
| 379 | ulx = self.left + 3 + ((portnum-1) * self.port_width / 2) |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 380 | uly = self.top |
| 381 | lrx = ulx + self.port_width |
| 382 | lry = uly + self.port_height |
| 383 | return ((ulx,uly), (lrx,lry), True) |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 384 | else: # bottom row |
| 385 | ulx = self.left + 3 + ((portnum-2) * self.port_width / 2) |
| 386 | uly = self.top + 3 + self.port_height |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 387 | lrx = ulx + self.port_width |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 388 | lry = uly + self.port_height |
Steve McIntyre | c7d3f85 | 2015-10-26 16:37:26 +0000 | [diff] [blame] | 389 | return ((ulx,uly), (lrx,lry), False) |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 390 | |
| 391 | # Debug: print some of the state of the switch object |
| 392 | def dump_state(self): |
| 393 | print 'port_width %d' % self.port_width |
| 394 | print 'port_height %d' % self.port_height |
| 395 | print 'text_width %d' % self.text_width |
| 396 | print 'text_height %d' % self.text_height |
| 397 | print 'label_left %d' % self.label_left |
| 398 | print 'label_bot %d' % self.label_bot |
| 399 | print 'total_width %d' % self.total_width |
| 400 | print 'total_height %d' % self.total_height |
| 401 | |
| 402 | # Test harness - generate a PNG using fake data |
| 403 | if __name__ == '__main__': |
| 404 | gim = Graphics() |
| 405 | gim.set_font(['/usr/share/fonts/truetype/inconsolata/Inconsolata.otf', |
| 406 | '/usr/share/fonts/truetype/freefont/FreeMono.ttf']) |
| 407 | try: |
| 408 | gim.font |
| 409 | except NameError: |
| 410 | print 'no fonts found' |
| 411 | sys.exit(1) |
| 412 | |
| 413 | switch = {} |
| 414 | size_x = {} |
| 415 | size_y = {} |
| 416 | switch[0] = Switch(gim, 48, 'lngswitch01') |
| 417 | switch[1] = Switch(gim, 24, 'lngswitch02') |
| 418 | switch[2] = Switch(gim, 52, 'lngswitch03') |
| 419 | label = "VLAN 4jj" |
Steve McIntyre | 9ff96bf | 2015-09-23 18:54:53 +0100 | [diff] [blame] | 420 | |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 421 | # Need to set gaps big enough for the number of trunks, at least. |
| 422 | num_trunks = 3 |
| 423 | y_gap = max(20, 15 * num_trunks) |
| 424 | x_gap = max(20, 15 * num_trunks) |
Steve McIntyre | 9ff96bf | 2015-09-23 18:54:53 +0100 | [diff] [blame] | 425 | |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 426 | x = 0 |
| 427 | y = y_gap |
| 428 | |
| 429 | for i in range (0, 3): |
| 430 | (size_x[i], size_y[i]) = switch[i].get_dimensions() |
| 431 | x = max(x, size_x[i]) |
| 432 | y += size_y[i] + y_gap |
| 433 | |
| 434 | # Add space for the legend and the label |
| 435 | (legend_width, legend_height) = gim.get_legend_dimensions() |
| 436 | (label_width, label_height) = gim.get_label_size(label, gim.label_font_size) |
| 437 | |
| 438 | x = max(x, legend_width + 2*x_gap + label_width) |
| 439 | x = x_gap + x + x_gap |
Steve McIntyre | 9ff96bf | 2015-09-23 18:54:53 +0100 | [diff] [blame] | 440 | y = y + max(legend_height + y_gap, label_height) |
| 441 | |
Steve McIntyre | 2454bf0 | 2015-09-23 18:33:02 +0100 | [diff] [blame] | 442 | gim.create_canvas(x, y) |
| 443 | |
| 444 | curr_y = y_gap |
| 445 | switch[0].draw_switch(gim, x_gap, curr_y) |
| 446 | switch[0].draw_default_ports(gim) |
| 447 | switch[0].draw_port(gim, 2, 'VLAN') |
| 448 | switch[0].draw_port(gim, 5, 'locked') |
| 449 | switch[0].draw_port(gim, 11, 'trunk') |
| 450 | switch[0].draw_port(gim, 44, 'trunk') |
| 451 | curr_y += size_y[0] + y_gap |
| 452 | |
| 453 | switch[1].draw_switch(gim, x_gap, curr_y) |
| 454 | switch[1].draw_default_ports(gim) |
| 455 | switch[1].draw_port(gim, 5, 'VLAN') |
| 456 | switch[1].draw_port(gim, 8, 'locked') |
| 457 | switch[1].draw_port(gim, 13, 'trunk') |
| 458 | switch[1].draw_port(gim, 16, 'trunk') |
| 459 | curr_y += size_y[2] + y_gap |
| 460 | |
| 461 | switch[2].draw_switch(gim, x_gap, curr_y) |
| 462 | switch[2].draw_default_ports(gim) |
| 463 | switch[2].draw_port(gim, 1, 'trunk') |
| 464 | switch[2].draw_port(gim, 2, 'locked') |
| 465 | switch[2].draw_port(gim, 14, 'trunk') |
| 466 | switch[2].draw_port(gim, 19, 'VLAN') |
| 467 | curr_y += size_y[2] + y_gap |
| 468 | |
| 469 | # Now let's try and draw some trunks! |
| 470 | gim.draw_trunk(0, |
| 471 | switch[0].get_port_location(11), |
| 472 | switch[1].get_port_location(16), |
| 473 | gim.port_pallette['trunk']['trace']) |
| 474 | gim.draw_trunk(1, |
| 475 | switch[1].get_port_location(13), |
| 476 | switch[2].get_port_location(1), |
| 477 | gim.port_pallette['trunk']['trace']) |
| 478 | gim.draw_trunk(2, |
| 479 | switch[0].get_port_location(44), |
| 480 | switch[2].get_port_location(14), |
| 481 | gim.port_pallette['trunk']['trace']) |
| 482 | |
| 483 | gim.draw_legend(x_gap, curr_y) |
| 484 | gim.draw_label(x - label_width - 2*x_gap, curr_y, label, int(x_gap / 2)) |
| 485 | |
| 486 | f=open('xx.png','w') |
| 487 | gim.im.writePng(f) |
| 488 | f.close() |
| 489 | print 'Test graphic written to xx.png' |