diff --git a/utils/grid.py b/utils/grid.py deleted file mode 100644 index c20100529d..0000000000 --- a/utils/grid.py +++ /dev/null @@ -1,1819 +0,0 @@ -#!/usr/bin/env python3 - -import re -import sys - -import cairo -import gtk - - -## DataRange class -class DataRange: - ## @var start - # start - ## @var end - # end - ## @var value - # value - def __init__(self, start=0, end=0, value=""): - """! Initializer - @param self this object - @param start start - @param end end - @param value value - """ - self.start = start - self.end = end - self.value = value - - -## EventString class -class EventString: - ## @var at - # at - ## @var value - # value - def __init__(self, at=0, value=""): - """! Initializer - @param self this object - @param at you - @param value value - """ - self.at = at - self.value = value - - -## EventFloat class -class EventFloat: - ## @var at - # at - ## @var value - # value - def __init__(self, at=0, value=0.0): - """! Initializer - @param self this object - @param at you - @param value value - """ - self.at = at - self.value = value - - -## EventInt class -class EventInt: - ## @var at - # at - ## @var value - # value - def __init__(self, at=0, value=0.0): - """! Initializer - @param self this object - @param at you - @param value value - """ - self.at = at - self.value = value - - -def ranges_cmp(a, b): - diff = a.start - b.start - if diff < 0: - return -1 - elif diff > 0: - return +1 - else: - return 0 - - -def events_cmp(a, b): - diff = a.at - b.at - if diff < 0: - return -1 - elif diff > 0: - return +1 - else: - return 0 - - -## TimelineDataRange -class TimelineDataRange: - ## @var name - # name - ## @var ranges - # ranges - def __init__(self, name=""): - """! Initializer - @param self this object - @param name name - """ - self.name = name - self.ranges = [] - return - - def __search(self, key): - """! Search - @param self this object - @param key key - @return index if found or -1 if not found - """ - l = 0 - u = len(self.ranges) - 1 - while l <= u: - i = int((l + u) / 2) - if key >= self.ranges[i].start and key <= self.ranges[i].end: - return i - elif key < self.ranges[i].start: - u = i - 1 - else: - # key > self.ranges[i].end - l = i + 1 - return -1 - - def add_range(self, range): - """! Add range - @param self this object - @param range range - @return none - """ - self.ranges.append(range) - - def get_all(self): - """! Get all ranges - @param self this object - @return the ranges - """ - return self.ranges - - def get_ranges(self, start, end): - """! Get selected ranges - @param self this object - @param start range start - @param end range end - @return the range or and empty list - """ - s = self.__search(start) - e = self.__search(end) - if s == -1 and e == -1: - return [] - elif s == -1: - return self.ranges[0 : e + 1] - elif e == -1: - return self.ranges[s : len(self.ranges)] - else: - return self.ranges[s : e + 1] - - def get_ranges_bounds(self, start, end): - """! Get ranges bounds - @param self this object - @param start range start - @param end range end - @return range - """ - s = self.__search(start) - e = self.__search(end) - if s == -1 and e == -1: - return (0, 0) - elif s == -1: - return (0, e + 1) - elif e == -1: - return (s, len(self.ranges)) - else: - return (s, e + 1) - - def sort(self): - """! Sort ranges - @param self this object - @return none - """ - self.ranges.sort(ranges_cmp) - - def get_bounds(self): - """! Get bounds - @param self this object - @return the bounds - """ - if len(self.ranges) > 0: - lo = self.ranges[0].start - hi = self.ranges[len(self.ranges) - 1].end - return (lo, hi) - else: - return (0, 0) - - -## TimelineEvent class -class TimelineEvent: - ## @var name - # name - ## @var events - # events - def __init__(self, name=""): - """! Get ranges bounds - @param self this object - @param name name - """ - self.name = name - self.events = [] - - def __search(self, key): - """! Search function - @param self this object - @param key the key - @return event index - """ - l = 0 - u = len(self.events) - 1 - while l <= u: - i = int((l + u) / 2) - if key == self.events[i].at: - return i - elif key < self.events[i].at: - u = i - 1 - else: - # key > self.events[i].at - l = i + 1 - return l - - def add_event(self, event): - """! Add Event - @param self this object - @param event event to add - @return none - """ - self.events.append(event) - - def get_events(self, start, end): - """! Get Events - @param self this object - @param start starting event - @param end ending event - @return the events - """ - s = self.__search(start) - e = self.__search(end) - return self.events[s : e + 1] - - def get_events_bounds(self, start, end): - """! Get Events Bounds - @param self this object - @param start starting event - @param end ending event - @return event bounds - """ - s = self.__search(start) - e = self.__search(end) - return (s, e + 1) - - def sort(self): - """! Sort function - @param self this object - @return none - """ - self.events.sort(events_cmp) - - def get_bounds(self): - """! Get Bounds - @param self this object - @return the bounds - """ - if len(self.events) > 0: - lo = self.events[0].at - hi = self.events[-1].at - return (lo, hi) - else: - return (0, 0) - - -## Timeline class -class Timeline: - ## @var name - # name - ## @var ranges - # ranges - ## @var event_str - # event string - ## @var event_int - # event int - def __init__(self, name=""): - """! Initializer - @param self this object - @param name name - """ - self.ranges = [] - self.event_str = [] - self.event_int = [] - self.name = name - - def get_range(self, name): - """! Get range - @param self this object - @param name name - @return the range - """ - for range in self.ranges: - if range.name == name: - return range - timeline = TimelineDataRange(name) - self.ranges.append(timeline) - return timeline - - def get_event_str(self, name): - """! Get Event String - @param self this object - @param name name - @return the event string - """ - for event_str in self.event_str: - if event_str.name == name: - return event_str - timeline = TimelineEvent(name) - self.event_str.append(timeline) - return timeline - - def get_event_int(self, name): - """! Get Event Int - @param self this object - @param name name - @return eevent int - """ - for event_int in self.event_int: - if event_int.name == name: - return event_int - timeline = TimelineEvent(name) - self.event_int.append(timeline) - return timeline - - def get_ranges(self): - """! Get Ranges - @param self this object - @return the ranges - """ - return self.ranges - - def get_events_str(self): - """! Get Events string - @param self this object - @return event string - """ - return self.event_str - - def get_events_int(self): - """! Get Events int - @param self this object - @return evrnt int - """ - return self.event_int - - def sort(self): - """! Sort the ranges and events - @param self this object - @return none - """ - for range in self.ranges: - range.sort() - for event in self.event_int: - event.sort() - for event in self.event_str: - event.sort() - - def get_bounds(self): - """! Get Bounds - @param self this object - @return the bounds - """ - lo = 0 - hi = 0 - for range in self.ranges: - (range_lo, range_hi) = range.get_bounds() - if range_lo < lo: - lo = range_lo - if range_hi > hi: - hi = range_hi - for event_str in self.event_str: - (ev_lo, ev_hi) = event_str.get_bounds() - if ev_lo < lo: - lo = ev_lo - if ev_hi > hi: - hi = ev_hi - for event_int in self.event_int: - (ev_lo, ev_hi) = event_int.get_bounds() - if ev_lo < lo: - lo = ev_lo - if ev_hi > hi: - hi = ev_hi - return (lo, hi) - - -## Timelines class -class Timelines: - ## @var timelines - # timelines - def __init__(self): - """Initializer - @param self: this object - """ - self.timelines = [] - - def get(self, name): - """! Get Timeline - @param self this object - @param name name - @return the timeline for the name - """ - for timeline in self.timelines: - if timeline.name == name: - return timeline - timeline = Timeline(name) - self.timelines.append(timeline) - return timeline - - def get_all(self): - """! Get All Timeline - @param self this object - @return all timelines - """ - return self.timelines - - def sort(self): - """! Sort the timelines - @param self this object - @return none - """ - for timeline in self.timelines: - timeline.sort() - - def get_bounds(self): - """! Get Bounds - @param self this object - @return the bounds for all timelines - """ - lo = 0 - hi = 0 - for timeline in self.timelines: - (t_lo, t_hi) = timeline.get_bounds() - if t_lo < lo: - lo = t_lo - if t_hi > hi: - hi = t_hi - return (lo, hi) - - def get_all_range_values(self): - """! Get All Ranges - @param self this object - @return the keys for all ranges - """ - range_values = {} - for timeline in self.timelines: - for ranges in timeline.get_ranges(): - for ran in ranges.get_all(): - range_values[ran.value] = 1 - return range_values.keys() - - -## Color class -class Color: - ## @var r - # red - ## @var g - # green - ## @var b - # blue - def __init__(self, r=0.0, g=0.0, b=0.0): - """! Initializer - @param self: this object - @param r: red - @param g: green - @param b: blue - """ - self.r = r - self.g = g - self.b = b - - def set(self, r, g, b): - """! Set color - @param self: this object - @param r: red - @param g: green - @param b: blue - @return none - """ - self.r = r - self.g = g - self.b = b - - -## Colors class -class Colors: - ## @var __colors - # colors - ## @var default_colors - # default colors - ## XXX add more - 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), - ] - - def __init__(self): - """! Initializer - @param self this object - """ - self.__colors = {} - - def add(self, name, color): - """! Add - @param self this object - @param name name of the color - @param color color value - @return none - """ - self.__colors[name] = color - - def lookup(self, name): - """! Lookup name - @param self this object - @param name name - @return named color - """ - if not self.__colors.has_key(name): - self.add(name, self.default_colors.pop()) - return self.__colors.get(name) - - -## TopLegendRenderer class -class TopLegendRenderer: - ## @var __padding - # padding - ## @var __legends - # legends - ## @var __colors - # colors - ## @var __width - # width - ## @var __height - # height - def __init__(self): - """! Initializer - @param self this object - """ - self.__padding = 10 - - def set_padding(self, padding): - """! Set padding - @param self this object - @param padding padding - @return none - """ - self.__padding = padding - - def set_legends(self, legends, colors): - """! Set padding - @param self this object - @param legends legends - @param colors colors - @return none - """ - self.__legends = legends - self.__colors = colors - - def layout(self, width): - """! Set padding - @param self this object - @param width width - @return none - """ - self.__width = width - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1) - ctx = cairo.Context(surface) - line_height = 0 - total_height = self.__padding - line_used = self.__padding - for legend in self.__legends: - (t_width, t_height) = ctx.text_extents(legend)[2:4] - item_width = self.__padding + self.__padding + t_width + self.__padding - item_height = t_height + self.__padding - if item_height > line_height: - line_height = item_height - if line_used + item_width > self.__width: - line_used = self.__padding + item_width - total_height += line_height - else: - line_used += item_width - x = line_used - item_width - total_height += line_height - self.__height = total_height - - def get_height(self): - """! Set padding - @param self this object - @return height - """ - return self.__height - - def draw(self, ctx): - """! Set padding - @param self this object - @param ctx ctx - @return none - """ - i = 0 - line_height = 0 - total_height = self.__padding - line_used = self.__padding - for legend in self.__legends: - (t_width, t_height) = ctx.text_extents(legend)[2:4] - item_width = self.__padding + self.__padding + t_width + self.__padding - item_height = t_height + self.__padding - if item_height > line_height: - line_height = item_height - if line_used + item_width > self.__width: - line_used = self.__padding + item_width - total_height += line_height - else: - line_used += item_width - x = line_used - item_width - ctx.rectangle(x, total_height, self.__padding, self.__padding) - ctx.set_source_rgb(0, 0, 0) - ctx.set_line_width(2) - ctx.stroke_preserve() - ctx.set_source_rgb(self.__colors[i].r, self.__colors[i].g, self.__colors[i].b) - ctx.fill() - ctx.move_to(x + self.__padding * 2, total_height + t_height) - ctx.set_source_rgb(0, 0, 0) - ctx.show_text(legend) - i += 1 - - -## TimelinesRenderer class -class TimelinesRenderer: - ## @var padding - # padding - ## @var timelines - # timelines - ## @var colors - # colors - ## @var start - # start - ## @var end - # end - ## @var left_width - # left width - ## @var right_width - # right width - ## @var max_text_height - # maximum text height - ## @var width - # width - ## @var height - # height - ## @var grey_background - # grey background - def __init__(self): - """! Initializer - @param self this object - """ - self.padding = 10 - return - - def get_height(self): - """! Get Height - @param self this object - @return height - """ - return self.height - - def set_timelines(self, timelines, colors): - """! Set Timelines - @param self this object - @param timelines timelines - @param colors colors - @return none - """ - self.timelines = timelines - self.colors = colors - - def set_render_range(self, start, end): - """! Set Render Range - @param self this object - @param start start - @param end end - @return none - """ - self.start = start - self.end = end - - def get_data_x_start(self): - """! Get Data X Start - @param self: this object - @return X start - """ - return ( - self.padding / 2 + self.left_width + self.padding + self.right_width + self.padding / 2 - ) - - def layout(self, width): - """! Get Data X Start - @param self this object - @param width width - @return none - """ - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1) - ctx = cairo.Context(surface) - max_text_height = ctx.text_extents( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcedefghijklmnopqrstuvwxyz0123456789" - )[3] - - left_width = 0 - right_width = 0 - left_n_lines = 0 - range_n = 0 - eventint_n = 0 - eventstr_n = 0 - for timeline in self.timelines.get_all(): - left_n_lines += 1 - t_width = ctx.text_extents(timeline.name)[2] - left_width = max(left_width, t_width) - for rang in timeline.get_ranges(): - t_width = ctx.text_extents(rang.name)[2] - right_width = max(right_width, t_width) - range_n += 1 - for events_int in timeline.get_events_int(): - t_width = ctx.text_extents(events_int.name)[2] - right_width = max(right_width, t_width) - eventint_n += 1 - for events_str in timeline.get_events_str(): - t_width = ctx.text_extents(events_str.name)[2] - right_width = max(right_width, t_width) - eventstr_n += 1 - - left_height = left_n_lines * max_text_height + (left_n_lines - 1) * self.padding - right_n_lines = range_n + eventint_n + eventstr_n - right_height = (right_n_lines - 1) * self.padding + right_n_lines * max_text_height - right_data_height = (eventint_n + eventstr_n) * (max_text_height + 5) + range_n * 10 - right_data_height += (right_n_lines - 1) * self.padding - - height = max(left_height, right_height) - height = max(height, right_data_height) - - self.left_width = left_width - self.right_width = right_width - self.max_text_height = max_text_height - self.width = width - self.height = height + self.padding - - def draw_line(self, ctx, x, y, width, height): - """! Draw Line - @param self this object - @param ctx ctx - @param x x - @param y y - @param width width - @param height height - @return none - """ - ctx.move_to(x, y) - ctx.rel_line_to(width, height) - ctx.close_path() - ctx.set_operator(cairo.OPERATOR_SOURCE) - ctx.set_line_width(1.0) - ctx.set_source_rgb(0, 0, 0) - ctx.stroke() - - def draw_events(self, ctx, events, x, y, width, height): - """! Draw Event - @param self this object - @param ctx ctx - @param events events - @param x x - @param y y - @param width width - @param height height - @return none - """ - if (self.grey_background % 2) == 0: - ctx.rectangle(x, y - self.padding / 2, width, height + self.padding) - ctx.set_source_rgb(0.9, 0.9, 0.9) - ctx.fill() - last_x_drawn = int(x) - (lo, hi) = events.get_events_bounds(self.start, self.end) - for event in events.events[lo:hi]: - real_x = int(x + (event.at - self.start) * width / (self.end - self.start)) - if real_x > last_x_drawn + 2: - ctx.rectangle(real_x, y, 1, 1) - ctx.set_source_rgb(1, 0, 0) - ctx.stroke() - ctx.move_to(real_x, y + self.max_text_height) - ctx.set_source_rgb(0, 0, 0) - ctx.show_text(str(event.value)) - last_x_drawn = real_x - self.grey_background += 1 - - def draw_ranges(self, ctx, ranges, x, y, width, height): - """! Draw Ranges - @param self this object - @param ctx ctx - @param ranges ranges - @param x x - @param y y - @param width width - @param height height - @return none - """ - if (self.grey_background % 2) == 0: - ctx.rectangle(x, y - self.padding / 2, width, height + self.padding) - ctx.set_source_rgb(0.9, 0.9, 0.9) - ctx.fill() - last_x_drawn = int(x - 1) - (lo, hi) = ranges.get_ranges_bounds(self.start, self.end) - for data_range in ranges.ranges[lo:hi]: - s = max(data_range.start, self.start) - e = min(data_range.end, self.end) - x_start = int(x + (s - self.start) * width / (self.end - self.start)) - x_end = int(x + (e - self.start) * width / (self.end - self.start)) - if x_end > last_x_drawn: - ctx.rectangle(x_start, y, x_end - x_start, 10) - ctx.set_source_rgb(0, 0, 0) - ctx.stroke_preserve() - color = self.colors.lookup(data_range.value) - ctx.set_source_rgb(color.r, color.g, color.b) - ctx.fill() - last_x_drawn = x_end - - self.grey_background += 1 - - def draw(self, ctx): - """! Draw - @param self this object - @param ctx ctx - @return none - """ - timeline_top = 0 - top_y = self.padding / 2 - left_x_start = self.padding / 2 - left_x_end = left_x_start + self.left_width - right_x_start = left_x_end + self.padding - right_x_end = right_x_start + self.right_width - data_x_start = right_x_end + self.padding / 2 - data_x_end = self.width - data_width = data_x_end - data_x_start - cur_y = top_y - self.draw_line(ctx, 0, 0, self.width, 0) - self.grey_background = 1 - for timeline in self.timelines.get_all(): - (y_bearing, t_width, t_height) = ctx.text_extents(timeline.name)[1:4] - ctx.move_to(left_x_start, cur_y + self.max_text_height - (t_height + y_bearing)) - ctx.show_text(timeline.name) - for events_int in timeline.get_events_int(): - (y_bearing, t_width, t_height) = ctx.text_extents(events_int.name)[1:4] - ctx.move_to(right_x_start, cur_y + self.max_text_height - (t_height + y_bearing)) - ctx.show_text(events_int.name) - self.draw_events( - ctx, events_int, data_x_start, cur_y, data_width, self.max_text_height + 5 - ) - cur_y += self.max_text_height + 5 + self.padding - self.draw_line( - ctx, - right_x_start - self.padding / 2, - cur_y - self.padding / 2, - self.right_width + self.padding, - 0, - ) - - for events_str in timeline.get_events_str(): - (y_bearing, t_width, t_height) = ctx.text_extents(events_str.name)[1:4] - ctx.move_to(right_x_start, cur_y + self.max_text_height - (t_height + y_bearing)) - ctx.show_text(events_str.name) - self.draw_events( - ctx, events_str, data_x_start, cur_y, data_width, self.max_text_height + 5 - ) - cur_y += self.max_text_height + 5 + self.padding - self.draw_line( - ctx, - right_x_start - self.padding / 2, - cur_y - self.padding / 2, - self.right_width + self.padding, - 0, - ) - for ranges in timeline.get_ranges(): - (y_bearing, t_width, t_height) = ctx.text_extents(ranges.name)[1:4] - ctx.move_to(right_x_start, cur_y + self.max_text_height - (t_height + y_bearing)) - ctx.show_text(ranges.name) - self.draw_ranges(ctx, ranges, data_x_start, cur_y, data_width, 10) - cur_y += self.max_text_height + self.padding - self.draw_line( - ctx, - right_x_start - self.padding / 2, - cur_y - self.padding / 2, - self.right_width + self.padding, - 0, - ) - self.draw_line(ctx, 0, cur_y - self.padding / 2, self.width, 0) - bot_y = cur_y - self.padding / 2 - self.draw_line(ctx, left_x_end + self.padding / 2, 0, 0, bot_y) - self.draw_line(ctx, right_x_end + self.padding / 2, 0, 0, bot_y) - - -## ScaleRenderer class -class ScaleRenderer: - ## @var __top - # top - ## @var __lo - # lo - ## @var __hi - # hi - ## @var __delta - # delta - ## @var __width - # width - ## @var __height - # height - ## @var max_text_height - # maximum text height - def __init__(self): - """! Initializer - @param self this object - """ - self.__top = 0 - return - - def set_bounds(self, lo, hi): - """! Set Bounds - @param self this object - @param lo lo - @param hi hi - @return none - """ - self.__lo = lo - self.__hi = hi - - def get_position(self, x): - """! Get Position - @param self this object - @param x x - @return real x - """ - real_x = (x - self.__lo) * self.__width / (self.__hi - self.__lo) - return real_x - - def set_top(self): - """! Set Top - @param self this object - @return none - """ - self.__top = 1 - - def set_bot(self): - """! Set Bottom - @param self this object - @return none - """ - self.__top = 0 - - def layout(self, width): - """! Layout - @param self this object - @param width width - @return none - """ - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1) - ctx = cairo.Context(surface) - - # calculate scale delta - data_delta = self.__hi - self.__lo - closest = 1 - while (closest * 10) < data_delta: - closest *= 10 - if (data_delta / closest) == 0: - delta = closest - elif (data_delta / closest) == 1: - delta = closest / 10 - else: - delta = closest - start = self.__lo - (self.__lo % delta) + delta - end = self.__hi - (self.__hi % delta) - - self.__delta = delta - self.__width = width - - # calculate text height - max_text_height = ctx.text_extents( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcedefghijklmnopqrstuvwxyz0123456789" - )[3] - self.max_text_height = max_text_height - height = max_text_height + 10 - self.__height = height - - def get_height(self): - """! Get Height - @param self: this object - @return height - """ - return self.__height - - def draw(self, ctx): - """! Draw - @param self this object - @param ctx ctx - @return none - """ - delta = self.__delta - start = self.__lo - (self.__lo % delta) + delta - end = self.__hi - (self.__hi % delta) - - if self.__top == 1: - s = -1 - else: - s = 1 - # print scale points - ctx.set_source_rgb(0, 0, 0) - ctx.set_line_width(1.0) - ticks = range(int(start), int(end + delta), int(delta)) - for x in ticks: - real_x = (x - self.__lo) * self.__width / (self.__hi - self.__lo) - ctx.move_to(real_x, 0) - ctx.line_to(real_x, 5 * s) - ctx.close_path() - ctx.stroke() - (t_y_bearing, t_width, t_height) = ctx.text_extents(str(x))[1:4] - if self.__top: - text_delta = t_height + t_y_bearing - else: - text_delta = -t_y_bearing - ctx.move_to(real_x - t_width / 2, (5 + 5 + text_delta) * s) - ctx.show_text(str(x)) - # draw subticks - delta /= 10 - if delta > 0: - start = self.__lo - (self.__lo % delta) + delta - end = self.__hi - (self.__hi % delta) - for x in range(int(start), int(end + delta), int(delta)): - real_x = (x - self.__lo) * self.__width / (self.__hi - self.__lo) - ctx.move_to(real_x, 0) - ctx.line_to(real_x, 3 * s) - ctx.close_path() - ctx.stroke() - - -## GraphicRenderer class -class GraphicRenderer: - ## @var __start - # start - ## @var __end - # end - ## @var __mid_scale - # mid scale - ## @var __bot_scale - # bottom scale - ## @var __width - # width - ## @var __height - # height - ## @var __r_start - # start - ## @var __r_end - # end - ## @var __data - # data - ## @var __mid_scale - # mid scale - ## @var __top_legend - # top legend - def __init__(self, start, end): - """! Initializer - @param self this object - @param start start - @param end end - """ - self.__start = float(start) - self.__end = float(end) - self.__mid_scale = ScaleRenderer() - self.__mid_scale.set_top() - self.__bot_scale = ScaleRenderer() - self.__bot_scale.set_bounds(start, end) - self.__bot_scale.set_bot() - self.__width = 1 - self.__height = 1 - - def get_width(self): - """! Get Width - @param self: this object - @return width - """ - return self.__width - - def get_height(self): - """! Get Height - @param self this object - @return height - """ - return self.__height - - # return x, y, width, height - def get_data_rectangle(self): - """! Get Data Rectangle - @param self this object - @return rectangle - """ - y_start = self.__top_legend.get_height() - x_start = self.__data.get_data_x_start() - return (x_start, y_start, self.__width - x_start, self.__data.get_height()) - - def scale_data(self, x): - """! Get Data Rectangle - @param self this object - @param x x - @return scaled x - """ - x_start = self.__data.get_data_x_start() - x_scaled = x / (self.__width - x_start) * (self.__r_end - self.__r_start) - return x_scaled - - # return x, y, width, height - def get_selection_rectangle(self): - """! Get Selection Rectangle - @param self this object - @return rectangle - """ - y_start = ( - self.__top_legend.get_height() - + self.__data.get_height() - + self.__mid_scale.get_height() - + 20 - ) - y_height = self.__bot_scale.get_height() + 20 - x_start = self.__bot_scale.get_position(self.__r_start) - x_end = self.__bot_scale.get_position(self.__r_end) - return (x_start, y_start, x_end - x_start, y_height) - - def scale_selection(self, x): - """! Scale Selection - @param self this object - @param x the X - @return scaled X - """ - x_scaled = x / self.__width * (self.__end - self.__start) - return x_scaled - - def set_range(self, start, end): - """! Set Range - @param self this object - @param start start - @param end end - @return none - """ - s = min(start, end) - e = max(start, end) - start = max(self.__start, s) - end = min(self.__end, e) - self.__r_start = start - self.__r_end = end - self.__data.set_render_range(start, end) - self.__mid_scale.set_bounds(start, end) - self.layout(self.__width, self.__height) - - def get_range(self): - """! Get Range - @param self this object - @return range - """ - return (self.__r_start, self.__r_end) - - def set_data(self, data): - """! Set Date - @param self this object - @param data data - @return none - """ - self.__data = data - - def set_top_legend(self, top_legend): - """! Set Top Legend - @param self this object - @param top_legend The legend - @return none - """ - self.__top_legend = top_legend - - def layout(self, width, height): - """! Set Layout - @param self this object - @param width width - @param height height - @return none - """ - self.__width = width - self.__height = height - self.__top_legend.layout(width) - top_legend_height = self.__top_legend.get_height() - self.__data.layout(width) - self.__mid_scale.layout(width - self.__data.get_data_x_start()) - self.__bot_scale.layout(width) - return - - def __x_pixel(self, x, width): - """! X Pixel - @param self this object - @param x x - @param width width - @return x pixel - """ - new_x = (x - self.__start) * width / (self.__end - self.__start) - return new_x - - def draw(self, ctx): - """! Draw - @param self this object - @param ctx ctx - @return none - """ - # default background is white - ctx.save() - ctx.set_source_rgb(1, 1, 1) - ctx.set_operator(cairo.OPERATOR_SOURCE) - ctx.rectangle(0, 0, self.__width, self.__height) - ctx.fill() - - # top legend - ctx.save() - self.__top_legend.draw(ctx) - top_legend_height = self.__top_legend.get_height() - ctx.restore() - - # separation line - ctx.move_to(0, top_legend_height) - ctx.line_to(self.__width, top_legend_height) - ctx.close_path() - ctx.set_line_width(2) - ctx.set_source_rgb(0, 0, 0) - ctx.stroke() - - # data - ctx.save() - ctx.translate(0, top_legend_height) - self.__data.draw(ctx) - ctx.restore() - - # scale below data - ctx.save() - ctx.translate( - self.__data.get_data_x_start(), - top_legend_height + self.__data.get_height() + self.__mid_scale.get_height(), - ) - self.__mid_scale.draw(ctx) - ctx.restore() - - height_used = top_legend_height + self.__data.get_height() + self.__mid_scale.get_height() - - # separation between scale and left pane - ctx.move_to(self.__data.get_data_x_start(), height_used) - ctx.rel_line_to(0, -self.__mid_scale.get_height()) - ctx.close_path() - ctx.set_source_rgb(0, 0, 0) - ctx.set_line_width(2) - ctx.stroke() - - # separation below scale - ctx.move_to(0, height_used) - ctx.line_to(self.__width, height_used) - ctx.close_path() - ctx.set_line_width(2) - ctx.set_source_rgb(0, 0, 0) - ctx.stroke() - - select_start = self.__bot_scale.get_position(self.__r_start) - select_end = self.__bot_scale.get_position(self.__r_end) - - # left connection between top scale and bottom scale - ctx.move_to(0, height_used) - ctx.line_to(self.__data.get_data_x_start(), height_used) - ctx.line_to(select_start, height_used + 20) - ctx.line_to(0, height_used + 20) - ctx.line_to(0, height_used) - ctx.set_source_rgb(0, 0, 0) - ctx.set_line_width(1) - ctx.stroke_preserve() - ctx.set_source_rgb(0.9, 0.9, 0.9) - ctx.fill() - - # right connection between top scale and bottom scale - ctx.move_to(self.__width, height_used) - ctx.line_to(self.__width, height_used + 20) - ctx.line_to(select_end, height_used + 20) - ctx.line_to(self.__width, height_used) - ctx.set_source_rgb(0, 0, 0) - ctx.set_line_width(1) - ctx.stroke_preserve() - ctx.set_source_rgb(0.9, 0.9, 0.9) - ctx.fill() - - height_used += 20 - - # unused area background - unused_start = self.__bot_scale.get_position(self.__r_start) - unused_end = self.__bot_scale.get_position(self.__r_end) - unused_height = self.__bot_scale.get_height() + 20 - ctx.rectangle(0, height_used, unused_start, unused_height) - ctx.rectangle(unused_end, height_used, self.__width - unused_end, unused_height) - ctx.set_source_rgb(0.9, 0.9, 0.9) - ctx.fill() - - # border line around bottom scale - ctx.move_to(unused_end, height_used) - ctx.line_to(self.__width, height_used) - ctx.line_to(self.__width, height_used + unused_height) - ctx.line_to(0, height_used + unused_height) - ctx.line_to(0, height_used) - ctx.line_to(unused_start, height_used) - ctx.close_path() - ctx.set_line_width(2) - ctx.set_source_rgb(0, 0, 0) - ctx.stroke() - ctx.move_to(unused_start, height_used) - ctx.line_to(unused_end, height_used) - ctx.close_path() - ctx.set_line_width(1) - ctx.set_source_rgb(0.9, 0.9, 0.9) - ctx.stroke() - - # unused area dot borders - ctx.save() - ctx.move_to(max(unused_start, 2), height_used) - ctx.rel_line_to(0, unused_height) - ctx.move_to(min(unused_end, self.__width - 2), height_used) - ctx.rel_line_to(0, unused_height) - ctx.set_dash([5], 0) - ctx.set_source_rgb(0, 0, 0) - ctx.set_line_width(1) - ctx.stroke() - ctx.restore() - - # bottom scale - ctx.save() - ctx.translate(0, height_used) - self.__bot_scale.draw(ctx) - ctx.restore() - - -## GtkGraphicRenderer class -class GtkGraphicRenderer(gtk.DrawingArea): - ## @var __data - # data - ## @var __moving_left - # moving left - ## @var __moving_right - # moving right - ## @var __moving_both - # moving both - ## @var __moving_top - # moving top - ## @var __force_full_redraw - # full redraw - ## @var __moving_left_cur - # moving left cur - ## @var __moving_right_cur - # moving right cur - ## @var __moving_both_start - # moving both start - ## @var __moving_both_cur - # moving both cur - ## @var __moving_top_cur - # moving top cur - ## @var __moving_top_start - # moving top start - ## @var __width - # width - ## @var __height - # height - ## @var __buffer_surface - # __buffer_surface - ## @var expose - # expose function - ## @var size_allocate - # size_allocate function - ## @var motion_notify - # motion_notify function - ## @var button_press - # button_press function - ## @var button_release - # button_release function - def __init__(self, data): - """! Initializer - @param self this object - @param data data - """ - super(GtkGraphicRenderer, self).__init__() - self.__data = data - self.__moving_left = False - self.__moving_right = False - self.__moving_both = False - self.__moving_top = False - self.__force_full_redraw = True - self.add_events(gtk.gdk.POINTER_MOTION_MASK) - self.add_events(gtk.gdk.BUTTON_PRESS_MASK) - self.add_events(gtk.gdk.BUTTON_RELEASE_MASK) - self.connect("expose_event", self.expose) - self.connect("size-allocate", self.size_allocate) - self.connect("motion-notify-event", self.motion_notify) - self.connect("button-press-event", self.button_press) - self.connect("button-release-event", self.button_release) - - def set_smaller_zoom(self): - """! Set Smaller Zoom - @param self this object - @return none - """ - (start, end) = self.__data.get_range() - self.__data.set_range(start, start + (end - start) * 2) - self.__force_full_redraw = True - self.queue_draw() - - def set_bigger_zoom(self): - """! Set Bigger Zoom - @param self this object - @return none - """ - (start, end) = self.__data.get_range() - self.__data.set_range(start, start + (end - start) / 2) - self.__force_full_redraw = True - self.queue_draw() - - def output_png(self, filename): - """! Output PNG - @param self this object - @param filename file name - @return none - """ - surface = cairo.ImageSurface( - cairo.FORMAT_ARGB32, self.__data.get_width(), self.__data.get_height() - ) - ctx = cairo.Context(self.__buffer_surface) - self.__data.draw(ctx) - surface.write_to_png(filename) - - def button_press(self, widget, event): - """! Button Press - @param self this object - @param widget widget - @param event event - @return true if button has been pressed otherwise false - """ - (x, y, width, height) = self.__data.get_selection_rectangle() - (d_x, d_y, d_width, d_height) = self.__data.get_data_rectangle() - if event.y > y and event.y < y + height: - if abs(event.x - x) < 5: - self.__moving_left = True - return True - if abs(event.x - (x + width)) < 5: - self.__moving_right = True - return True - if event.x > x and event.x < x + width: - self.__moving_both = True - self.__moving_both_start = event.x - self.__moving_both_cur = event.x - return True - if event.y > d_y and event.y < (d_y + d_height): - if event.x > d_x and event.x < (d_x + d_width): - self.__moving_top = True - self.__moving_top_start = event.x - self.__moving_top_cur = event.x - return True - return False - - def button_release(self, widget, event): - """! Button Release - @param self this object - @param widget widget - @param event event - @return true if button was released otherwise false - """ - if self.__moving_left: - self.__moving_left = False - left = self.__data.scale_selection(self.__moving_left_cur) - right = self.__data.get_range()[1] - self.__data.set_range(left, right) - self.__force_full_redraw = True - self.queue_draw() - return True - if self.__moving_right: - self.__moving_right = False - right = self.__data.scale_selection(self.__moving_right_cur) - left = self.__data.get_range()[0] - self.__data.set_range(left, right) - self.__force_full_redraw = True - self.queue_draw() - return True - if self.__moving_both: - self.__moving_both = False - delta = self.__data.scale_selection(self.__moving_both_cur - self.__moving_both_start) - (left, right) = self.__data.get_range() - self.__data.set_range(left + delta, right + delta) - self.__force_full_redraw = True - self.queue_draw() - return True - if self.__moving_top: - self.__moving_top = False - return False - - def motion_notify(self, widget, event): - """! Motion Notify - @param self this object - @param widget widget - @param event event - @return true if moving otherwise false - """ - (x, y, width, height) = self.__data.get_selection_rectangle() - if self.__moving_left: - if event.x <= 0: - self.__moving_left_cur = 0 - elif event.x >= x + width: - self.__moving_left_cur = x + width - else: - self.__moving_left_cur = event.x - self.queue_draw_area(0, int(y), int(self.__width), int(height)) - return True - if self.__moving_right: - if event.x >= self.__width: - self.__moving_right = self.__width - elif event.x < x: - self.__moving_right_cur = x - else: - self.__moving_right_cur = event.x - self.queue_draw_area(0, int(y), int(self.__width), int(height)) - return True - if self.__moving_both: - cur_e = self.__width - (x + width - self.__moving_both_start) - cur_s = self.__moving_both_start - x - if event.x < cur_s: - self.__moving_both_cur = cur_s - elif event.x > cur_e: - self.__moving_both_cur = cur_e - else: - self.__moving_both_cur = event.x - self.queue_draw_area(0, int(y), int(self.__width), int(height)) - return True - if self.__moving_top: - self.__moving_top_cur = event.x - delta = self.__data.scale_data(self.__moving_top_start - self.__moving_top_cur) - (left, right) = self.__data.get_range() - self.__data.set_range(left + delta, right + delta) - self.__force_full_redraw = True - self.__moving_top_start = event.x - self.queue_draw() - return True - (d_x, d_y, d_width, d_height) = self.__data.get_data_rectangle() - if event.y > y and event.y < y + height: - if abs(event.x - x) < 5 or abs(event.x - (x + width)) < 5: - widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.SB_H_DOUBLE_ARROW)) - return True - if event.x > x and event.x < x + width: - widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) - return True - if event.y > d_y and event.y < (d_y + d_height): - if event.x > d_x and event.x < (d_x + d_width): - widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) - return True - widget.window.set_cursor(None) - return False - - def size_allocate(self, widget, allocation): - """! Size Allocate - @param self this object - @param widget widget - @param allocation allocation - @return none - """ - self.__width = allocation.width - self.__height = allocation.height - self.__data.layout(allocation.width, allocation.height) - self.__force_full_redraw = True - self.queue_draw() - - def expose(self, widget, event): - """! Expose - @param self this object - @param widget widget - @param event event - @return false - """ - if self.__force_full_redraw: - self.__buffer_surface = cairo.ImageSurface( - cairo.FORMAT_ARGB32, self.__data.get_width(), self.__data.get_height() - ) - ctx = cairo.Context(self.__buffer_surface) - self.__data.draw(ctx) - self.__force_full_redraw = False - ctx = widget.window.cairo_create() - ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) - ctx.clip() - ctx.set_source_surface(self.__buffer_surface) - ctx.paint() - (x, y, width, height) = self.__data.get_selection_rectangle() - if self.__moving_left: - ctx.move_to(max(self.__moving_left_cur, 2), y) - ctx.rel_line_to(0, height) - ctx.close_path() - ctx.set_line_width(1) - ctx.set_source_rgb(0, 0, 0) - ctx.stroke() - if self.__moving_right: - ctx.move_to(min(self.__moving_right_cur, self.__width - 2), y) - ctx.rel_line_to(0, height) - ctx.close_path() - ctx.set_line_width(1) - ctx.set_source_rgb(0, 0, 0) - ctx.stroke() - if self.__moving_both: - delta_x = self.__moving_both_cur - self.__moving_both_start - left_x = x + delta_x - ctx.move_to(x + delta_x, y) - ctx.rel_line_to(0, height) - ctx.close_path() - ctx.move_to(x + width + delta_x, y) - ctx.rel_line_to(0, height) - ctx.close_path() - ctx.set_source_rgb(0, 0, 0) - ctx.set_line_width(1) - ctx.stroke() - return False - - -## MainWindow class -class MainWindow: - ## @var __window - # window - ## @var __render - # render - ## @var __dialog - # dialog - ## @var __set_smaller_cb - # __set_smaller_cb function - ## @var __set_bigger_cb - # __set_bigger_cb function - ## @var __output_png_cb - # __output_png_cb function - ## @var __dialog_response_cb - # __dialog_response_cb function - def __init__(self): - """! Initializer - @param self this object - """ - return - - def run(self, graphic): - """! Run function - @param self this object - @param graphic graphic - @return none - """ - window = gtk.Window() - self.__window = window - window.set_default_size(200, 200) - vbox = gtk.VBox() - window.add(vbox) - render = GtkGraphicRenderer(graphic) - self.__render = render - vbox.pack_end(render, True, True, 0) - hbox = gtk.HBox() - vbox.pack_start(hbox, False, False, 0) - smaller_zoom = gtk.Button("Zoom Out") - smaller_zoom.connect("clicked", self.__set_smaller_cb) - hbox.pack_start(smaller_zoom) - bigger_zoom = gtk.Button("Zoom In") - bigger_zoom.connect("clicked", self.__set_bigger_cb) - hbox.pack_start(bigger_zoom) - output_png = gtk.Button("Output Png") - output_png.connect("clicked", self.__output_png_cb) - hbox.pack_start(output_png) - window.connect("destroy", gtk.main_quit) - window.show_all() - # gtk.bindings_activate(gtk.main_quit, 'q', 0) - gtk.main() - - def __set_smaller_cb(self, widget): - """! Set Smaller Callback - @param self this object - @param widget widget - @return none - """ - self.__render.set_smaller_zoom() - - def __set_bigger_cb(self, widget): - """! Set Bigger Callback - @param self this object - @param widget widget - @return none - """ - self.__render.set_bigger_zoom() - - def __output_png_cb(self, widget): - """! Output PNG Callback - @param self this object - @param widget widget - @return none - """ - dialog = gtk.FileChooserDialog( - "Output Png", self.__window, gtk.FILE_CHOOSER_ACTION_SAVE, ("Save", 1) - ) - self.__dialog = dialog - dialog.set_default_response(1) - dialog.connect("response", self.__dialog_response_cb) - dialog.show() - - def __dialog_response_cb(self, widget, response): - """! Dialog Response Callback - @param self this object - @param widget widget - @param response response - @return none - """ - if response == 1: - filename = self.__dialog.get_filename() - self.__render.output_png(filename) - widget.hide() - - -## read_data function -def read_data(filename): - timelines = Timelines() - colors = Colors() - m1 = re.compile("range ([^ ]+) ([^ ]+) ([^ ]+) ([0-9]+) ([0-9]+)") - m2 = re.compile("event-str ([^ ]+) ([^ ]+) ([^ ]+) ([0-9]+)") - m3 = re.compile("event-int ([^ ]+) ([^ ]+) ([0-9]+) ([0-9]+)") - m4 = re.compile("color ([^ ]+) #([a-fA-F0-9]{2,2})([a-fA-F0-9]{2,2})([a-fA-F0-9]{2,2})") - - with open(filename, encoding="utf-8") as fh: - for line in fh.readlines(): - m = m1.match(line) - if m: - line_name = m.group(1) - timeline = timelines.get(m.group(1)) - rang = timeline.get_range(m.group(2)) - data_range = DataRange() - data_range.value = m.group(3) - data_range.start = int(m.group(4)) - data_range.end = int(m.group(5)) - rang.add_range(data_range) - continue - m = m2.match(line) - if m: - line_name = m.group(1) - timeline = timelines.get(m.group(1)) - ev = timeline.get_event_str(m.group(2)) - event = EventString() - event.value = m.group(3) - event.at = int(m.group(4)) - ev.add_event(event) - continue - m = m3.match(line) - if m: - line_name = m.group(1) - timeline = timelines.get(m.group(1)) - ev = timeline.get_event_int(m.group(2)) - event = EventInt() - event.value = int(m.group(3)) - event.at = int(m.group(4)) - ev.add_event(event) - continue - - m = m4.match(line) - if m: - r = int(m.group(2), 16) - g = int(m.group(3), 16) - b = int(m.group(4), 16) - color = Color(r / 255, g / 255, b / 255) - colors.add(m.group(1), color) - continue - timelines.sort() - return (colors, timelines) - - -def main(): - (colors, timelines) = read_data(sys.argv[1]) - (lower_bound, upper_bound) = timelines.get_bounds() - graphic = GraphicRenderer(lower_bound, upper_bound) - top_legend = TopLegendRenderer() - range_values = timelines.get_all_range_values() - range_colors = [] - for range_value in range_values: - range_colors.append(colors.lookup(range_value)) - top_legend.set_legends(range_values, range_colors) - graphic.set_top_legend(top_legend) - data = TimelinesRenderer() - data.set_timelines(timelines, colors) - graphic.set_data(data) - - # default range - range_mid = (upper_bound - lower_bound) / 2 - range_width = (upper_bound - lower_bound) / 10 - range_lo = range_mid - range_width / 2 - range_hi = range_mid + range_width / 2 - graphic.set_range(range_lo, range_hi) - - main_window = MainWindow() - main_window.run(graphic) - - -main()