1#!/usr/bin/env python3 2## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- 3 4import cairo 5import sys 6import re 7import gtk 8 9 10## DataRange class 11class DataRange: 12 ## @var start 13 # start 14 ## @var end 15 # end 16 ## @var value 17 # value 18 def __init__(self, start = 0, end = 0, value = ''): 19 """! Initializer 20 @param self this object 21 @param start start 22 @param end end 23 @param value value 24 @return none 25 """ 26 self.start = start 27 self.end = end 28 self.value = value 29## EventString class 30class EventString: 31 ## @var at 32 # at 33 ## @var value 34 # value 35 def __init__(self, at = 0, value = ''): 36 """! Initializer 37 @param self this object 38 @param at you 39 @param value value 40 @return none 41 """ 42 self.at = at 43 self.value = value 44## EventFloat class 45class EventFloat: 46 ## @var at 47 # at 48 ## @var value 49 # value 50 def __init__(self, at = 0, value = 0.0): 51 """! Initializer 52 @param self this object 53 @param at you 54 @param value value 55 @return none 56 """ 57 self.at = at 58 self.value = value 59## EventInt class 60class EventInt: 61 ## @var at 62 # at 63 ## @var value 64 # value 65 def __init__(self, at = 0, value = 0.0): 66 """! Initializer 67 @param self this object 68 @param at you 69 @param value value 70 @return none 71 """ 72 self.at = at 73 self.value = value 74def ranges_cmp(a, b): 75 diff = a.start - b.start 76 if diff < 0: 77 return -1 78 elif diff > 0: 79 return +1 80 else: 81 return 0 82def events_cmp(a, b): 83 diff = a.at - b.at 84 if diff < 0: 85 return -1 86 elif diff > 0: 87 return +1 88 else: 89 return 0 90## TimelineDataRange 91class TimelineDataRange: 92 ## @var name 93 # name 94 ## @var ranges 95 # ranges 96 def __init__(self, name = ''): 97 """! Initializer 98 @param self this object 99 @param name name 100 @return none 101 """ 102 self.name = name 103 self.ranges = [] 104 return 105 def __search(self, key): 106 """! Search 107 @param self this object 108 @param key key 109 @return index if found or -1 if not found 110 """ 111 l = 0 112 u = len(self.ranges)-1 113 while l <= u: 114 i = int((l + u) / 2) 115 if key >= self.ranges[i].start and key <= self.ranges[i].end: 116 return i 117 elif key < self.ranges[i].start: 118 u = i - 1 119 else: 120 # key > self.ranges[i].end 121 l = i + 1 122 return - 1 123 def add_range(self, range): 124 """! Add range 125 @param self this object 126 @param range range 127 @return none 128 """ 129 self.ranges.append(range) 130 def get_all(self): 131 """! Get all ranges 132 @param self this object 133 @return the ranges 134 """ 135 return self.ranges 136 def get_ranges(self, start, end): 137 """! Get selected ranges 138 @param self this object 139 @param start range start 140 @param end range end 141 @return the range or and empty list 142 """ 143 s = self.__search(start) 144 e = self.__search(end) 145 if s == -1 and e == -1: 146 return [] 147 elif s == -1: 148 return self.ranges[0:e + 1] 149 elif e == -1: 150 return self.ranges[s:len(self.ranges)] 151 else: 152 return self.ranges[s:e + 1] 153 def get_ranges_bounds(self, start, end): 154 """! Get ranges bounds 155 @param self this object 156 @param start range start 157 @param end range end 158 @return range 159 """ 160 s = self.__search(start) 161 e = self.__search(end) 162 if s == -1 and e == -1: 163 return(0, 0) 164 elif s == -1: 165 return(0, e + 1) 166 elif e == -1: 167 return(s, len(self.ranges)) 168 else: 169 return(s, e + 1) 170 def sort(self): 171 """! Sort ranges 172 @param self this object 173 @return none 174 """ 175 self.ranges.sort(ranges_cmp) 176 def get_bounds(self): 177 """! Get bounds 178 @param self this object 179 @return the bounds 180 """ 181 if len(self.ranges) > 0: 182 lo = self.ranges[0].start 183 hi = self.ranges[len(self.ranges)-1].end 184 return(lo, hi) 185 else: 186 return(0, 0) 187## TimelineEvent class 188class TimelineEvent: 189 ## @var name 190 # name 191 ## @var events 192 # events 193 def __init__(self, name = ''): 194 """! Get ranges bounds 195 @param self this object 196 @param name name 197 @return none 198 """ 199 self.name = name 200 self.events = [] 201 def __search(self, key): 202 """! Search function 203 @param self this object 204 @param key the key 205 @return event index 206 """ 207 l = 0 208 u = len(self.events)-1 209 while l <= u: 210 i = int((l + u) / 2) 211 if key == self.events[i].at: 212 return i 213 elif key < self.events[i].at: 214 u = i - 1 215 else: 216 # key > self.events[i].at 217 l = i + 1 218 return l 219 def add_event(self, event): 220 """! Add Event 221 @param self this object 222 @param event event to add 223 @return none 224 """ 225 self.events.append(event) 226 def get_events(self, start, end): 227 """! Get Events 228 @param self this object 229 @param start starting event 230 @param end ending event 231 @return the events 232 """ 233 s = self.__search(start) 234 e = self.__search(end) 235 return self.events[s:e + 1] 236 def get_events_bounds(self, start, end): 237 """! Get Events Bounds 238 @param self this object 239 @param start starting event 240 @param end ending event 241 @return event bounds 242 """ 243 s = self.__search(start) 244 e = self.__search(end) 245 return(s, e + 1) 246 def sort(self): 247 """! Sort function 248 @param self this object 249 @return none 250 """ 251 self.events.sort(events_cmp) 252 def get_bounds(self): 253 """! Get Bounds 254 @param self this object 255 @return the bounds 256 """ 257 if len(self.events) > 0: 258 lo = self.events[0].at 259 hi = self.events[-1].at 260 return(lo, hi) 261 else: 262 return(0, 0) 263 264## Timeline class 265class Timeline: 266 ## @var name 267 # name 268 ## @var ranges 269 # ranges 270 ## @var event_str 271 # event string 272 ## @var event_int 273 # event int 274 def __init__(self, name = ''): 275 """! Initializer 276 @param self this object 277 @param name name 278 @return none 279 """ 280 self.ranges = [] 281 self.event_str = [] 282 self.event_int = [] 283 self.name = name 284 def get_range(self, name): 285 """! Get range 286 @param self this object 287 @param name name 288 @return the range 289 """ 290 for range in self.ranges: 291 if range.name == name: 292 return range 293 timeline = TimelineDataRange(name) 294 self.ranges.append(timeline) 295 return timeline 296 def get_event_str(self, name): 297 """! Get Event String 298 @param self this object 299 @param name name 300 @return the event string 301 """ 302 for event_str in self.event_str: 303 if event_str.name == name: 304 return event_str 305 timeline = TimelineEvent(name) 306 self.event_str.append(timeline) 307 return timeline 308 def get_event_int(self, name): 309 """! Get Event Int 310 @param self this object 311 @param name name 312 @return eevent int 313 """ 314 for event_int in self.event_int: 315 if event_int.name == name: 316 return event_int 317 timeline = TimelineEvent(name) 318 self.event_int.append(timeline) 319 return timeline 320 def get_ranges(self): 321 """! Get Ranges 322 @param self this object 323 @return the ranges 324 """ 325 return self.ranges 326 def get_events_str(self): 327 """! Get Events string 328 @param self this object 329 @return event string 330 """ 331 return self.event_str 332 def get_events_int(self): 333 """! Get Events int 334 @param self this object 335 @return evrnt int 336 """ 337 return self.event_int 338 def sort(self): 339 """! Sort the ranges and events 340 @param self this object 341 @return none 342 """ 343 for range in self.ranges: 344 range.sort() 345 for event in self.event_int: 346 event.sort() 347 for event in self.event_str: 348 event.sort() 349 def get_bounds(self): 350 """! Get Bounds 351 @param self this object 352 @return the bounds 353 """ 354 lo = 0 355 hi = 0 356 for range in self.ranges: 357 (range_lo, range_hi) = range.get_bounds() 358 if range_lo < lo: 359 lo = range_lo 360 if range_hi > hi: 361 hi = range_hi 362 for event_str in self.event_str: 363 (ev_lo, ev_hi) = event_str.get_bounds() 364 if ev_lo < lo: 365 lo = ev_lo 366 if ev_hi > hi: 367 hi = ev_hi 368 for event_int in self.event_int: 369 (ev_lo, ev_hi) = event_int.get_bounds() 370 if ev_lo < lo: 371 lo = ev_lo 372 if ev_hi > hi: 373 hi = ev_hi 374 return(lo, hi) 375 376## Timelines class 377class Timelines: 378 ## @var timelines 379 # timelines 380 def __init__(self): 381 """ Initializer 382 @param self: this object 383 """ 384 self.timelines = [] 385 def get(self, name): 386 """! Get Timeline 387 @param self this object 388 @param name name 389 @return the timeline for the name 390 """ 391 for timeline in self.timelines: 392 if timeline.name == name: 393 return timeline 394 timeline = Timeline(name) 395 self.timelines.append(timeline) 396 return timeline 397 def get_all(self): 398 """! Get All Timeline 399 @param self this object 400 @return all timelines 401 """ 402 return self.timelines 403 def sort(self): 404 """! Sort the timelines 405 @param self this object 406 @return none 407 """ 408 for timeline in self.timelines: 409 timeline.sort() 410 def get_bounds(self): 411 """! Get Bounds 412 @param self this object 413 @return the bounds for all timelines 414 """ 415 lo = 0 416 hi = 0 417 for timeline in self.timelines: 418 (t_lo, t_hi) = timeline.get_bounds() 419 if t_lo < lo: 420 lo = t_lo 421 if t_hi > hi: 422 hi = t_hi 423 return(lo, hi) 424 def get_all_range_values(self): 425 """! Get All Ranges 426 @param self this object 427 @return the keys for all ranges 428 """ 429 range_values = {} 430 for timeline in self.timelines: 431 for ranges in timeline.get_ranges(): 432 for ran in ranges.get_all(): 433 range_values[ran.value] = 1 434 return range_values.keys() 435 436## Color class 437class Color: 438 ## @var r 439 # red 440 ## @var g 441 # green 442 ## @var b 443 # blue 444 def __init__(self, r = 0.0, g = 0.0, b = 0.0): 445 """! Initializer 446 @param self: this object 447 @param r: red 448 @param g: green 449 @param b: blue 450 @return none 451 """ 452 self.r = r 453 self.g = g 454 self.b = b 455 def set(self, r, g, b): 456 """! Set color 457 @param self: this object 458 @param r: red 459 @param g: green 460 @param b: blue 461 @return none 462 """ 463 self.r = r 464 self.g = g 465 self.b = b 466 467## Colors class 468class Colors: 469 ## @var __colors 470 # colors 471 ## @var default_colors 472 # default colors 473 ## XXX add more 474 default_colors = [Color(1, 0, 0), Color(0, 1, 0), Color(0, 0, 1), Color(1, 1, 0), Color(1, 0, 1), Color(0, 1, 1)] 475 def __init__(self): 476 """! Initializer 477 @param self this object 478 @return none 479 """ 480 self.__colors = {} 481 def add(self, name, color): 482 """! Add 483 @param self this object 484 @param name name of the color 485 @param color color value 486 @return none 487 """ 488 self.__colors[name] = color 489 def lookup(self, name): 490 """! Lookup name 491 @param self this object 492 @param name name 493 @return named color 494 """ 495 if not self.__colors.has_key(name): 496 self.add(name, self.default_colors.pop()) 497 return self.__colors.get(name) 498 499## TopLegendRenderer class 500class TopLegendRenderer: 501 ## @var __padding 502 # padding 503 ## @var __legends 504 # legends 505 ## @var __colors 506 # colors 507 ## @var __width 508 # width 509 ## @var __height 510 # height 511 def __init__(self): 512 """! Initializer 513 @param self this object 514 @return none 515 """ 516 self.__padding = 10 517 def set_padding(self, padding): 518 """! Set padding 519 @param self this object 520 @param padding padding 521 @return none 522 """ 523 self.__padding = padding 524 def set_legends(self, legends, colors): 525 """! Set padding 526 @param self this object 527 @param legends legends 528 @param colors colors 529 @return none 530 """ 531 self.__legends = legends 532 self.__colors = colors 533 def layout(self, width): 534 """! Set padding 535 @param self this object 536 @param width width 537 @return none 538 """ 539 self.__width = width 540 surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1) 541 ctx = cairo.Context(surface) 542 line_height = 0 543 total_height = self.__padding 544 line_used = self.__padding 545 for legend in self.__legends: 546 (t_width, t_height) = ctx.text_extents(legend)[2:4] 547 item_width = self.__padding + self.__padding + t_width + self.__padding 548 item_height = t_height + self.__padding 549 if item_height > line_height: 550 line_height = item_height 551 if line_used + item_width > self.__width: 552 line_used = self.__padding + item_width 553 total_height += line_height 554 else: 555 line_used += item_width 556 x = line_used - item_width 557 total_height += line_height 558 self.__height = total_height 559 560 def get_height(self): 561 """! Set padding 562 @param self this object 563 @return height 564 """ 565 return self.__height 566 def draw(self, ctx): 567 """! Set padding 568 @param self this object 569 @param ctx ctx 570 @return none 571 """ 572 i = 0 573 line_height = 0 574 total_height = self.__padding 575 line_used = self.__padding 576 for legend in self.__legends: 577 (t_width, t_height) = ctx.text_extents(legend)[2:4] 578 item_width = self.__padding + self.__padding + t_width + self.__padding 579 item_height = t_height + self.__padding 580 if item_height > line_height: 581 line_height = item_height 582 if line_used + item_width > self.__width: 583 line_used = self.__padding + item_width 584 total_height += line_height 585 else: 586 line_used += item_width 587 x = line_used - item_width 588 ctx.rectangle(x, total_height, self.__padding, self.__padding) 589 ctx.set_source_rgb(0, 0, 0) 590 ctx.set_line_width(2) 591 ctx.stroke_preserve() 592 ctx.set_source_rgb(self.__colors[i].r, 593 self.__colors[i].g, 594 self.__colors[i].b) 595 ctx.fill() 596 ctx.move_to(x + self.__padding*2, total_height + t_height) 597 ctx.set_source_rgb(0, 0, 0) 598 ctx.show_text(legend) 599 i += 1 600 601 return 602 603## TimelinesRenderer class 604class TimelinesRenderer: 605 ## @var padding 606 # padding 607 ## @var timelines 608 # timelines 609 ## @var colors 610 # colors 611 ## @var start 612 # start 613 ## @var end 614 # end 615 ## @var left_width 616 # left width 617 ## @var right_width 618 # right width 619 ## @var max_text_height 620 # maximum text height 621 ## @var width 622 # width 623 ## @var height 624 # height 625 ## @var grey_background 626 # grey background 627 def __init__(self): 628 """! Initializer 629 @param self this object 630 @return none 631 """ 632 self.padding = 10 633 return 634 def get_height(self): 635 """! Get Height 636 @param self this object 637 @return height 638 """ 639 return self.height 640 def set_timelines(self, timelines, colors): 641 """! Set Timelines 642 @param self this object 643 @param timelines timelines 644 @param colors colors 645 @return none 646 """ 647 self.timelines = timelines 648 self.colors = colors 649 def set_render_range(self, start, end): 650 """! Set Render Range 651 @param self this object 652 @param start start 653 @param end end 654 @return none 655 """ 656 self.start = start 657 self.end = end 658 def get_data_x_start(self): 659 """! Get Data X Start 660 @param self: this object 661 @return X start 662 """ 663 return self.padding / 2 + self.left_width + self.padding + self.right_width + self.padding / 2 664 def layout(self, width): 665 """! Get Data X Start 666 @param self this object 667 @param width width 668 @return none 669 """ 670 surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1) 671 ctx = cairo.Context(surface) 672 max_text_height = ctx.text_extents("ABCDEFGHIJKLMNOPQRSTUVWXYZabcedefghijklmnopqrstuvwxyz0123456789")[3] 673 674 left_width = 0 675 right_width = 0 676 left_n_lines = 0 677 range_n = 0 678 eventint_n = 0 679 eventstr_n = 0 680 for timeline in self.timelines.get_all(): 681 left_n_lines += 1 682 t_width = ctx.text_extents(timeline.name)[2] 683 left_width = max(left_width, t_width) 684 for rang in timeline.get_ranges(): 685 t_width = ctx.text_extents(rang.name)[2] 686 right_width = max(right_width, t_width) 687 range_n += 1 688 for events_int in timeline.get_events_int(): 689 t_width = ctx.text_extents(events_int.name)[2] 690 right_width = max(right_width, t_width) 691 eventint_n += 1 692 for events_str in timeline.get_events_str(): 693 t_width = ctx.text_extents(events_str.name)[2] 694 right_width = max(right_width, t_width) 695 eventstr_n += 1 696 697 left_height = left_n_lines * max_text_height + (left_n_lines - 1) * self.padding 698 right_n_lines = range_n + eventint_n + eventstr_n 699 right_height = (right_n_lines - 1) * self.padding + right_n_lines * max_text_height 700 right_data_height = (eventint_n + eventstr_n) * (max_text_height + 5) + range_n * 10 701 right_data_height += (right_n_lines - 1) * self.padding 702 703 height = max(left_height, right_height) 704 height = max(height, right_data_height) 705 706 self.left_width = left_width 707 self.right_width = right_width 708 self.max_text_height = max_text_height 709 self.width = width 710 self.height = height + self.padding 711 def draw_line(self, ctx, x, y, width, height): 712 """! Draw Line 713 @param self this object 714 @param ctx ctx 715 @param x x 716 @param y y 717 @param width width 718 @param height height 719 @return none 720 """ 721 ctx.move_to(x, y) 722 ctx.rel_line_to(width, height) 723 ctx.close_path() 724 ctx.set_operator(cairo.OPERATOR_SOURCE) 725 ctx.set_line_width(1.0) 726 ctx.set_source_rgb(0, 0, 0) 727 ctx.stroke() 728 def draw_events(self, ctx, events, x, y, width, height): 729 """! Draw Event 730 @param self this object 731 @param ctx ctx 732 @param events events 733 @param x x 734 @param y y 735 @param width width 736 @param height height 737 @return none 738 """ 739 if (self.grey_background % 2) == 0: 740 ctx.rectangle(x, y - self.padding / 2, 741 width, height + self.padding) 742 ctx.set_source_rgb(0.9, 0.9, 0.9) 743 ctx.fill() 744 last_x_drawn = int(x) 745 (lo, hi) = events.get_events_bounds(self.start, self.end) 746 for event in events.events[lo:hi]: 747 real_x = int(x + (event.at - self.start) * width / (self.end - self.start)) 748 if real_x > last_x_drawn + 2: 749 ctx.rectangle(real_x, y, 1, 1) 750 ctx.set_source_rgb(1, 0, 0) 751 ctx.stroke() 752 ctx.move_to(real_x, y + self.max_text_height) 753 ctx.set_source_rgb(0, 0, 0) 754 ctx.show_text(str(event.value)) 755 last_x_drawn = real_x 756 self.grey_background += 1 757 def draw_ranges(self, ctx, ranges, x, y, width, height): 758 """! Draw Ranges 759 @param self this object 760 @param ctx ctx 761 @param ranges ranges 762 @param x x 763 @param y y 764 @param width width 765 @param height height 766 @return none 767 """ 768 if (self.grey_background % 2) == 0: 769 ctx.rectangle(x, y - self.padding / 2, 770 width, height + self.padding) 771 ctx.set_source_rgb(0.9, 0.9, 0.9) 772 ctx.fill() 773 last_x_drawn = int(x - 1) 774 (lo, hi) = ranges.get_ranges_bounds(self.start, self.end) 775 for data_range in ranges.ranges[lo:hi]: 776 s = max(data_range.start, self.start) 777 e = min(data_range.end, self.end) 778 x_start = int(x + (s - self.start) * width / (self.end - self.start)) 779 x_end = int(x + (e - self.start) * width / (self.end - self.start)) 780 if x_end > last_x_drawn: 781 ctx.rectangle(x_start, y, x_end - x_start, 10) 782 ctx.set_source_rgb(0, 0, 0) 783 ctx.stroke_preserve() 784 color = self.colors.lookup(data_range.value) 785 ctx.set_source_rgb(color.r, color.g, color.b) 786 ctx.fill() 787 last_x_drawn = x_end 788 789 self.grey_background += 1 790 791 def draw(self, ctx): 792 """! Draw 793 @param self this object 794 @param ctx ctx 795 @return none 796 """ 797 timeline_top = 0 798 top_y = self.padding / 2 799 left_x_start = self.padding / 2 800 left_x_end = left_x_start + self.left_width 801 right_x_start = left_x_end + self.padding 802 right_x_end = right_x_start + self.right_width 803 data_x_start = right_x_end + self.padding / 2 804 data_x_end = self.width 805 data_width = data_x_end - data_x_start 806 cur_y = top_y 807 self.draw_line(ctx, 0, 0, self.width, 0) 808 self.grey_background = 1 809 for timeline in self.timelines.get_all(): 810 (y_bearing, t_width, t_height) = ctx.text_extents(timeline.name)[1:4] 811 ctx.move_to(left_x_start, cur_y + self.max_text_height - (t_height + y_bearing)) 812 ctx.show_text(timeline.name); 813 for events_int in timeline.get_events_int(): 814 (y_bearing, t_width, t_height) = ctx.text_extents(events_int.name)[1:4] 815 ctx.move_to(right_x_start, cur_y + self.max_text_height - (t_height + y_bearing)) 816 ctx.show_text(events_int.name) 817 self.draw_events(ctx, events_int, data_x_start, cur_y, data_width, self.max_text_height + 5) 818 cur_y += self.max_text_height + 5 + self.padding 819 self.draw_line(ctx, right_x_start - self.padding / 2, cur_y - self.padding / 2, 820 self.right_width + self.padding, 0) 821 822 for events_str in timeline.get_events_str(): 823 (y_bearing, t_width, t_height) = ctx.text_extents(events_str.name)[1:4] 824 ctx.move_to(right_x_start, cur_y + self.max_text_height - (t_height + y_bearing)) 825 ctx.show_text(events_str.name) 826 self.draw_events(ctx, events_str, data_x_start, cur_y, data_width, self.max_text_height + 5) 827 cur_y += self.max_text_height + 5 + self.padding 828 self.draw_line(ctx, right_x_start - self.padding / 2, cur_y - self.padding / 2, 829 self.right_width + self.padding, 0) 830 for ranges in timeline.get_ranges(): 831 (y_bearing, t_width, t_height) = ctx.text_extents(ranges.name)[1:4] 832 ctx.move_to(right_x_start, cur_y + self.max_text_height - (t_height + y_bearing)) 833 ctx.show_text(ranges.name) 834 self.draw_ranges(ctx, ranges, data_x_start, cur_y, data_width, 10) 835 cur_y += self.max_text_height + self.padding 836 self.draw_line(ctx, right_x_start - self.padding / 2, cur_y - self.padding / 2, 837 self.right_width + self.padding, 0) 838 self.draw_line(ctx, 0, cur_y - self.padding / 2, 839 self.width, 0) 840 bot_y = cur_y - self.padding / 2 841 self.draw_line(ctx, left_x_end + self.padding / 2, 0, 842 0, bot_y) 843 self.draw_line(ctx, right_x_end + self.padding / 2, 0, 844 0, bot_y) 845 return 846 847## ScaleRenderer class 848class ScaleRenderer: 849 ## @var __top 850 # top 851 ## @var __lo 852 # lo 853 ## @var __hi 854 # hi 855 ## @var __delta 856 # delta 857 ## @var __width 858 # width 859 ## @var __height 860 # height 861 ## @var max_text_height 862 # maximum text height 863 def __init__(self): 864 """! Initializer 865 @param self this object 866 @return none 867 """ 868 self.__top = 0 869 return 870 def set_bounds(self, lo, hi): 871 """! Set Bounds 872 @param self this object 873 @param lo lo 874 @param hi hi 875 @return none 876 """ 877 self.__lo = lo 878 self.__hi = hi 879 def get_position(self, x): 880 """! Get Position 881 @param self this object 882 @param x x 883 @return real x 884 """ 885 real_x = (x - self.__lo ) * self.__width / (self.__hi - self.__lo) 886 return real_x 887 def set_top(self): 888 """! Set Top 889 @param self this object 890 @return none 891 """ 892 self.__top = 1 893 def set_bot(self): 894 """! Set Bottom 895 @param self this object 896 @return none 897 """ 898 self.__top = 0 899 def layout(self, width): 900 """! Layout 901 @param self this object 902 @param width width 903 @return none 904 """ 905 surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1) 906 ctx = cairo.Context(surface) 907 908 # calculate scale delta 909 data_delta = self.__hi - self.__lo 910 closest = 1 911 while (closest*10) < data_delta: 912 closest *= 10 913 if (data_delta / closest) == 0: 914 delta = closest 915 elif(data_delta / closest) == 1: 916 delta = closest / 10 917 else: 918 delta = closest 919 start = self.__lo - (self.__lo % delta) + delta 920 end = self.__hi - (self.__hi % delta) 921 922 self.__delta = delta 923 self.__width = width 924 925 # calculate text height 926 max_text_height = ctx.text_extents("ABCDEFGHIJKLMNOPQRSTUVWXYZabcedefghijklmnopqrstuvwxyz0123456789")[3] 927 self.max_text_height = max_text_height 928 height = max_text_height + 10 929 self.__height = height 930 931 def get_height(self): 932 """! Get Height 933 @param self: this object 934 @return height 935 """ 936 return self.__height 937 def draw(self, ctx): 938 """! Draw 939 @param self this object 940 @param ctx ctx 941 @return none 942 """ 943 delta = self.__delta 944 start = self.__lo - (self.__lo % delta) + delta 945 end = self.__hi - (self.__hi % delta) 946 947 if self.__top == 1: 948 s = -1 949 else: 950 s = 1 951 # print scale points 952 ctx.set_source_rgb(0, 0, 0) 953 ctx.set_line_width(1.0) 954 ticks = range(int(start), int(end + delta), int(delta)) 955 for x in ticks: 956 real_x = (x - self.__lo ) * self.__width / (self.__hi - self.__lo) 957 ctx.move_to(real_x, 0) 958 ctx.line_to(real_x, 5*s) 959 ctx.close_path() 960 ctx.stroke() 961 (t_y_bearing, t_width, t_height) = ctx.text_extents(str(x))[1:4] 962 if self.__top: 963 text_delta = t_height + t_y_bearing 964 else: 965 text_delta = -t_y_bearing 966 ctx.move_to(real_x - t_width / 2, (5 + 5 + text_delta)*s) 967 ctx.show_text(str(x)) 968 # draw subticks 969 delta /= 10 970 if delta > 0: 971 start = self.__lo - (self.__lo % delta) + delta 972 end = self.__hi - (self.__hi % delta) 973 for x in range(int(start), int(end + delta), int(delta)): 974 real_x = (x - self.__lo ) * self.__width / (self.__hi - self.__lo) 975 ctx.move_to(real_x, 0) 976 ctx.line_to(real_x, 3*s) 977 ctx.close_path() 978 ctx.stroke() 979 980 981## GraphicRenderer class 982class GraphicRenderer: 983 ## @var __start 984 # start 985 ## @var __end 986 # end 987 ## @var __mid_scale 988 # mid scale 989 ## @var __bot_scale 990 # bottom scale 991 ## @var __width 992 # width 993 ## @var __height 994 # height 995 ## @var __r_start 996 # start 997 ## @var __r_end 998 # end 999 ## @var __data 1000 # data 1001 ## @var __mid_scale 1002 # mid scale 1003 ## @var __top_legend 1004 # top legend 1005 def __init__(self, start, end): 1006 """! Initializer 1007 @param self this object 1008 @param start start 1009 @param end end 1010 @return none 1011 """ 1012 self.__start = float(start) 1013 self.__end = float(end) 1014 self.__mid_scale = ScaleRenderer() 1015 self.__mid_scale.set_top() 1016 self.__bot_scale = ScaleRenderer() 1017 self.__bot_scale.set_bounds(start, end) 1018 self.__bot_scale.set_bot() 1019 self.__width = 1 1020 self.__height = 1 1021 def get_width(self): 1022 """! Get Width 1023 @param self: this object 1024 @return width 1025 """ 1026 return self.__width 1027 def get_height(self): 1028 """! Get Height 1029 @param self this object 1030 @return height 1031 """ 1032 return self.__height 1033 # return x, y, width, height 1034 def get_data_rectangle(self): 1035 """! Get Data Rectangle 1036 @param self this object 1037 @return rectangle 1038 """ 1039 y_start = self.__top_legend.get_height() 1040 x_start = self.__data.get_data_x_start() 1041 return(x_start, y_start, self.__width - x_start, self.__data.get_height()) 1042 def scale_data(self, x): 1043 """! Get Data Rectangle 1044 @param self this object 1045 @param x x 1046 @return scaled x 1047 """ 1048 x_start = self.__data.get_data_x_start() 1049 x_scaled = x / (self.__width - x_start) * (self.__r_end - self.__r_start) 1050 return x_scaled 1051 # return x, y, width, height 1052 def get_selection_rectangle(self): 1053 """! Get Selection Rectangle 1054 @param self this object 1055 @return rectangle 1056 """ 1057 y_start = self.__top_legend.get_height() + self.__data.get_height() + self.__mid_scale.get_height() + 20 1058 y_height = self.__bot_scale.get_height() + 20 1059 x_start = self.__bot_scale.get_position(self.__r_start) 1060 x_end = self.__bot_scale.get_position(self.__r_end) 1061 return(x_start, y_start, x_end - x_start, y_height) 1062 def scale_selection(self, x): 1063 """! Scale Selection 1064 @param self this object 1065 @param x the X 1066 @return scaled X 1067 """ 1068 x_scaled = x / self.__width * (self.__end - self.__start) 1069 return x_scaled 1070 def set_range(self, start, end): 1071 """! Set Range 1072 @param self this object 1073 @param start start 1074 @param end end 1075 @return none 1076 """ 1077 s = min(start, end) 1078 e = max(start, end) 1079 start = max(self.__start, s) 1080 end = min(self.__end, e) 1081 self.__r_start = start 1082 self.__r_end = end 1083 self.__data.set_render_range(start, end) 1084 self.__mid_scale.set_bounds(start, end) 1085 self.layout(self.__width, self.__height) 1086 def get_range(self): 1087 """! Get Range 1088 @param self this object 1089 @return range 1090 """ 1091 return(self.__r_start, self.__r_end) 1092 def set_data(self, data): 1093 """! Set Date 1094 @param self this object 1095 @param data data 1096 @return none 1097 """ 1098 self.__data = data 1099 def set_top_legend(self, top_legend): 1100 """! Set Top Legend 1101 @param self this object 1102 @param top_legend The legend 1103 @return none 1104 """ 1105 self.__top_legend = top_legend 1106 def layout(self, width, height): 1107 """! Set Layout 1108 @param self this object 1109 @param width width 1110 @param height height 1111 @return none 1112 """ 1113 self.__width = width 1114 self.__height = height 1115 self.__top_legend.layout(width) 1116 top_legend_height = self.__top_legend.get_height() 1117 self.__data.layout(width) 1118 self.__mid_scale.layout(width - self.__data.get_data_x_start()) 1119 self.__bot_scale.layout(width) 1120 return 1121 def __x_pixel(self, x, width): 1122 """! X Pixel 1123 @param self this object 1124 @param x x 1125 @param width width 1126 @return x pixel 1127 """ 1128 new_x = (x - self.__start) * width / (self.__end - self.__start) 1129 return new_x 1130 1131 def draw(self, ctx): 1132 """! Draw 1133 @param self this object 1134 @param ctx ctx 1135 @return none 1136 """ 1137 # default background is white 1138 ctx.save() 1139 ctx.set_source_rgb(1, 1, 1) 1140 ctx.set_operator(cairo.OPERATOR_SOURCE) 1141 ctx.rectangle(0, 0, self.__width, self.__height) 1142 ctx.fill() 1143 1144 # top legend 1145 ctx.save() 1146 self.__top_legend.draw(ctx) 1147 top_legend_height = self.__top_legend.get_height() 1148 ctx.restore() 1149 1150 # separation line 1151 ctx.move_to(0, top_legend_height) 1152 ctx.line_to(self.__width, top_legend_height) 1153 ctx.close_path() 1154 ctx.set_line_width(2) 1155 ctx.set_source_rgb(0, 0, 0) 1156 ctx.stroke() 1157 1158 # data 1159 ctx.save() 1160 ctx.translate(0, 1161 top_legend_height) 1162 self.__data.draw(ctx) 1163 ctx.restore() 1164 1165 # scale below data 1166 ctx.save() 1167 ctx.translate(self.__data.get_data_x_start(), 1168 top_legend_height + self.__data.get_height() + self.__mid_scale.get_height()) 1169 self.__mid_scale.draw(ctx) 1170 ctx.restore() 1171 1172 height_used = top_legend_height + self.__data.get_height() + self.__mid_scale.get_height() 1173 1174 # separation between scale and left pane 1175 ctx.move_to(self.__data.get_data_x_start(), height_used) 1176 ctx.rel_line_to(0, -self.__mid_scale.get_height()) 1177 ctx.close_path() 1178 ctx.set_source_rgb(0, 0, 0) 1179 ctx.set_line_width(2) 1180 ctx.stroke() 1181 1182 # separation below scale 1183 ctx.move_to(0, height_used) 1184 ctx.line_to(self.__width, height_used) 1185 ctx.close_path() 1186 ctx.set_line_width(2) 1187 ctx.set_source_rgb(0, 0, 0) 1188 ctx.stroke() 1189 1190 select_start = self.__bot_scale.get_position(self.__r_start) 1191 select_end = self.__bot_scale.get_position(self.__r_end) 1192 1193 # left connection between top scale and bottom scale 1194 ctx.move_to(0, height_used); 1195 ctx.line_to(self.__data.get_data_x_start(), height_used) 1196 ctx.line_to(select_start, height_used + 20) 1197 ctx.line_to(0, height_used + 20) 1198 ctx.line_to(0, height_used) 1199 ctx.set_source_rgb(0, 0, 0) 1200 ctx.set_line_width(1) 1201 ctx.stroke_preserve() 1202 ctx.set_source_rgb(0.9, 0.9, 0.9) 1203 ctx.fill() 1204 1205 # right connection between top scale and bottom scale 1206 ctx.move_to(self.__width, height_used) 1207 ctx.line_to(self.__width, height_used + 20) 1208 ctx.line_to(select_end, height_used + 20) 1209 ctx.line_to(self.__width, height_used) 1210 ctx.set_source_rgb(0, 0, 0) 1211 ctx.set_line_width(1) 1212 ctx.stroke_preserve() 1213 ctx.set_source_rgb(0.9, 0.9, 0.9) 1214 ctx.fill() 1215 1216 height_used += 20 1217 1218 # unused area background 1219 unused_start = self.__bot_scale.get_position(self.__r_start) 1220 unused_end = self.__bot_scale.get_position(self.__r_end) 1221 unused_height = self.__bot_scale.get_height() + 20 1222 ctx.rectangle(0, height_used, 1223 unused_start, 1224 unused_height) 1225 ctx.rectangle(unused_end, 1226 height_used, 1227 self.__width - unused_end, 1228 unused_height) 1229 ctx.set_source_rgb(0.9, 0.9, 0.9) 1230 ctx.fill() 1231 1232 # border line around bottom scale 1233 ctx.move_to(unused_end, height_used) 1234 ctx.line_to(self.__width, height_used) 1235 ctx.line_to(self.__width, height_used + unused_height) 1236 ctx.line_to(0, height_used + unused_height) 1237 ctx.line_to(0, height_used) 1238 ctx.line_to(unused_start, height_used) 1239 ctx.close_path() 1240 ctx.set_line_width(2) 1241 ctx.set_source_rgb(0, 0, 0) 1242 ctx.stroke() 1243 ctx.move_to(unused_start, height_used) 1244 ctx.line_to(unused_end, height_used) 1245 ctx.close_path() 1246 ctx.set_line_width(1) 1247 ctx.set_source_rgb(0.9, 0.9, 0.9) 1248 ctx.stroke() 1249 1250 # unused area dot borders 1251 ctx.save() 1252 ctx.move_to(max(unused_start, 2), height_used) 1253 ctx.rel_line_to(0, unused_height) 1254 ctx.move_to(min(unused_end, self.__width - 2), height_used) 1255 ctx.rel_line_to(0, unused_height) 1256 ctx.set_dash([5], 0) 1257 ctx.set_source_rgb(0, 0, 0) 1258 ctx.set_line_width(1) 1259 ctx.stroke() 1260 ctx.restore() 1261 1262 # bottom scale 1263 ctx.save() 1264 ctx.translate(0, height_used) 1265 self.__bot_scale.draw(ctx) 1266 ctx.restore() 1267 1268## GtkGraphicRenderer class 1269class GtkGraphicRenderer(gtk.DrawingArea): 1270 ## @var __data 1271 # data 1272 ## @var __moving_left 1273 # moving left 1274 ## @var __moving_right 1275 # moving right 1276 ## @var __moving_both 1277 # moving both 1278 ## @var __moving_top 1279 # moving top 1280 ## @var __force_full_redraw 1281 # full redraw 1282 ## @var __moving_left_cur 1283 # moving left cur 1284 ## @var __moving_right_cur 1285 # moving right cur 1286 ## @var __moving_both_start 1287 # moving both start 1288 ## @var __moving_both_cur 1289 # moving both cur 1290 ## @var __moving_top_cur 1291 # moving top cur 1292 ## @var __moving_top_start 1293 # moving top start 1294 ## @var __width 1295 # width 1296 ## @var __height 1297 # height 1298 ## @var __buffer_surface 1299 # buffer surface 1300 def __init__(self, data): 1301 """! Initializer 1302 @param self this object 1303 @param data data 1304 @return none 1305 """ 1306 super(GtkGraphicRenderer, self).__init__() 1307 self.__data = data 1308 self.__moving_left = False 1309 self.__moving_right = False 1310 self.__moving_both = False 1311 self.__moving_top = False 1312 self.__force_full_redraw = True 1313 self.add_events(gtk.gdk.POINTER_MOTION_MASK) 1314 self.add_events(gtk.gdk.BUTTON_PRESS_MASK) 1315 self.add_events(gtk.gdk.BUTTON_RELEASE_MASK) 1316 self.connect("expose_event", self.expose) 1317 self.connect('size-allocate', self.size_allocate) 1318 self.connect('motion-notify-event', self.motion_notify) 1319 self.connect('button-press-event', self.button_press) 1320 self.connect('button-release-event', self.button_release) 1321 def set_smaller_zoom(self): 1322 """! Set Smaller Zoom 1323 @param self this object 1324 @return none 1325 """ 1326 (start, end) = self.__data.get_range() 1327 self.__data.set_range(start, start + (end - start)*2) 1328 self.__force_full_redraw = True 1329 self.queue_draw() 1330 def set_bigger_zoom(self): 1331 """! Set Bigger Zoom 1332 @param self this object 1333 @return none 1334 """ 1335 (start, end) = self.__data.get_range() 1336 self.__data.set_range(start, start + (end - start) / 2) 1337 self.__force_full_redraw = True 1338 self.queue_draw() 1339 def output_png(self, filename): 1340 """! Output PNG 1341 @param self this object 1342 @param filename file name 1343 @return none 1344 """ 1345 surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1346 self.__data.get_width(), 1347 self.__data.get_height()) 1348 ctx = cairo.Context(self.__buffer_surface) 1349 self.__data.draw(ctx) 1350 surface.write_to_png(filename) 1351 def button_press(self, widget, event): 1352 """! Button Press 1353 @param self this object 1354 @param widget widget 1355 @param event event 1356 @return true if button has been pressed otherwise false 1357 """ 1358 (x, y, width, height) = self.__data.get_selection_rectangle() 1359 (d_x, d_y, d_width, d_height) = self.__data.get_data_rectangle() 1360 if event.y > y and event.y < y + height: 1361 if abs(event.x - x) < 5: 1362 self.__moving_left = True 1363 return True 1364 if abs(event.x - (x + width)) < 5: 1365 self.__moving_right = True 1366 return True 1367 if event.x > x and event.x < x + width: 1368 self.__moving_both = True 1369 self.__moving_both_start = event.x 1370 self.__moving_both_cur = event.x 1371 return True 1372 if event.y > d_y and event.y < (d_y + d_height): 1373 if event.x > d_x and event.x < (d_x + d_width): 1374 self.__moving_top = True 1375 self.__moving_top_start = event.x 1376 self.__moving_top_cur = event.x 1377 return True 1378 return False 1379 def button_release(self, widget, event): 1380 """! Button Release 1381 @param self this object 1382 @param widget widget 1383 @param event event 1384 @return true if button was released otherwise false 1385 """ 1386 if self.__moving_left: 1387 self.__moving_left = False 1388 left = self.__data.scale_selection(self.__moving_left_cur) 1389 right = self.__data.get_range()[1] 1390 self.__data.set_range(left, right) 1391 self.__force_full_redraw = True 1392 self.queue_draw() 1393 return True 1394 if self.__moving_right: 1395 self.__moving_right = False 1396 right = self.__data.scale_selection(self.__moving_right_cur) 1397 left = self.__data.get_range()[0] 1398 self.__data.set_range(left, right) 1399 self.__force_full_redraw = True 1400 self.queue_draw() 1401 return True 1402 if self.__moving_both: 1403 self.__moving_both = False 1404 delta = self.__data.scale_selection(self.__moving_both_cur - self.__moving_both_start) 1405 (left, right) = self.__data.get_range() 1406 self.__data.set_range(left + delta, right + delta) 1407 self.__force_full_redraw = True 1408 self.queue_draw() 1409 return True 1410 if self.__moving_top: 1411 self.__moving_top = False 1412 return False 1413 def motion_notify(self, widget, event): 1414 """! Motion Notify 1415 @param self this object 1416 @param widget widget 1417 @param event event 1418 @return true if moving otherwise false 1419 """ 1420 (x, y, width, height) = self.__data.get_selection_rectangle() 1421 if self.__moving_left: 1422 if event.x <= 0: 1423 self.__moving_left_cur = 0 1424 elif event.x >= x + width: 1425 self.__moving_left_cur = x + width 1426 else: 1427 self.__moving_left_cur = event.x 1428 self.queue_draw_area(0, int(y), int(self.__width), int(height)) 1429 return True 1430 if self.__moving_right: 1431 if event.x >= self.__width: 1432 self.__moving_right = self.__width 1433 elif event.x < x: 1434 self.__moving_right_cur = x 1435 else: 1436 self.__moving_right_cur = event.x 1437 self.queue_draw_area(0, int(y), int(self.__width), int(height)) 1438 return True 1439 if self.__moving_both: 1440 cur_e = self.__width - (x + width - self.__moving_both_start) 1441 cur_s = (self.__moving_both_start - x) 1442 if event.x < cur_s: 1443 self.__moving_both_cur = cur_s 1444 elif event.x > cur_e: 1445 self.__moving_both_cur = cur_e 1446 else: 1447 self.__moving_both_cur = event.x 1448 self.queue_draw_area(0, int(y), int(self.__width), int(height)) 1449 return True 1450 if self.__moving_top: 1451 self.__moving_top_cur = event.x 1452 delta = self.__data.scale_data(self.__moving_top_start - self.__moving_top_cur) 1453 (left, right) = self.__data.get_range() 1454 self.__data.set_range(left + delta, right + delta) 1455 self.__force_full_redraw = True 1456 self.__moving_top_start = event.x 1457 self.queue_draw() 1458 return True 1459 (d_x, d_y, d_width, d_height) = self.__data.get_data_rectangle() 1460 if event.y > y and event.y < y + height: 1461 if abs(event.x - x) < 5 or abs(event.x - (x + width)) < 5: 1462 widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.SB_H_DOUBLE_ARROW)) 1463 return True 1464 if event.x > x and event.x < x + width: 1465 widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) 1466 return True 1467 if event.y > d_y and event.y < (d_y + d_height): 1468 if event.x > d_x and event.x < (d_x + d_width): 1469 widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) 1470 return True 1471 widget.window.set_cursor(None) 1472 return False 1473 def size_allocate(self, widget, allocation): 1474 """! Size Allocate 1475 @param self this object 1476 @param widget widget 1477 @param allocation allocation 1478 @return none 1479 """ 1480 self.__width = allocation.width 1481 self.__height = allocation.height 1482 self.__data.layout(allocation.width, allocation.height) 1483 self.__force_full_redraw = True 1484 self.queue_draw() 1485 def expose(self, widget, event): 1486 """! Expose 1487 @param self this object 1488 @param widget widget 1489 @param event event 1490 @return false 1491 """ 1492 if self.__force_full_redraw: 1493 self.__buffer_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1494 self.__data.get_width(), 1495 self.__data.get_height()) 1496 ctx = cairo.Context(self.__buffer_surface) 1497 self.__data.draw(ctx) 1498 self.__force_full_redraw = False 1499 ctx = widget.window.cairo_create() 1500 ctx.rectangle(event.area.x, event.area.y, 1501 event.area.width, event.area.height) 1502 ctx.clip() 1503 ctx.set_source_surface(self.__buffer_surface) 1504 ctx.paint() 1505 (x, y, width, height) = self.__data.get_selection_rectangle() 1506 if self.__moving_left: 1507 ctx.move_to(max(self.__moving_left_cur, 2), y) 1508 ctx.rel_line_to(0, height) 1509 ctx.close_path() 1510 ctx.set_line_width(1) 1511 ctx.set_source_rgb(0, 0, 0) 1512 ctx.stroke() 1513 if self.__moving_right: 1514 ctx.move_to(min(self.__moving_right_cur, self.__width - 2), y) 1515 ctx.rel_line_to(0, height) 1516 ctx.close_path() 1517 ctx.set_line_width(1) 1518 ctx.set_source_rgb(0, 0, 0) 1519 ctx.stroke() 1520 if self.__moving_both: 1521 delta_x = self.__moving_both_cur - self.__moving_both_start 1522 left_x = x + delta_x 1523 ctx.move_to(x + delta_x, y) 1524 ctx.rel_line_to(0, height) 1525 ctx.close_path() 1526 ctx.move_to(x + width + delta_x, y) 1527 ctx.rel_line_to(0, height) 1528 ctx.close_path() 1529 ctx.set_source_rgb(0, 0, 0) 1530 ctx.set_line_width(1) 1531 ctx.stroke() 1532 return False 1533 1534## MainWindow class 1535class MainWindow: 1536 ## @var __window 1537 # window 1538 ## @var __render 1539 # render 1540 ## @var __dialog 1541 # dialog 1542 def __init__(self): 1543 """! Initializer 1544 @param self this object 1545 @return none 1546 """ 1547 return 1548 def run(self, graphic): 1549 """! Run function 1550 @param self this object 1551 @param graphic graphic 1552 @return none 1553 """ 1554 window = gtk.Window() 1555 self.__window = window 1556 window.set_default_size(200, 200) 1557 vbox = gtk.VBox() 1558 window.add(vbox) 1559 render = GtkGraphicRenderer(graphic) 1560 self.__render = render 1561 vbox.pack_end(render, True, True, 0) 1562 hbox = gtk.HBox() 1563 vbox.pack_start(hbox, False, False, 0) 1564 smaller_zoom = gtk.Button("Zoom Out") 1565 smaller_zoom.connect("clicked", self.__set_smaller_cb) 1566 hbox.pack_start(smaller_zoom) 1567 bigger_zoom = gtk.Button("Zoom In") 1568 bigger_zoom.connect("clicked", self.__set_bigger_cb) 1569 hbox.pack_start(bigger_zoom) 1570 output_png = gtk.Button("Output Png") 1571 output_png.connect("clicked", self.__output_png_cb) 1572 hbox.pack_start(output_png) 1573 window.connect('destroy', gtk.main_quit) 1574 window.show_all() 1575 #gtk.bindings_activate(gtk.main_quit, 'q', 0) 1576 gtk.main() 1577 def __set_smaller_cb(self, widget): 1578 """! Set Smaller Callback 1579 @param self this object 1580 @param widget widget 1581 @return none 1582 """ 1583 self.__render.set_smaller_zoom() 1584 def __set_bigger_cb(self, widget): 1585 """! Set Bigger Callback 1586 @param self this object 1587 @param widget widget 1588 @return none 1589 """ 1590 self.__render.set_bigger_zoom() 1591 def __output_png_cb(self, widget): 1592 """! Output PNG Callback 1593 @param self this object 1594 @param widget widget 1595 @return none 1596 """ 1597 dialog = gtk.FileChooserDialog("Output Png", self.__window, 1598 gtk.FILE_CHOOSER_ACTION_SAVE, ("Save", 1)) 1599 self.__dialog = dialog 1600 dialog.set_default_response(1) 1601 dialog.connect("response", self.__dialog_response_cb) 1602 dialog.show() 1603 return 1604 def __dialog_response_cb(self, widget, response): 1605 """! Dialog Response Callback 1606 @param self this object 1607 @param widget widget 1608 @param response response 1609 @return none 1610 """ 1611 if response == 1: 1612 filename = self.__dialog.get_filename() 1613 self.__render.output_png(filename) 1614 widget.hide() 1615 return 1616 1617 1618## read_data function 1619def read_data(filename): 1620 timelines = Timelines() 1621 colors = Colors() 1622 m1 = re.compile('range ([^ ]+) ([^ ]+) ([^ ]+) ([0-9]+) ([0-9]+)') 1623 m2 = re.compile('event-str ([^ ]+) ([^ ]+) ([^ ]+) ([0-9]+)') 1624 m3 = re.compile('event-int ([^ ]+) ([^ ]+) ([0-9]+) ([0-9]+)') 1625 m4 = re.compile('color ([^ ]+) #([a-fA-F0-9]{2,2})([a-fA-F0-9]{2,2})([a-fA-F0-9]{2,2})') 1626 1627 with open(filename) as fh: 1628 for line in fh.readlines(): 1629 m = m1.match(line) 1630 if m: 1631 line_name = m.group(1) 1632 timeline = timelines.get(m.group(1)) 1633 rang = timeline.get_range(m.group(2)) 1634 data_range = DataRange() 1635 data_range.value = m.group(3) 1636 data_range.start = int(m.group(4)) 1637 data_range.end = int(m.group(5)) 1638 rang.add_range(data_range) 1639 continue 1640 m = m2.match(line) 1641 if m: 1642 line_name = m.group(1) 1643 timeline = timelines.get(m.group(1)) 1644 ev = timeline.get_event_str(m.group(2)) 1645 event = EventString() 1646 event.value = m.group(3) 1647 event.at = int(m.group(4)) 1648 ev.add_event(event) 1649 continue 1650 m = m3.match(line) 1651 if m: 1652 line_name = m.group(1) 1653 timeline = timelines.get(m.group(1)) 1654 ev = timeline.get_event_int(m.group(2)) 1655 event = EventInt() 1656 event.value = int(m.group(3)) 1657 event.at = int(m.group(4)) 1658 ev.add_event(event) 1659 continue 1660 1661 m = m4.match(line) 1662 if m: 1663 r = int(m.group(2), 16) 1664 g = int(m.group(3), 16) 1665 b = int(m.group(4), 16) 1666 color = Color(r / 255, g / 255, b / 255) 1667 colors.add(m.group(1), color) 1668 continue 1669 timelines.sort() 1670 return (colors, timelines) 1671 1672 1673 1674def main(): 1675 (colors, timelines) = read_data(sys.argv[1]) 1676 (lower_bound, upper_bound) = timelines.get_bounds() 1677 graphic = GraphicRenderer(lower_bound, upper_bound) 1678 top_legend = TopLegendRenderer() 1679 range_values = timelines.get_all_range_values() 1680 range_colors = [] 1681 for range_value in range_values: 1682 range_colors.append(colors.lookup(range_value)) 1683 top_legend.set_legends(range_values, 1684 range_colors) 1685 graphic.set_top_legend(top_legend) 1686 data = TimelinesRenderer() 1687 data.set_timelines(timelines, colors) 1688 graphic.set_data(data) 1689 1690 # default range 1691 range_mid = (upper_bound - lower_bound) / 2 1692 range_width = (upper_bound - lower_bound) / 10 1693 range_lo = range_mid - range_width / 2 1694 range_hi = range_mid + range_width / 2 1695 graphic.set_range(range_lo, range_hi) 1696 1697 main_window = MainWindow() 1698 main_window.run(graphic) 1699 1700 1701main() 1702