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