| # SchedGui.py - Python extension for perf script, basic GUI code for |
| # traces drawing and overview. |
| # |
| # Copyright (C) 2010 by Frederic Weisbecker <fweisbec@gmail.com> |
| # |
| # This software is distributed under the terms of the GNU General |
| # Public License ("GPL") version 2 as published by the Free Software |
| # Foundation. |
| |
| |
| try: |
| import wx |
| except ImportError: |
| raise ImportError, "You need to install the wxpython lib for this script" |
| |
| |
| class RootFrame(wx.Frame): |
| Y_OFFSET = 100 |
| RECT_HEIGHT = 100 |
| RECT_SPACE = 50 |
| EVENT_MARKING_WIDTH = 5 |
| |
| def __init__(self, sched_tracer, title, parent = None, id = -1): |
| wx.Frame.__init__(self, parent, id, title) |
| |
| (self.screen_width, self.screen_height) = wx.GetDisplaySize() |
| self.screen_width -= 10 |
| self.screen_height -= 10 |
| self.zoom = 0.5 |
| self.scroll_scale = 20 |
| self.sched_tracer = sched_tracer |
| self.sched_tracer.set_root_win(self) |
| (self.ts_start, self.ts_end) = sched_tracer.interval() |
| self.update_width_virtual() |
| self.nr_rects = sched_tracer.nr_rectangles() + 1 |
| self.height_virtual = RootFrame.Y_OFFSET + (self.nr_rects * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) |
| |
| # whole window panel |
| self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height)) |
| |
| # scrollable container |
| self.scroll = wx.ScrolledWindow(self.panel) |
| self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale) |
| self.scroll.EnableScrolling(True, True) |
| self.scroll.SetFocus() |
| |
| # scrollable drawing area |
| self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width - 15, self.screen_height / 2)) |
| self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint) |
| self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) |
| self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) |
| self.scroll.Bind(wx.EVT_PAINT, self.on_paint) |
| self.scroll.Bind(wx.EVT_KEY_DOWN, self.on_key_press) |
| self.scroll.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) |
| |
| self.scroll.Fit() |
| self.Fit() |
| |
| self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, self.height_virtual, wx.SIZE_USE_EXISTING) |
| |
| self.txt = None |
| |
| self.Show(True) |
| |
| def us_to_px(self, val): |
| return val / (10 ** 3) * self.zoom |
| |
| def px_to_us(self, val): |
| return (val / self.zoom) * (10 ** 3) |
| |
| def scroll_start(self): |
| (x, y) = self.scroll.GetViewStart() |
| return (x * self.scroll_scale, y * self.scroll_scale) |
| |
| def scroll_start_us(self): |
| (x, y) = self.scroll_start() |
| return self.px_to_us(x) |
| |
| def paint_rectangle_zone(self, nr, color, top_color, start, end): |
| offset_px = self.us_to_px(start - self.ts_start) |
| width_px = self.us_to_px(end - self.ts_start) |
| |
| offset_py = RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) |
| width_py = RootFrame.RECT_HEIGHT |
| |
| dc = self.dc |
| |
| if top_color is not None: |
| (r, g, b) = top_color |
| top_color = wx.Colour(r, g, b) |
| brush = wx.Brush(top_color, wx.SOLID) |
| dc.SetBrush(brush) |
| dc.DrawRectangle(offset_px, offset_py, width_px, RootFrame.EVENT_MARKING_WIDTH) |
| width_py -= RootFrame.EVENT_MARKING_WIDTH |
| offset_py += RootFrame.EVENT_MARKING_WIDTH |
| |
| (r ,g, b) = color |
| color = wx.Colour(r, g, b) |
| brush = wx.Brush(color, wx.SOLID) |
| dc.SetBrush(brush) |
| dc.DrawRectangle(offset_px, offset_py, width_px, width_py) |
| |
| def update_rectangles(self, dc, start, end): |
| start += self.ts_start |
| end += self.ts_start |
| self.sched_tracer.fill_zone(start, end) |
| |
| def on_paint(self, event): |
| dc = wx.PaintDC(self.scroll_panel) |
| self.dc = dc |
| |
| width = min(self.width_virtual, self.screen_width) |
| (x, y) = self.scroll_start() |
| start = self.px_to_us(x) |
| end = self.px_to_us(x + width) |
| self.update_rectangles(dc, start, end) |
| |
| def rect_from_ypixel(self, y): |
| y -= RootFrame.Y_OFFSET |
| rect = y / (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) |
| height = y % (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) |
| |
| if rect < 0 or rect > self.nr_rects - 1 or height > RootFrame.RECT_HEIGHT: |
| return -1 |
| |
| return rect |
| |
| def update_summary(self, txt): |
| if self.txt: |
| self.txt.Destroy() |
| self.txt = wx.StaticText(self.panel, -1, txt, (0, (self.screen_height / 2) + 50)) |
| |
| |
| def on_mouse_down(self, event): |
| (x, y) = event.GetPositionTuple() |
| rect = self.rect_from_ypixel(y) |
| if rect == -1: |
| return |
| |
| t = self.px_to_us(x) + self.ts_start |
| |
| self.sched_tracer.mouse_down(rect, t) |
| |
| |
| def update_width_virtual(self): |
| self.width_virtual = self.us_to_px(self.ts_end - self.ts_start) |
| |
| def __zoom(self, x): |
| self.update_width_virtual() |
| (xpos, ypos) = self.scroll.GetViewStart() |
| xpos = self.us_to_px(x) / self.scroll_scale |
| self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale, xpos, ypos) |
| self.Refresh() |
| |
| def zoom_in(self): |
| x = self.scroll_start_us() |
| self.zoom *= 2 |
| self.__zoom(x) |
| |
| def zoom_out(self): |
| x = self.scroll_start_us() |
| self.zoom /= 2 |
| self.__zoom(x) |
| |
| |
| def on_key_press(self, event): |
| key = event.GetRawKeyCode() |
| if key == ord("+"): |
| self.zoom_in() |
| return |
| if key == ord("-"): |
| self.zoom_out() |
| return |
| |
| key = event.GetKeyCode() |
| (x, y) = self.scroll.GetViewStart() |
| if key == wx.WXK_RIGHT: |
| self.scroll.Scroll(x + 1, y) |
| elif key == wx.WXK_LEFT: |
| self.scroll.Scroll(x - 1, y) |
| elif key == wx.WXK_DOWN: |
| self.scroll.Scroll(x, y + 1) |
| elif key == wx.WXK_UP: |
| self.scroll.Scroll(x, y - 1) |