diff options
| author | Gary Bressler <garybressler@nc.rr.com> | 2010-04-08 17:11:08 -0400 |
|---|---|---|
| committer | Gary Bressler <garybressler@nc.rr.com> | 2010-04-08 17:11:08 -0400 |
| commit | ceff6457bfeb5642616f4711f14e0bb652d12164 (patch) | |
| tree | d05b4ebd1c3ee6e28884c669d65fd31700941086 /unit_trace | |
| parent | 01abc8352aa2fd192678b4066b26ea749a203801 (diff) | |
Updated the documentation to describe the visualizer, made unit-trace itself not require gtk/cairo, and a few other minor things.
Diffstat (limited to 'unit_trace')
| -rw-r--r-- | unit_trace/viz/__init__.py | 23 | ||||
| -rw-r--r-- | unit_trace/viz/canvas.py | 340 | ||||
| -rw-r--r-- | unit_trace/viz/convert.py | 24 | ||||
| -rw-r--r-- | unit_trace/viz/format.py | 30 | ||||
| -rw-r--r-- | unit_trace/viz/graph.py | 444 | ||||
| -rw-r--r-- | unit_trace/viz/renderer.py | 16 | ||||
| -rw-r--r-- | unit_trace/viz/schedule.py | 366 | ||||
| -rw-r--r-- | unit_trace/viz/viewer.py | 409 | ||||
| -rwxr-xr-x | unit_trace/viz/visualizer.py | 10 | ||||
| -rw-r--r-- | unit_trace/viz/windows.py | 65 |
10 files changed, 980 insertions, 747 deletions
diff --git a/unit_trace/viz/__init__.py b/unit_trace/viz/__init__.py index 8ba4784..99ed99e 100644 --- a/unit_trace/viz/__init__.py +++ b/unit_trace/viz/__init__.py | |||
| @@ -1,10 +1,19 @@ | |||
| 1 | import visualizer | 1 | try: |
| 2 | import viewer | 2 | import pygtk |
| 3 | import renderer | 3 | import gtk |
| 4 | import format | 4 | import gobject |
| 5 | import gobject | 5 | import cairo |
| 6 | import gtk | 6 | import visualizer |
| 7 | import convert | 7 | import viewer |
| 8 | import renderer | ||
| 9 | import format | ||
| 10 | import convert | ||
| 11 | except ImportError: | ||
| 12 | import sys | ||
| 13 | |||
| 14 | print 'Unit-Trace could not find pycairo and/or pygtk installed on your system. Please\n' \ | ||
| 15 | + 'make sure these libraries are installed before attempting to use the visualizer.' | ||
| 16 | sys.exit(1) | ||
| 8 | 17 | ||
| 9 | gobject.signal_new('set-scroll-adjustments', viewer.GraphArea, gobject.SIGNAL_RUN_FIRST, | 18 | gobject.signal_new('set-scroll-adjustments', viewer.GraphArea, gobject.SIGNAL_RUN_FIRST, |
| 10 | None, (gtk.Adjustment, gtk.Adjustment)) | 19 | None, (gtk.Adjustment, gtk.Adjustment)) |
diff --git a/unit_trace/viz/canvas.py b/unit_trace/viz/canvas.py index 758dea3..ea73d98 100644 --- a/unit_trace/viz/canvas.py +++ b/unit_trace/viz/canvas.py | |||
| @@ -33,19 +33,19 @@ class Surface(object): | |||
| 33 | self.scale = 1.0 | 33 | self.scale = 1.0 |
| 34 | self.fname = fname | 34 | self.fname = fname |
| 35 | self.ctx = ctx | 35 | self.ctx = ctx |
| 36 | 36 | ||
| 37 | def renew(self, width, height): | 37 | def renew(self, width, height): |
| 38 | raise NotImplementedError | 38 | raise NotImplementedError |
| 39 | 39 | ||
| 40 | def change_ctx(self, ctx): | 40 | def change_ctx(self, ctx): |
| 41 | self.ctx = ctx | 41 | self.ctx = ctx |
| 42 | 42 | ||
| 43 | def get_fname(self): | 43 | def get_fname(self): |
| 44 | return self.fname | 44 | return self.fname |
| 45 | 45 | ||
| 46 | def write_out(self, fname): | 46 | def write_out(self, fname): |
| 47 | raise NotImplementedError | 47 | raise NotImplementedError |
| 48 | 48 | ||
| 49 | def pan(self, x, y, width, height): | 49 | def pan(self, x, y, width, height): |
| 50 | """A surface actually represents just a ``window'' into | 50 | """A surface actually represents just a ``window'' into |
| 51 | what we are drawing on. For instance, if we are scrolling through | 51 | what we are drawing on. For instance, if we are scrolling through |
| @@ -57,11 +57,11 @@ class Surface(object): | |||
| 57 | self.virt_y = y | 57 | self.virt_y = y |
| 58 | self.width = width | 58 | self.width = width |
| 59 | self.height = height | 59 | self.height = height |
| 60 | 60 | ||
| 61 | def set_scale(self, scale): | 61 | def set_scale(self, scale): |
| 62 | """Sets the scale factor.""" | 62 | """Sets the scale factor.""" |
| 63 | self.scale = scale | 63 | self.scale = scale |
| 64 | 64 | ||
| 65 | def get_real_coor(self, x, y): | 65 | def get_real_coor(self, x, y): |
| 66 | """Translates the coordinates (x, y) | 66 | """Translates the coordinates (x, y) |
| 67 | in the ``theoretical'' plane to the true (x, y) coordinates on this surface | 67 | in the ``theoretical'' plane to the true (x, y) coordinates on this surface |
| @@ -69,57 +69,57 @@ class Surface(object): | |||
| 69 | bounds of the surface, | 69 | bounds of the surface, |
| 70 | if we want something outside the surface's ``window''.""" | 70 | if we want something outside the surface's ``window''.""" |
| 71 | return (x - self.virt_x * self.scale, y - self.virt_y * self.scale) | 71 | return (x - self.virt_x * self.scale, y - self.virt_y * self.scale) |
| 72 | 72 | ||
| 73 | def get_virt_coor(self, x, y): | 73 | def get_virt_coor(self, x, y): |
| 74 | """Does the inverse of the last method.""" | 74 | """Does the inverse of the last method.""" |
| 75 | return (x + self.virt_x * self.scale, y + self.virt_y * self.scale) | 75 | return (x + self.virt_x * self.scale, y + self.virt_y * self.scale) |
| 76 | 76 | ||
| 77 | def get_virt_coor_unscaled(self, x, y): | 77 | def get_virt_coor_unscaled(self, x, y): |
| 78 | """Does the same, but removes the scale factor (i.e. behaves as if | 78 | """Does the same, but removes the scale factor (i.e. behaves as if |
| 79 | the scale was 1.0 all along).""" | 79 | the scale was 1.0 all along).""" |
| 80 | return (x / self.scale + self.virt_x, y / self.scale + self.virt_y) | 80 | return (x / self.scale + self.virt_x, y / self.scale + self.virt_y) |
| 81 | 81 | ||
| 82 | class SVGSurface(Surface): | 82 | class SVGSurface(Surface): |
| 83 | def renew(self, width, height): | 83 | def renew(self, width, height): |
| 84 | iwidth = int(math.ceil(width)) | 84 | iwidth = int(math.ceil(width)) |
| 85 | iheight = int(math.ceil(height)) | 85 | iheight = int(math.ceil(height)) |
| 86 | self.surface = cairo.SVGSurface(self.fname, iwidth, iheight) | 86 | self.surface = cairo.SVGSurface(self.fname, iwidth, iheight) |
| 87 | self.ctx = cairo.Context(self.surface) | 87 | self.ctx = cairo.Context(self.surface) |
| 88 | 88 | ||
| 89 | def write_out(self, fname): | 89 | def write_out(self, fname): |
| 90 | os.execl('cp', self.fname, fname) | 90 | os.execl('cp', self.fname, fname) |
| 91 | 91 | ||
| 92 | class ImageSurface(Surface): | 92 | class ImageSurface(Surface): |
| 93 | def renew(self, width, height): | 93 | def renew(self, width, height): |
| 94 | iwidth = int(math.ceil(width)) | 94 | iwidth = int(math.ceil(width)) |
| 95 | iheight = int(math.ceil(height)) | 95 | iheight = int(math.ceil(height)) |
| 96 | self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, iwidth, iheight) | 96 | self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, iwidth, iheight) |
| 97 | self.ctx = cairo.Context(self.surface) | 97 | self.ctx = cairo.Context(self.surface) |
| 98 | 98 | ||
| 99 | def write_out(self, fname): | 99 | def write_out(self, fname): |
| 100 | if self.surface is None: | 100 | if self.surface is None: |
| 101 | raise ValueError('Don\'t own surface, can\'t write to to file') | 101 | raise ValueError('Don\'t own surface, can\'t write to to file') |
| 102 | 102 | ||
| 103 | self.surface.write_to_png(fname) | 103 | self.surface.write_to_png(fname) |
| 104 | 104 | ||
| 105 | class Pattern(object): | 105 | class Pattern(object): |
| 106 | DEF_STRIPE_SIZE = 10 | 106 | DEF_STRIPE_SIZE = 10 |
| 107 | MAX_FADE_WIDTH = 250 | 107 | MAX_FADE_WIDTH = 250 |
| 108 | 108 | ||
| 109 | def __init__(self, color_list, stripe_size=DEF_STRIPE_SIZE): | 109 | def __init__(self, color_list, stripe_size=DEF_STRIPE_SIZE): |
| 110 | self.color_list = color_list | 110 | self.color_list = color_list |
| 111 | self.stripe_size = stripe_size | 111 | self.stripe_size = stripe_size |
| 112 | 112 | ||
| 113 | def render_on_canvas(self, canvas, x, y, width, height, fade=False): | 113 | def render_on_canvas(self, canvas, x, y, width, height, fade=False): |
| 114 | fade_span = min(width, Pattern.MAX_FADE_WIDTH) | 114 | fade_span = min(width, Pattern.MAX_FADE_WIDTH) |
| 115 | 115 | ||
| 116 | if len(self.color_list) == 1: | 116 | if len(self.color_list) == 1: |
| 117 | if fade: | 117 | if fade: |
| 118 | canvas.fill_rect_fade(x, y, fade_span, height, (1.0, 1.0, 1.0), \ | 118 | canvas.fill_rect_fade(x, y, fade_span, height, (1.0, 1.0, 1.0), \ |
| 119 | self.color_list[0]) | 119 | self.color_list[0]) |
| 120 | else: | 120 | else: |
| 121 | canvas.fill_rect(x, y, width, height, self.color_list[0]) | 121 | canvas.fill_rect(x, y, width, height, self.color_list[0]) |
| 122 | 122 | ||
| 123 | if width > Pattern.MAX_FADE_WIDTH: | 123 | if width > Pattern.MAX_FADE_WIDTH: |
| 124 | canvas.fill_rect(x + Pattern.MAX_FADE_WIDTH, y, width - Pattern.MAX_FADE_WIDTH, | 124 | canvas.fill_rect(x + Pattern.MAX_FADE_WIDTH, y, width - Pattern.MAX_FADE_WIDTH, |
| 125 | height, self.color_list[0]) | 125 | height, self.color_list[0]) |
| @@ -133,100 +133,100 @@ class Pattern(object): | |||
| 133 | min(self.stripe_size, bottom - y), (1.0, 1.0, 1.0), self.color_list[i]) | 133 | min(self.stripe_size, bottom - y), (1.0, 1.0, 1.0), self.color_list[i]) |
| 134 | else: | 134 | else: |
| 135 | canvas.fill_rect(x, y, width, min(self.stripe_size, bottom - y), self.color_list[i]) | 135 | canvas.fill_rect(x, y, width, min(self.stripe_size, bottom - y), self.color_list[i]) |
| 136 | 136 | ||
| 137 | if width > Pattern.MAX_FADE_WIDTH: | 137 | if width > Pattern.MAX_FADE_WIDTH: |
| 138 | canvas.fill_rect(x + Pattern.MAX_FADE_WIDTH, y, width - Pattern.MAX_FADE_WIDTH, | 138 | canvas.fill_rect(x + Pattern.MAX_FADE_WIDTH, y, width - Pattern.MAX_FADE_WIDTH, |
| 139 | min(self.stripe_size, bottom - y), self.color_list[i]) | 139 | min(self.stripe_size, bottom - y), self.color_list[i]) |
| 140 | 140 | ||
| 141 | y += self.stripe_size | 141 | y += self.stripe_size |
| 142 | n += 1 | 142 | n += 1 |
| 143 | 143 | ||
| 144 | class Canvas(object): | 144 | class Canvas(object): |
| 145 | """This is a basic class that stores and draws on a Cairo surface, | 145 | """This is a basic class that stores and draws on a Cairo surface, |
| 146 | using various primitives related to drawing a real-time graph (up-arrows, | 146 | using various primitives related to drawing a real-time graph (up-arrows, |
| 147 | down-arrows, bars, ...). | 147 | down-arrows, bars, ...). |
| 148 | 148 | ||
| 149 | This is the lowest-level representation (aside perhaps from the Cairo | 149 | This is the lowest-level representation (aside perhaps from the Cairo |
| 150 | surface itself) of a real-time graph. It allows the user to draw | 150 | surface itself) of a real-time graph. It allows the user to draw |
| 151 | primitives at certain locations, but for the most part does not know | 151 | primitives at certain locations, but for the most part does not know |
| 152 | anything about real-time scheduling, just how to draw the basic parts | 152 | anything about real-time scheduling, just how to draw the basic parts |
| 153 | that make up a schedule graph. For that, see Graph or its descendants.""" | 153 | that make up a schedule graph. For that, see Graph or its descendants.""" |
| 154 | 154 | ||
| 155 | BOTTOM_LAYER = 0 | 155 | BOTTOM_LAYER = 0 |
| 156 | MIDDLE_LAYER = 1 | 156 | MIDDLE_LAYER = 1 |
| 157 | TOP_LAYER = 2 | 157 | TOP_LAYER = 2 |
| 158 | 158 | ||
| 159 | LAYERS = (BOTTOM_LAYER, MIDDLE_LAYER, TOP_LAYER) | 159 | LAYERS = (BOTTOM_LAYER, MIDDLE_LAYER, TOP_LAYER) |
| 160 | 160 | ||
| 161 | NULL_PATTERN = -1 | 161 | NULL_PATTERN = -1 |
| 162 | 162 | ||
| 163 | SQRT3 = math.sqrt(3.0) | 163 | SQRT3 = math.sqrt(3.0) |
| 164 | 164 | ||
| 165 | def __init__(self, width, height, item_clist, bar_plist, surface): | 165 | def __init__(self, width, height, item_clist, bar_plist, surface): |
| 166 | """Creates a new Canvas of dimensions (width, height). The | 166 | """Creates a new Canvas of dimensions (width, height). The |
| 167 | parameters ``item_plist'' and ``bar_plist'' each specify a list | 167 | parameters ``item_plist'' and ``bar_plist'' each specify a list |
| 168 | of patterns to choose from when drawing the items on the y-axis | 168 | of patterns to choose from when drawing the items on the y-axis |
| 169 | or filling in bars, respectively.""" | 169 | or filling in bars, respectively.""" |
| 170 | 170 | ||
| 171 | self.surface = surface | 171 | self.surface = surface |
| 172 | 172 | ||
| 173 | self.width = int(math.ceil(width)) | 173 | self.width = int(math.ceil(width)) |
| 174 | self.height = int(math.ceil(height)) | 174 | self.height = int(math.ceil(height)) |
| 175 | self.item_clist = item_clist | 175 | self.item_clist = item_clist |
| 176 | self.bar_plist = bar_plist | 176 | self.bar_plist = bar_plist |
| 177 | 177 | ||
| 178 | self.selectable_regions = {} | 178 | self.selectable_regions = {} |
| 179 | 179 | ||
| 180 | self.scale = 1.0 | 180 | self.scale = 1.0 |
| 181 | 181 | ||
| 182 | # clears the canvas. | 182 | # clears the canvas. |
| 183 | def clear(self): | 183 | def clear(self): |
| 184 | raise NotImplementedError | 184 | raise NotImplementedError |
| 185 | 185 | ||
| 186 | def set_scale(self, scale): | 186 | def set_scale(self, scale): |
| 187 | self.scale = scale | 187 | self.scale = scale |
| 188 | self.surface.set_scale(scale) | 188 | self.surface.set_scale(scale) |
| 189 | for event in self.selectable_regions: | 189 | for event in self.selectable_regions: |
| 190 | self.selectable_regions[event].set_scale(scale) | 190 | self.selectable_regions[event].set_scale(scale) |
| 191 | 191 | ||
| 192 | def scaled(self, *coors): | 192 | def scaled(self, *coors): |
| 193 | """Scales a series of coordinates.""" | 193 | """Scales a series of coordinates.""" |
| 194 | return [coor * self.scale for coor in coors] | 194 | return [coor * self.scale for coor in coors] |
| 195 | 195 | ||
| 196 | def unscaled(self, *coors): | 196 | def unscaled(self, *coors): |
| 197 | """Inverse of scale().""" | 197 | """Inverse of scale().""" |
| 198 | return [coor / self.scale for coor in coors] | 198 | return [coor / self.scale for coor in coors] |
| 199 | 199 | ||
| 200 | def draw_rect(self, x, y, width, height, color, thickness, snap=True): | 200 | def draw_rect(self, x, y, width, height, color, thickness, snap=True): |
| 201 | """Draws a rectangle somewhere (border only).""" | 201 | """Draws a rectangle somewhere (border only).""" |
| 202 | raise NotImplementedError | 202 | raise NotImplementedError |
| 203 | 203 | ||
| 204 | def fill_rect(self, x, y, width, height, color, snap=True): | 204 | def fill_rect(self, x, y, width, height, color, snap=True): |
| 205 | """Draws a filled rectangle somewhere. ``color'' is a 3-tuple.""" | 205 | """Draws a filled rectangle somewhere. ``color'' is a 3-tuple.""" |
| 206 | raise NotImplementedError | 206 | raise NotImplementedError |
| 207 | 207 | ||
| 208 | def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, snap=True): | 208 | def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, snap=True): |
| 209 | """Draws a rectangle somewhere, filled in with the fade.""" | 209 | """Draws a rectangle somewhere, filled in with the fade.""" |
| 210 | raise NotImplementedError | 210 | raise NotImplementedError |
| 211 | 211 | ||
| 212 | def draw_line(self, p0, p1, color, thickness, snap=True): | 212 | def draw_line(self, p0, p1, color, thickness, snap=True): |
| 213 | """Draws a line from p0 to p1 with a certain color and thickness.""" | 213 | """Draws a line from p0 to p1 with a certain color and thickness.""" |
| 214 | raise NotImplementedError | 214 | raise NotImplementedError |
| 215 | 215 | ||
| 216 | def draw_polyline(self, coor_list, color, thickness, snap=True): | 216 | def draw_polyline(self, coor_list, color, thickness, snap=True): |
| 217 | """Draws a polyline, where coor_list = [(x_0, y_0), (x_1, y_1), ... (x_m, y_m)] | 217 | """Draws a polyline, where coor_list = [(x_0, y_0), (x_1, y_1), ... (x_m, y_m)] |
| 218 | specifies a polyline from (x_0, y_0) to (x_1, y_1), etc.""" | 218 | specifies a polyline from (x_0, y_0) to (x_1, y_1), etc.""" |
| 219 | raise NotImplementedError | 219 | raise NotImplementedError |
| 220 | 220 | ||
| 221 | def fill_polyline(self, coor_list, color, thickness, snap=True): | 221 | def fill_polyline(self, coor_list, color, thickness, snap=True): |
| 222 | """Draws a polyline (probably a polygon) and fills it.""" | 222 | """Draws a polyline (probably a polygon) and fills it.""" |
| 223 | raise NotImplementedError | 223 | raise NotImplementedError |
| 224 | 224 | ||
| 225 | def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, | 225 | def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, |
| 226 | halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, snap=True): | 226 | halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, snap=True): |
| 227 | """Draws text at a position with a certain alignment.""" | 227 | """Draws text at a position with a certain alignment.""" |
| 228 | raise NotImplementedError | 228 | raise NotImplementedError |
| 229 | 229 | ||
| 230 | def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ | 230 | def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ |
| 231 | textfopts=GraphFormat.DEF_FOPTS_LABEL, | 231 | textfopts=GraphFormat.DEF_FOPTS_LABEL, |
| 232 | sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \ | 232 | sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \ |
| @@ -234,31 +234,31 @@ class Canvas(object): | |||
| 234 | """Draws text at a position with a certain alignment, along with optionally a superscript and | 234 | """Draws text at a position with a certain alignment, along with optionally a superscript and |
| 235 | subscript (which are None if either is not used.)""" | 235 | subscript (which are None if either is not used.)""" |
| 236 | raise NotImplementedError | 236 | raise NotImplementedError |
| 237 | 237 | ||
| 238 | def draw_y_axis(self, x, y, height): | 238 | def draw_y_axis(self, x, y, height): |
| 239 | """Draws the y-axis, starting from the bottom at the point x, y.""" | 239 | """Draws the y-axis, starting from the bottom at the point x, y.""" |
| 240 | self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0) | 240 | self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0) |
| 241 | 241 | ||
| 242 | self.draw_line((x, y), (x, y - height), (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) | 242 | self.draw_line((x, y), (x, y - height), (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) |
| 243 | 243 | ||
| 244 | def draw_y_axis_labels(self, x, y, height, item_list, item_size, fopts=None): | 244 | def draw_y_axis_labels(self, x, y, height, item_list, item_size, fopts=None): |
| 245 | """Draws the item labels on the y-axis. ``item_list'' is the list | 245 | """Draws the item labels on the y-axis. ``item_list'' is the list |
| 246 | of strings to print, while item_size gives the vertical amount of | 246 | of strings to print, while item_size gives the vertical amount of |
| 247 | space that each item shall take up, in pixels.""" | 247 | space that each item shall take up, in pixels.""" |
| 248 | if fopts is None: | 248 | if fopts is None: |
| 249 | fopts = GraphFormat.DEF_FOPTS_ITEM | 249 | fopts = GraphFormat.DEF_FOPTS_ITEM |
| 250 | 250 | ||
| 251 | x -= GraphFormat.Y_AXIS_ITEM_GAP | 251 | x -= GraphFormat.Y_AXIS_ITEM_GAP |
| 252 | y -= height - item_size / 2.0 | 252 | y -= height - item_size / 2.0 |
| 253 | 253 | ||
| 254 | orig_color = fopts.color | 254 | orig_color = fopts.color |
| 255 | for ctr, item in enumerate(item_list): | 255 | for ctr, item in enumerate(item_list): |
| 256 | fopts.color = self.get_item_color(ctr) | 256 | fopts.color = self.get_item_color(ctr) |
| 257 | self.draw_label(item, x, y, fopts, AlignMode.RIGHT, AlignMode.CENTER) | 257 | self.draw_label(item, x, y, fopts, AlignMode.RIGHT, AlignMode.CENTER) |
| 258 | y += item_size | 258 | y += item_size |
| 259 | 259 | ||
| 260 | fopts.color = orig_color | 260 | fopts.color = orig_color |
| 261 | 261 | ||
| 262 | def draw_x_axis(self, x, y, start_tick, end_tick, maj_sep, min_per_maj): | 262 | def draw_x_axis(self, x, y, start_tick, end_tick, maj_sep, min_per_maj): |
| 263 | """Draws the x-axis, including all the major and minor ticks (but not the labels). | 263 | """Draws the x-axis, including all the major and minor ticks (but not the labels). |
| 264 | ``num_maj'' gives the number of major ticks, ``maj_sep'' the number of pixels between | 264 | ``num_maj'' gives the number of major ticks, ``maj_sep'' the number of pixels between |
| @@ -271,48 +271,48 @@ class Canvas(object): | |||
| 271 | for i in range(start_tick, end_tick + 1): | 271 | for i in range(start_tick, end_tick + 1): |
| 272 | self.draw_line((x, y), (x, y + GraphFormat.MAJ_TICK_SIZE), | 272 | self.draw_line((x, y), (x, y + GraphFormat.MAJ_TICK_SIZE), |
| 273 | (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) | 273 | (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) |
| 274 | 274 | ||
| 275 | if (i < end_tick): | 275 | if (i < end_tick): |
| 276 | for j in range(0, min_per_maj): | 276 | for j in range(0, min_per_maj): |
| 277 | self.draw_line((x, y), (x + maj_sep / min_per_maj, y), | 277 | self.draw_line((x, y), (x + maj_sep / min_per_maj, y), |
| 278 | (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) | 278 | (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) |
| 279 | 279 | ||
| 280 | x += 1.0 * maj_sep / min_per_maj | 280 | x += 1.0 * maj_sep / min_per_maj |
| 281 | if j < min_per_maj - 1: | 281 | if j < min_per_maj - 1: |
| 282 | self.draw_line((x, y), (x, y + GraphFormat.MIN_TICK_SIZE), | 282 | self.draw_line((x, y), (x, y + GraphFormat.MIN_TICK_SIZE), |
| 283 | (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) | 283 | (0.0, 0.0, 0.0), GraphFormat.AXIS_THICKNESS) |
| 284 | 284 | ||
| 285 | def draw_x_axis_labels(self, x, y, start_tick, end_tick, maj_sep, min_per_maj, start=0, incr=1, show_min=False, \ | 285 | def draw_x_axis_labels(self, x, y, start_tick, end_tick, maj_sep, min_per_maj, start=0, incr=1, show_min=False, \ |
| 286 | majfopts=GraphFormat.DEF_FOPTS_MAJ, minfopts=GraphFormat.DEF_FOPTS_MIN): | 286 | majfopts=GraphFormat.DEF_FOPTS_MAJ, minfopts=GraphFormat.DEF_FOPTS_MIN): |
| 287 | """Draws the labels for the x-axis. (x, y) should give the origin. | 287 | """Draws the labels for the x-axis. (x, y) should give the origin. |
| 288 | how far down you want the text. ``incr'' gives the increment per major | 288 | how far down you want the text. ``incr'' gives the increment per major |
| 289 | tick. ``start'' gives the value of the first tick. ``show_min'' specifies | 289 | tick. ``start'' gives the value of the first tick. ``show_min'' specifies |
| 290 | whether to draw labels at minor ticks.""" | 290 | whether to draw labels at minor ticks.""" |
| 291 | 291 | ||
| 292 | x += GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep | 292 | x += GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep |
| 293 | y += GraphFormat.X_AXIS_LABEL_GAP + GraphFormat.MAJ_TICK_SIZE | 293 | y += GraphFormat.X_AXIS_LABEL_GAP + GraphFormat.MAJ_TICK_SIZE |
| 294 | 294 | ||
| 295 | minincr = incr / (min_per_maj * 1.0) | 295 | minincr = incr / (min_per_maj * 1.0) |
| 296 | 296 | ||
| 297 | cur = start * 1.0 | 297 | cur = start * 1.0 |
| 298 | 298 | ||
| 299 | for i in range(start_tick, end_tick + 1): | 299 | for i in range(start_tick, end_tick + 1): |
| 300 | text = util.format_float(cur, 2) | 300 | text = util.format_float(cur, 2) |
| 301 | self.draw_label(text, x, y, majfopts, AlignMode.CENTER, AlignMode.TOP) | 301 | self.draw_label(text, x, y, majfopts, AlignMode.CENTER, AlignMode.TOP) |
| 302 | 302 | ||
| 303 | if (i < end_tick): | 303 | if (i < end_tick): |
| 304 | if show_min: | 304 | if show_min: |
| 305 | for j in range(0, min_per_maj): | 305 | for j in range(0, min_per_maj): |
| 306 | x += 1.0 * maj_sep / min_per_maj | 306 | x += 1.0 * maj_sep / min_per_maj |
| 307 | cur += minincr | 307 | cur += minincr |
| 308 | text = util.format_float(cur, 2) | 308 | text = util.format_float(cur, 2) |
| 309 | 309 | ||
| 310 | if j < min_per_maj - 1: | 310 | if j < min_per_maj - 1: |
| 311 | self.draw_label(text, x, y, minfopts, AlignMode.CENTER, AlignMode.TOP) | 311 | self.draw_label(text, x, y, minfopts, AlignMode.CENTER, AlignMode.TOP) |
| 312 | else: | 312 | else: |
| 313 | x += maj_sep | 313 | x += maj_sep |
| 314 | cur += incr | 314 | cur += incr |
| 315 | 315 | ||
| 316 | def draw_grid(self, x, y, height, start_tick, end_tick, start_item, end_item, maj_sep, item_size, \ | 316 | def draw_grid(self, x, y, height, start_tick, end_tick, start_item, end_item, maj_sep, item_size, \ |
| 317 | min_per_maj=None, show_min=False): | 317 | min_per_maj=None, show_min=False): |
| 318 | """Draws a grid dividing along the item boundaries and the major ticks. | 318 | """Draws a grid dividing along the item boundaries and the major ticks. |
| @@ -321,22 +321,22 @@ class Canvas(object): | |||
| 321 | ``start_item'' and ``end_item'' give the item boundaries to start and end drawing horizontal lines.""" | 321 | ``start_item'' and ``end_item'' give the item boundaries to start and end drawing horizontal lines.""" |
| 322 | if start_tick > end_tick or start_item > end_item: | 322 | if start_tick > end_tick or start_item > end_item: |
| 323 | return | 323 | return |
| 324 | 324 | ||
| 325 | line_width = (end_tick - start_tick) * maj_sep | 325 | line_width = (end_tick - start_tick) * maj_sep |
| 326 | line_height = (end_item - start_item) * item_size | 326 | line_height = (end_item - start_item) * item_size |
| 327 | 327 | ||
| 328 | origin = (x, y) | 328 | origin = (x, y) |
| 329 | 329 | ||
| 330 | # draw horizontal lines first | 330 | # draw horizontal lines first |
| 331 | x = origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep | 331 | x = origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep |
| 332 | y = origin[1] - height + start_item * item_size | 332 | y = origin[1] - height + start_item * item_size |
| 333 | for i in range(start_item, end_item + 1): | 333 | for i in range(start_item, end_item + 1): |
| 334 | self.draw_line((x, y), (x + line_width, y), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS) | 334 | self.draw_line((x, y), (x + line_width, y), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS) |
| 335 | y += item_size | 335 | y += item_size |
| 336 | 336 | ||
| 337 | x = origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep | 337 | x = origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + start_tick * maj_sep |
| 338 | y = origin[1] - height + start_item * item_size | 338 | y = origin[1] - height + start_item * item_size |
| 339 | 339 | ||
| 340 | if show_min: | 340 | if show_min: |
| 341 | for i in range(0, (end_tick - start_tick) * min_per_maj + 1): | 341 | for i in range(0, (end_tick - start_tick) * min_per_maj + 1): |
| 342 | self.draw_line((x, y), (x, y + line_height), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS) | 342 | self.draw_line((x, y), (x, y + line_height), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS) |
| @@ -345,222 +345,222 @@ class Canvas(object): | |||
| 345 | for i in range(start_tick, end_tick + 1): | 345 | for i in range(start_tick, end_tick + 1): |
| 346 | self.draw_line((x, y), (x, y + line_height), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS) | 346 | self.draw_line((x, y), (x, y + line_height), GraphFormat.GRID_COLOR, GraphFormat.GRID_THICKNESS) |
| 347 | x += maj_sep | 347 | x += maj_sep |
| 348 | 348 | ||
| 349 | def draw_bar(self, x, y, width, height, n, clip_side, selected): | 349 | def draw_bar(self, x, y, width, height, n, clip_side, selected): |
| 350 | """Draws a bar with a certain set of dimensions, using pattern ``n'' from the | 350 | """Draws a bar with a certain set of dimensions, using pattern ``n'' from the |
| 351 | bar pattern list.""" | 351 | bar pattern list.""" |
| 352 | 352 | ||
| 353 | color, thickness = {False : (GraphFormat.BORDER_COLOR, GraphFormat.BORDER_THICKNESS), | 353 | color, thickness = {False : (GraphFormat.BORDER_COLOR, GraphFormat.BORDER_THICKNESS), |
| 354 | True : (GraphFormat.HIGHLIGHT_COLOR, GraphFormat.BORDER_THICKNESS * 2.0)}[selected] | 354 | True : (GraphFormat.HIGHLIGHT_COLOR, GraphFormat.BORDER_THICKNESS * 2.0)}[selected] |
| 355 | 355 | ||
| 356 | # use a pattern to be pretty | 356 | # use a pattern to be pretty |
| 357 | self.get_bar_pattern(n).render_on_canvas(self, x, y, width, height, True) | 357 | self.get_bar_pattern(n).render_on_canvas(self, x, y, width, height, True) |
| 358 | 358 | ||
| 359 | self.draw_rect(x, y, width, height, color, thickness, clip_side) | 359 | self.draw_rect(x, y, width, height, color, thickness, clip_side) |
| 360 | 360 | ||
| 361 | def add_sel_bar(self, x, y, width, height, event): | 361 | def add_sel_bar(self, x, y, width, height, event): |
| 362 | self.add_sel_region(SelectableRegion(x, y, width, height, event)) | 362 | self.add_sel_region(SelectableRegion(x, y, width, height, event)) |
| 363 | 363 | ||
| 364 | def draw_mini_bar(self, x, y, width, height, n, clip_side, selected): | 364 | def draw_mini_bar(self, x, y, width, height, n, clip_side, selected): |
| 365 | """Like the above, except it draws a miniature version. This is usually used for | 365 | """Like the above, except it draws a miniature version. This is usually used for |
| 366 | secondary purposes (i.e. to show jobs that _should_ have been running at a certain time). | 366 | secondary purposes (i.e. to show jobs that _should_ have been running at a certain time). |
| 367 | 367 | ||
| 368 | Of course we don't enforce the fact that this is mini, since the user can pass in width | 368 | Of course we don't enforce the fact that this is mini, since the user can pass in width |
| 369 | and height (but the mini bars do look slightly different: namely the borders are a different | 369 | and height (but the mini bars do look slightly different: namely the borders are a different |
| 370 | color)""" | 370 | color)""" |
| 371 | 371 | ||
| 372 | color, thickness = {False : (GraphFormat.LITE_BORDER_COLOR, GraphFormat.BORDER_THICKNESS), | 372 | color, thickness = {False : (GraphFormat.LITE_BORDER_COLOR, GraphFormat.BORDER_THICKNESS), |
| 373 | True : (GraphFormat.HIGHLIGHT_COLOR, GraphFormat.BORDER_THICKNESS * 1.5)}[selected] | 373 | True : (GraphFormat.HIGHLIGHT_COLOR, GraphFormat.BORDER_THICKNESS * 1.5)}[selected] |
| 374 | 374 | ||
| 375 | self.get_bar_pattern(n).render_on_canvas(self, x, y, width, height, True) | 375 | self.get_bar_pattern(n).render_on_canvas(self, x, y, width, height, True) |
| 376 | 376 | ||
| 377 | self.draw_rect(x, y, width, height, color, thickness, clip_side) | 377 | self.draw_rect(x, y, width, height, color, thickness, clip_side) |
| 378 | 378 | ||
| 379 | def add_sel_mini_bar(self, x, y, width, height, event): | 379 | def add_sel_mini_bar(self, x, y, width, height, event): |
| 380 | self.add_sel_region(SelectableRegion(x, y, width, height, event)) | 380 | self.add_sel_region(SelectableRegion(x, y, width, height, event)) |
| 381 | 381 | ||
| 382 | def draw_completion_marker(self, x, y, height, selected): | 382 | def draw_completion_marker(self, x, y, height, selected): |
| 383 | """Draws the symbol that represents a job completion, using a certain height.""" | 383 | """Draws the symbol that represents a job completion, using a certain height.""" |
| 384 | 384 | ||
| 385 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] | 385 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] |
| 386 | self.draw_line((x - height * GraphFormat.TEE_FACTOR / 2.0, y), | 386 | self.draw_line((x - height * GraphFormat.TEE_FACTOR / 2.0, y), |
| 387 | (x + height * GraphFormat.TEE_FACTOR / 2.0, y), | 387 | (x + height * GraphFormat.TEE_FACTOR / 2.0, y), |
| 388 | color, GraphFormat.BORDER_THICKNESS) | 388 | color, GraphFormat.BORDER_THICKNESS) |
| 389 | self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS) | 389 | self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS) |
| 390 | 390 | ||
| 391 | def add_sel_completion_marker(self, x, y, height, event): | 391 | def add_sel_completion_marker(self, x, y, height, event): |
| 392 | self.add_sel_region(SelectableRegion(x - height * GraphFormat.TEE_FACTOR / 2.0, y, | 392 | self.add_sel_region(SelectableRegion(x - height * GraphFormat.TEE_FACTOR / 2.0, y, |
| 393 | height * GraphFormat.TEE_FACTOR, height, event)) | 393 | height * GraphFormat.TEE_FACTOR, height, event)) |
| 394 | 394 | ||
| 395 | def draw_release_arrow_big(self, x, y, height, selected): | 395 | def draw_release_arrow_big(self, x, y, height, selected): |
| 396 | """Draws a release arrow of a certain height: (x, y) should give the top | 396 | """Draws a release arrow of a certain height: (x, y) should give the top |
| 397 | (northernmost point) of the arrow. The height includes the arrowhead.""" | 397 | (northernmost point) of the arrow. The height includes the arrowhead.""" |
| 398 | big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height | 398 | big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height |
| 399 | 399 | ||
| 400 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] | 400 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] |
| 401 | colors = [(1.0, 1.0, 1.0), color] | 401 | colors = [(1.0, 1.0, 1.0), color] |
| 402 | draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] | 402 | draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] |
| 403 | for i in range(0, 2): | 403 | for i in range(0, 2): |
| 404 | color = colors[i] | 404 | color = colors[i] |
| 405 | draw_func = draw_funcs[i] | 405 | draw_func = draw_funcs[i] |
| 406 | 406 | ||
| 407 | draw_func(self, [(x, y), (x - big_arrowhead_height / Canvas.SQRT3, y + big_arrowhead_height), \ | 407 | draw_func(self, [(x, y), (x - big_arrowhead_height / Canvas.SQRT3, y + big_arrowhead_height), \ |
| 408 | (x + big_arrowhead_height / Canvas.SQRT3, y + big_arrowhead_height), (x, y)], \ | 408 | (x + big_arrowhead_height / Canvas.SQRT3, y + big_arrowhead_height), (x, y)], \ |
| 409 | color, GraphFormat.BORDER_THICKNESS) | 409 | color, GraphFormat.BORDER_THICKNESS) |
| 410 | 410 | ||
| 411 | self.draw_line((x, y + big_arrowhead_height), (x, y + height), color, GraphFormat.BORDER_THICKNESS) | 411 | self.draw_line((x, y + big_arrowhead_height), (x, y + height), color, GraphFormat.BORDER_THICKNESS) |
| 412 | 412 | ||
| 413 | def add_sel_release_arrow_big(self, x, y, height, event): | 413 | def add_sel_release_arrow_big(self, x, y, height, event): |
| 414 | self.add_sel_arrow_big(x, y, height, event) | 414 | self.add_sel_arrow_big(x, y, height, event) |
| 415 | 415 | ||
| 416 | def draw_deadline_arrow_big(self, x, y, height, selected): | 416 | def draw_deadline_arrow_big(self, x, y, height, selected): |
| 417 | """Draws a release arrow: x, y should give the top (northernmost | 417 | """Draws a release arrow: x, y should give the top (northernmost |
| 418 | point) of the arrow. The height includes the arrowhead.""" | 418 | point) of the arrow. The height includes the arrowhead.""" |
| 419 | big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height | 419 | big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height |
| 420 | 420 | ||
| 421 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] | 421 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] |
| 422 | colors = [(1.0, 1.0, 1.0), color] | 422 | colors = [(1.0, 1.0, 1.0), color] |
| 423 | draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] | 423 | draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] |
| 424 | for i in range(0, 2): | 424 | for i in range(0, 2): |
| 425 | color = colors[i] | 425 | color = colors[i] |
| 426 | draw_func = draw_funcs[i] | 426 | draw_func = draw_funcs[i] |
| 427 | 427 | ||
| 428 | draw_func(self, [(x, y + height), (x - big_arrowhead_height / Canvas.SQRT3, \ | 428 | draw_func(self, [(x, y + height), (x - big_arrowhead_height / Canvas.SQRT3, \ |
| 429 | y + height - big_arrowhead_height), \ | 429 | y + height - big_arrowhead_height), \ |
| 430 | (x + big_arrowhead_height / Canvas.SQRT3, \ | 430 | (x + big_arrowhead_height / Canvas.SQRT3, \ |
| 431 | y + height - big_arrowhead_height), \ | 431 | y + height - big_arrowhead_height), \ |
| 432 | (x, y + height)], color, GraphFormat.BORDER_THICKNESS) | 432 | (x, y + height)], color, GraphFormat.BORDER_THICKNESS) |
| 433 | 433 | ||
| 434 | self.draw_line((x, y), (x, y + height - big_arrowhead_height), | 434 | self.draw_line((x, y), (x, y + height - big_arrowhead_height), |
| 435 | color, GraphFormat.BORDER_THICKNESS) | 435 | color, GraphFormat.BORDER_THICKNESS) |
| 436 | 436 | ||
| 437 | def add_sel_deadline_arrow_big(self, x, y, height, event): | 437 | def add_sel_deadline_arrow_big(self, x, y, height, event): |
| 438 | self.add_sel_arrow_big(x, y, height, event) | 438 | self.add_sel_arrow_big(x, y, height, event) |
| 439 | 439 | ||
| 440 | def add_sel_arrow_big(self, x, y, height, event): | 440 | def add_sel_arrow_big(self, x, y, height, event): |
| 441 | big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height | 441 | big_arrowhead_height = GraphFormat.BIG_ARROWHEAD_FACTOR * height |
| 442 | 442 | ||
| 443 | self.add_sel_region(SelectableRegion(x - big_arrowhead_height / Canvas.SQRT3, | 443 | self.add_sel_region(SelectableRegion(x - big_arrowhead_height / Canvas.SQRT3, |
| 444 | y, 2.0 * big_arrowhead_height / Canvas.SQRT3, height, event)) | 444 | y, 2.0 * big_arrowhead_height / Canvas.SQRT3, height, event)) |
| 445 | 445 | ||
| 446 | def draw_release_arrow_small(self, x, y, height, selected): | 446 | def draw_release_arrow_small(self, x, y, height, selected): |
| 447 | """Draws a small release arrow (most likely coming off the x-axis, although | 447 | """Draws a small release arrow (most likely coming off the x-axis, although |
| 448 | this method doesn't enforce this): x, y should give the top of the arrow""" | 448 | this method doesn't enforce this): x, y should give the top of the arrow""" |
| 449 | small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height | 449 | small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height |
| 450 | 450 | ||
| 451 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] | 451 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] |
| 452 | 452 | ||
| 453 | self.draw_line((x, y), (x - small_arrowhead_height, y + small_arrowhead_height), \ | 453 | self.draw_line((x, y), (x - small_arrowhead_height, y + small_arrowhead_height), \ |
| 454 | color, GraphFormat.BORDER_THICKNESS) | 454 | color, GraphFormat.BORDER_THICKNESS) |
| 455 | self.draw_line((x, y), (x + small_arrowhead_height, y + small_arrowhead_height), \ | 455 | self.draw_line((x, y), (x + small_arrowhead_height, y + small_arrowhead_height), \ |
| 456 | color, GraphFormat.BORDER_THICKNESS) | 456 | color, GraphFormat.BORDER_THICKNESS) |
| 457 | self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS) | 457 | self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS) |
| 458 | 458 | ||
| 459 | def add_sel_release_arrow_small(self, x, y, height, event): | 459 | def add_sel_release_arrow_small(self, x, y, height, event): |
| 460 | self.add_sel_arrow_small(x, y, height, event) | 460 | self.add_sel_arrow_small(x, y, height, event) |
| 461 | 461 | ||
| 462 | def draw_deadline_arrow_small(self, x, y, height, selected): | 462 | def draw_deadline_arrow_small(self, x, y, height, selected): |
| 463 | """Draws a small deadline arrow (most likely coming off the x-axis, although | 463 | """Draws a small deadline arrow (most likely coming off the x-axis, although |
| 464 | this method doesn't enforce this): x, y should give the top of the arrow""" | 464 | this method doesn't enforce this): x, y should give the top of the arrow""" |
| 465 | small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height | 465 | small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height |
| 466 | 466 | ||
| 467 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] | 467 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] |
| 468 | 468 | ||
| 469 | self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS) | 469 | self.draw_line((x, y), (x, y + height), color, GraphFormat.BORDER_THICKNESS) |
| 470 | self.draw_line((x - small_arrowhead_height, y + height - small_arrowhead_height), \ | 470 | self.draw_line((x - small_arrowhead_height, y + height - small_arrowhead_height), \ |
| 471 | (x, y + height), color, GraphFormat.BORDER_THICKNESS) | 471 | (x, y + height), color, GraphFormat.BORDER_THICKNESS) |
| 472 | self.draw_line((x + small_arrowhead_height, y + height - small_arrowhead_height), \ | 472 | self.draw_line((x + small_arrowhead_height, y + height - small_arrowhead_height), \ |
| 473 | (x, y + height), color, GraphFormat.BORDER_THICKNESS) | 473 | (x, y + height), color, GraphFormat.BORDER_THICKNESS) |
| 474 | 474 | ||
| 475 | def add_sel_deadline_arrow_small(self, x, y, height, event): | 475 | def add_sel_deadline_arrow_small(self, x, y, height, event): |
| 476 | self.add_sel_arrow_small(x, y, height, event) | 476 | self.add_sel_arrow_small(x, y, height, event) |
| 477 | 477 | ||
| 478 | def add_sel_arrow_small(self, x, y, height, event): | 478 | def add_sel_arrow_small(self, x, y, height, event): |
| 479 | small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height | 479 | small_arrowhead_height = GraphFormat.SMALL_ARROWHEAD_FACTOR * height |
| 480 | 480 | ||
| 481 | self.add_sel_region(SelectableRegion(x - small_arrowhead_height, y, | 481 | self.add_sel_region(SelectableRegion(x - small_arrowhead_height, y, |
| 482 | small_arrowhead_height * 2.0, height, event)) | 482 | small_arrowhead_height * 2.0, height, event)) |
| 483 | 483 | ||
| 484 | def draw_suspend_triangle(self, x, y, height, selected): | 484 | def draw_suspend_triangle(self, x, y, height, selected): |
| 485 | """Draws the triangle that marks a suspension. (x, y) gives the topmost (northernmost) point | 485 | """Draws the triangle that marks a suspension. (x, y) gives the topmost (northernmost) point |
| 486 | of the symbol.""" | 486 | of the symbol.""" |
| 487 | 487 | ||
| 488 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] | 488 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] |
| 489 | colors = [(0.0, 0.0, 0.0), color] | 489 | colors = [(0.0, 0.0, 0.0), color] |
| 490 | 490 | ||
| 491 | draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] | 491 | draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] |
| 492 | for i in range(0, 2): | 492 | for i in range(0, 2): |
| 493 | color = colors[i] | 493 | color = colors[i] |
| 494 | draw_func = draw_funcs[i] | 494 | draw_func = draw_funcs[i] |
| 495 | draw_func(self, [(x, y), (x + height / 2.0, y + height / 2.0), (x, y + height), (x, y)], \ | 495 | draw_func(self, [(x, y), (x + height / 2.0, y + height / 2.0), (x, y + height), (x, y)], \ |
| 496 | color, GraphFormat.BORDER_THICKNESS) | 496 | color, GraphFormat.BORDER_THICKNESS) |
| 497 | 497 | ||
| 498 | def add_sel_suspend_triangle(self, x, y, height, event): | 498 | def add_sel_suspend_triangle(self, x, y, height, event): |
| 499 | self.add_sel_region(SelectableRegion(x, y, height / 2.0, height, event)) | 499 | self.add_sel_region(SelectableRegion(x, y, height / 2.0, height, event)) |
| 500 | 500 | ||
| 501 | def draw_resume_triangle(self, x, y, height, selected): | 501 | def draw_resume_triangle(self, x, y, height, selected): |
| 502 | """Draws the triangle that marks a resumption. (x, y) gives the topmost (northernmost) point | 502 | """Draws the triangle that marks a resumption. (x, y) gives the topmost (northernmost) point |
| 503 | of the symbol.""" | 503 | of the symbol.""" |
| 504 | 504 | ||
| 505 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] | 505 | color = {False : GraphFormat.BORDER_COLOR, True : GraphFormat.HIGHLIGHT_COLOR}[selected] |
| 506 | colors = [(1.0, 1.0, 1.0), color] | 506 | colors = [(1.0, 1.0, 1.0), color] |
| 507 | 507 | ||
| 508 | draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] | 508 | draw_funcs = [self.__class__.fill_polyline, self.__class__.draw_polyline] |
| 509 | for i in range(0, 2): | 509 | for i in range(0, 2): |
| 510 | color = colors[i] | 510 | color = colors[i] |
| 511 | draw_func = draw_funcs[i] | 511 | draw_func = draw_funcs[i] |
| 512 | draw_func(self, [(x, y), (x - height / 2.0, y + height / 2.0), (x, y + height), (x, y)], \ | 512 | draw_func(self, [(x, y), (x - height / 2.0, y + height / 2.0), (x, y + height), (x, y)], \ |
| 513 | color, GraphFormat.BORDER_THICKNESS) | 513 | color, GraphFormat.BORDER_THICKNESS) |
| 514 | 514 | ||
| 515 | def add_sel_resume_triangle(self, x, y, height, event): | 515 | def add_sel_resume_triangle(self, x, y, height, event): |
| 516 | self.add_sel_region(SelectableRegion(x - height / 2.0, y, height / 2.0, height, event)) | 516 | self.add_sel_region(SelectableRegion(x - height / 2.0, y, height / 2.0, height, event)) |
| 517 | 517 | ||
| 518 | def clear_selectable_regions(self): | 518 | def clear_selectable_regions(self): |
| 519 | self.selectable_regions = {} | 519 | self.selectable_regions = {} |
| 520 | 520 | ||
| 521 | #def clear_selectable_regions(self, real_x, real_y, width, height): | 521 | #def clear_selectable_regions(self, real_x, real_y, width, height): |
| 522 | # x, y = self.surface.get_virt_coor(real_x, real_y) | 522 | # x, y = self.surface.get_virt_coor(real_x, real_y) |
| 523 | # for event in self.selectable_regions.keys(): | 523 | # for event in self.selectable_regions.keys(): |
| 524 | # if self.selectable_regions[event].intersects(x, y, width, height): | 524 | # if self.selectable_regions[event].intersects(x, y, width, height): |
| 525 | # del self.selectable_regions[event] | 525 | # del self.selectable_regions[event] |
| 526 | 526 | ||
| 527 | def add_sel_region(self, region): | 527 | def add_sel_region(self, region): |
| 528 | region.set_scale(self.scale) | 528 | region.set_scale(self.scale) |
| 529 | self.selectable_regions[region.get_event()] = region | 529 | self.selectable_regions[region.get_event()] = region |
| 530 | 530 | ||
| 531 | def get_sel_region(self, event): | 531 | def get_sel_region(self, event): |
| 532 | return self.selectable_regions[event] | 532 | return self.selectable_regions[event] |
| 533 | 533 | ||
| 534 | def has_sel_region(self, event): | 534 | def has_sel_region(self, event): |
| 535 | return event in self.selectable_regions | 535 | return event in self.selectable_regions |
| 536 | 536 | ||
| 537 | def get_selected_regions(self, real_x, real_y, width, height): | 537 | def get_selected_regions(self, real_x, real_y, width, height): |
| 538 | x, y = self.surface.get_virt_coor(real_x, real_y) | 538 | x, y = self.surface.get_virt_coor(real_x, real_y) |
| 539 | 539 | ||
| 540 | selected = {} | 540 | selected = {} |
| 541 | for event in self.selectable_regions: | 541 | for event in self.selectable_regions: |
| 542 | region = self.selectable_regions[event] | 542 | region = self.selectable_regions[event] |
| 543 | if region.intersects(x, y, width, height): | 543 | if region.intersects(x, y, width, height): |
| 544 | selected[event] = region | 544 | selected[event] = region |
| 545 | 545 | ||
| 546 | return selected | 546 | return selected |
| 547 | 547 | ||
| 548 | def whiteout(self): | 548 | def whiteout(self): |
| 549 | """Overwrites the surface completely white, but technically doesn't delete anything""" | 549 | """Overwrites the surface completely white, but technically doesn't delete anything""" |
| 550 | # Make sure we don't scale here (we want to literally white out just this region) | 550 | # Make sure we don't scale here (we want to literally white out just this region) |
| 551 | 551 | ||
| 552 | x, y = self.surface.get_virt_coor_unscaled(0, 0) | 552 | x, y = self.surface.get_virt_coor_unscaled(0, 0) |
| 553 | width, height = self.unscaled(self.surface.width, self.surface.height) | 553 | width, height = self.unscaled(self.surface.width, self.surface.height) |
| 554 | 554 | ||
| 555 | self.fill_rect(x, y, width, height, (1.0, 1.0, 1.0), False) | 555 | self.fill_rect(x, y, width, height, (1.0, 1.0, 1.0), False) |
| 556 | 556 | ||
| 557 | def get_item_color(self, n): | 557 | def get_item_color(self, n): |
| 558 | """Gets the nth color in the item color list, which are the colors used to draw the items | 558 | """Gets the nth color in the item color list, which are the colors used to draw the items |
| 559 | on the y-axis. Note that there are conceptually infinitely | 559 | on the y-axis. Note that there are conceptually infinitely |
| 560 | many patterns because the patterns repeat -- that is, we just mod out by the size of the pattern | 560 | many patterns because the patterns repeat -- that is, we just mod out by the size of the pattern |
| 561 | list when indexing.""" | 561 | list when indexing.""" |
| 562 | return self.item_clist[n % len(self.item_clist)] | 562 | return self.item_clist[n % len(self.item_clist)] |
| 563 | 563 | ||
| 564 | def get_bar_pattern(self, n): | 564 | def get_bar_pattern(self, n): |
| 565 | """Gets the nth pattern in the bar pattern list, which is a list of surfaces that are used to | 565 | """Gets the nth pattern in the bar pattern list, which is a list of surfaces that are used to |
| 566 | fill in the bars. Note that there are conceptually infinitely | 566 | fill in the bars. Note that there are conceptually infinitely |
| @@ -574,38 +574,38 @@ class CairoCanvas(Canvas): | |||
| 574 | """This is a basic class that stores and draws on a Cairo surface, | 574 | """This is a basic class that stores and draws on a Cairo surface, |
| 575 | using various primitives related to drawing a real-time graph (up-arrows, | 575 | using various primitives related to drawing a real-time graph (up-arrows, |
| 576 | down-arrows, bars, ...). | 576 | down-arrows, bars, ...). |
| 577 | 577 | ||
| 578 | This is the lowest-level non-abstract representation | 578 | This is the lowest-level non-abstract representation |
| 579 | (aside perhaps from the Cairo surface itself) of a real-time graph. | 579 | (aside perhaps from the Cairo surface itself) of a real-time graph. |
| 580 | It allows the user to draw primitives at certain locations, but for | 580 | It allows the user to draw primitives at certain locations, but for |
| 581 | the most part does not know anything about real-time scheduling, | 581 | the most part does not know anything about real-time scheduling, |
| 582 | just how to draw the basic parts that make up a schedule graph. | 582 | just how to draw the basic parts that make up a schedule graph. |
| 583 | For that, see Graph or its descendants.""" | 583 | For that, see Graph or its descendants.""" |
| 584 | 584 | ||
| 585 | #def __init__(self, fname, width, height, item_clist, bar_plist, surface): | 585 | #def __init__(self, fname, width, height, item_clist, bar_plist, surface): |
| 586 | # """Creates a new Canvas of dimensions (width, height). The | 586 | # """Creates a new Canvas of dimensions (width, height). The |
| 587 | # parameters ``item_plist'' and ``bar_plist'' each specify a list | 587 | # parameters ``item_plist'' and ``bar_plist'' each specify a list |
| 588 | # of patterns to choose from when drawing the items on the y-axis | 588 | # of patterns to choose from when drawing the items on the y-axis |
| 589 | # or filling in bars, respectively.""" | 589 | # or filling in bars, respectively.""" |
| 590 | 590 | ||
| 591 | # super(CairoCanvas, self).__init__(fname, width, height, item_clist, bar_plist, surface) | 591 | # super(CairoCanvas, self).__init__(fname, width, height, item_clist, bar_plist, surface) |
| 592 | 592 | ||
| 593 | #def clear(self): | 593 | #def clear(self): |
| 594 | # self.surface = self.SurfaceType(self.width, self.height, self.fname) | 594 | # self.surface = self.SurfaceType(self.width, self.height, self.fname) |
| 595 | # self.whiteout() | 595 | # self.whiteout() |
| 596 | 596 | ||
| 597 | def get_surface(self): | 597 | def get_surface(self): |
| 598 | """Gets the Surface that we are drawing on in its current state.""" | 598 | """Gets the Surface that we are drawing on in its current state.""" |
| 599 | return self.surface | 599 | return self.surface |
| 600 | 600 | ||
| 601 | def _rect_common(self, x, y, width, height, color, thickness, clip_side=None, do_snap=True): | 601 | def _rect_common(self, x, y, width, height, color, thickness, clip_side=None, do_snap=True): |
| 602 | EXTRA_FACTOR = 2.0 | 602 | EXTRA_FACTOR = 2.0 |
| 603 | 603 | ||
| 604 | x, y, width, height = self.scaled(x, y, width, height) | 604 | x, y, width, height = self.scaled(x, y, width, height) |
| 605 | x, y = self.surface.get_real_coor(x, y) | 605 | x, y = self.surface.get_real_coor(x, y) |
| 606 | max_width = self.surface.width + EXTRA_FACTOR * thickness | 606 | max_width = self.surface.width + EXTRA_FACTOR * thickness |
| 607 | max_height = self.surface.height + EXTRA_FACTOR * thickness | 607 | max_height = self.surface.height + EXTRA_FACTOR * thickness |
| 608 | 608 | ||
| 609 | # if dimensions are really large this can cause Cairo problems -- | 609 | # if dimensions are really large this can cause Cairo problems -- |
| 610 | # so clip it to the size of the surface, which is the only part we see anyway | 610 | # so clip it to the size of the surface, which is the only part we see anyway |
| 611 | if x < 0: | 611 | if x < 0: |
| @@ -618,11 +618,11 @@ class CairoCanvas(Canvas): | |||
| 618 | width = max_width | 618 | width = max_width |
| 619 | if height > max_height: | 619 | if height > max_height: |
| 620 | height = max_height | 620 | height = max_height |
| 621 | 621 | ||
| 622 | if do_snap: | 622 | if do_snap: |
| 623 | x = snap(x) | 623 | x = snap(x) |
| 624 | y = snap(y) | 624 | y = snap(y) |
| 625 | 625 | ||
| 626 | if clip_side == AlignMode.LEFT: | 626 | if clip_side == AlignMode.LEFT: |
| 627 | self.surface.ctx.move_to(x, y) | 627 | self.surface.ctx.move_to(x, y) |
| 628 | self.surface.ctx.line_to(x + width, y) | 628 | self.surface.ctx.line_to(x + width, y) |
| @@ -636,23 +636,23 @@ class CairoCanvas(Canvas): | |||
| 636 | else: | 636 | else: |
| 637 | # don't clip one edge of the rectangle -- just draw a Cairo rectangle | 637 | # don't clip one edge of the rectangle -- just draw a Cairo rectangle |
| 638 | self.surface.ctx.rectangle(x, y, width, height) | 638 | self.surface.ctx.rectangle(x, y, width, height) |
| 639 | 639 | ||
| 640 | self.surface.ctx.set_line_width(thickness * self.scale) | 640 | self.surface.ctx.set_line_width(thickness * self.scale) |
| 641 | self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) | 641 | self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) |
| 642 | 642 | ||
| 643 | def draw_rect(self, x, y, width, height, color, thickness, clip_side=None, do_snap=True): | 643 | def draw_rect(self, x, y, width, height, color, thickness, clip_side=None, do_snap=True): |
| 644 | self._rect_common(x, y, width, height, color, thickness, clip_side, do_snap) | 644 | self._rect_common(x, y, width, height, color, thickness, clip_side, do_snap) |
| 645 | self.surface.ctx.stroke() | 645 | self.surface.ctx.stroke() |
| 646 | 646 | ||
| 647 | def fill_rect(self, x, y, width, height, color, do_snap=True): | 647 | def fill_rect(self, x, y, width, height, color, do_snap=True): |
| 648 | self._rect_common(x, y, width, height, color, 1, do_snap) | 648 | self._rect_common(x, y, width, height, color, 1, do_snap) |
| 649 | self.surface.ctx.fill() | 649 | self.surface.ctx.fill() |
| 650 | 650 | ||
| 651 | def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, do_snap=True): | 651 | def fill_rect_fade(self, x, y, width, height, lcolor, rcolor, do_snap=True): |
| 652 | """Draws a rectangle somewhere, filled in with the fade.""" | 652 | """Draws a rectangle somewhere, filled in with the fade.""" |
| 653 | x, y, width, height = self.scaled(x, y, width, height) | 653 | x, y, width, height = self.scaled(x, y, width, height) |
| 654 | x, y = self.surface.get_real_coor(x, y) | 654 | x, y = self.surface.get_real_coor(x, y) |
| 655 | 655 | ||
| 656 | if do_snap: | 656 | if do_snap: |
| 657 | linear = cairo.LinearGradient(snap(x), snap(y), \ | 657 | linear = cairo.LinearGradient(snap(x), snap(y), \ |
| 658 | snap(x + width), snap(y + height)) | 658 | snap(x + width), snap(y + height)) |
| @@ -667,7 +667,7 @@ class CairoCanvas(Canvas): | |||
| 667 | else: | 667 | else: |
| 668 | self.surface.ctx.rectangle(x, y, width, height) | 668 | self.surface.ctx.rectangle(x, y, width, height) |
| 669 | self.surface.ctx.fill() | 669 | self.surface.ctx.fill() |
| 670 | 670 | ||
| 671 | def draw_line(self, p0, p1, color, thickness, do_snap=True): | 671 | def draw_line(self, p0, p1, color, thickness, do_snap=True): |
| 672 | """Draws a line from p0 to p1 with a certain color and thickness.""" | 672 | """Draws a line from p0 to p1 with a certain color and thickness.""" |
| 673 | p0 = self.scaled(p0[0], p0[1]) | 673 | p0 = self.scaled(p0[0], p0[1]) |
| @@ -677,36 +677,36 @@ class CairoCanvas(Canvas): | |||
| 677 | if do_snap: | 677 | if do_snap: |
| 678 | p0 = (snap(p0[0]), snap(p0[1])) | 678 | p0 = (snap(p0[0]), snap(p0[1])) |
| 679 | p1 = (snap(p1[0]), snap(p1[1])) | 679 | p1 = (snap(p1[0]), snap(p1[1])) |
| 680 | 680 | ||
| 681 | self.surface.ctx.move_to(p0[0], p0[1]) | 681 | self.surface.ctx.move_to(p0[0], p0[1]) |
| 682 | self.surface.ctx.line_to(p1[0], p1[1]) | 682 | self.surface.ctx.line_to(p1[0], p1[1]) |
| 683 | self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) | 683 | self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) |
| 684 | self.surface.ctx.set_line_width(thickness * self.scale) | 684 | self.surface.ctx.set_line_width(thickness * self.scale) |
| 685 | self.surface.ctx.stroke() | 685 | self.surface.ctx.stroke() |
| 686 | 686 | ||
| 687 | def _polyline_common(self, coor_list, color, thickness, do_snap=True): | 687 | def _polyline_common(self, coor_list, color, thickness, do_snap=True): |
| 688 | scaled_coor_list = [self.scaled(coor[0], coor[1]) for coor in coor_list] | 688 | scaled_coor_list = [self.scaled(coor[0], coor[1]) for coor in coor_list] |
| 689 | real_coor_list = [self.surface.get_real_coor(coor[0], coor[1]) for coor in scaled_coor_list] | 689 | real_coor_list = [self.surface.get_real_coor(coor[0], coor[1]) for coor in scaled_coor_list] |
| 690 | 690 | ||
| 691 | self.surface.ctx.move_to(real_coor_list[0][0], real_coor_list[0][1]) | 691 | self.surface.ctx.move_to(real_coor_list[0][0], real_coor_list[0][1]) |
| 692 | if do_snap: | 692 | if do_snap: |
| 693 | for i in range(0, len(real_coor_list)): | 693 | for i in range(0, len(real_coor_list)): |
| 694 | real_coor_list[i] = (snap(real_coor_list[i][0]), snap(real_coor_list[i][1])) | 694 | real_coor_list[i] = (snap(real_coor_list[i][0]), snap(real_coor_list[i][1])) |
| 695 | 695 | ||
| 696 | for coor in real_coor_list[1:]: | 696 | for coor in real_coor_list[1:]: |
| 697 | self.surface.ctx.line_to(coor[0], coor[1]) | 697 | self.surface.ctx.line_to(coor[0], coor[1]) |
| 698 | 698 | ||
| 699 | self.surface.ctx.set_line_width(thickness * self.scale) | 699 | self.surface.ctx.set_line_width(thickness * self.scale) |
| 700 | self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) | 700 | self.surface.ctx.set_source_rgb(color[0], color[1], color[2]) |
| 701 | 701 | ||
| 702 | def draw_polyline(self, coor_list, color, thickness, do_snap=True): | 702 | def draw_polyline(self, coor_list, color, thickness, do_snap=True): |
| 703 | self._polyline_common(coor_list, color, thickness, do_snap) | 703 | self._polyline_common(coor_list, color, thickness, do_snap) |
| 704 | self.surface.ctx.stroke() | 704 | self.surface.ctx.stroke() |
| 705 | 705 | ||
| 706 | def fill_polyline(self, coor_list, color, thickness, do_snap=True): | 706 | def fill_polyline(self, coor_list, color, thickness, do_snap=True): |
| 707 | self._polyline_common(coor_list, color, thickness, do_snap) | 707 | self._polyline_common(coor_list, color, thickness, do_snap) |
| 708 | self.surface.ctx.fill() | 708 | self.surface.ctx.fill() |
| 709 | 709 | ||
| 710 | def _draw_label_common(self, text, x, y, fopts, x_bearing_factor, \ | 710 | def _draw_label_common(self, text, x, y, fopts, x_bearing_factor, \ |
| 711 | f_descent_factor, width_factor, f_height_factor, do_snap=True): | 711 | f_descent_factor, width_factor, f_height_factor, do_snap=True): |
| 712 | """Helper function for drawing a label with some alignment. Instead of taking in an alignment, | 712 | """Helper function for drawing a label with some alignment. Instead of taking in an alignment, |
| @@ -714,30 +714,30 @@ class CairoCanvas(Canvas): | |||
| 714 | the x and y parameters. Only should be used internally.""" | 714 | the x and y parameters. Only should be used internally.""" |
| 715 | x, y = self.scaled(x, y) | 715 | x, y = self.scaled(x, y) |
| 716 | x, y = self.surface.get_real_coor(x, y) | 716 | x, y = self.surface.get_real_coor(x, y) |
| 717 | 717 | ||
| 718 | self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0) | 718 | self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0) |
| 719 | 719 | ||
| 720 | self.surface.ctx.select_font_face(fopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) | 720 | self.surface.ctx.select_font_face(fopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) |
| 721 | self.surface.ctx.set_font_size(fopts.size * self.scale) | 721 | self.surface.ctx.set_font_size(fopts.size * self.scale) |
| 722 | 722 | ||
| 723 | fe = self.surface.ctx.font_extents() | 723 | fe = self.surface.ctx.font_extents() |
| 724 | f_ascent, f_descent, f_height = fe[:3] | 724 | f_ascent, f_descent, f_height = fe[:3] |
| 725 | 725 | ||
| 726 | te = self.surface.ctx.text_extents(text) | 726 | te = self.surface.ctx.text_extents(text) |
| 727 | x_bearing, y_bearing, width, height = te[:4] | 727 | x_bearing, y_bearing, width, height = te[:4] |
| 728 | 728 | ||
| 729 | actual_x = x - x_bearing * x_bearing_factor - width * width_factor | 729 | actual_x = x - x_bearing * x_bearing_factor - width * width_factor |
| 730 | actual_y = y - f_descent * f_descent_factor + f_height * f_height_factor | 730 | actual_y = y - f_descent * f_descent_factor + f_height * f_height_factor |
| 731 | 731 | ||
| 732 | self.surface.ctx.set_source_rgb(fopts.color[0], fopts.color[1], fopts.color[2]) | 732 | self.surface.ctx.set_source_rgb(fopts.color[0], fopts.color[1], fopts.color[2]) |
| 733 | 733 | ||
| 734 | if do_snap: | 734 | if do_snap: |
| 735 | self.surface.ctx.move_to(snap(actual_x), snap(actual_y)) | 735 | self.surface.ctx.move_to(snap(actual_x), snap(actual_y)) |
| 736 | else: | 736 | else: |
| 737 | self.surface.ctx.move_to(actual_x, actual_y) | 737 | self.surface.ctx.move_to(actual_x, actual_y) |
| 738 | 738 | ||
| 739 | self.surface.ctx.show_text(text) | 739 | self.surface.ctx.show_text(text) |
| 740 | 740 | ||
| 741 | def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True): | 741 | def draw_label(self, text, x, y, fopts=GraphFormat.DEF_FOPTS_LABEL, halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True): |
| 742 | """Draws a label with the given parameters, with the given horizontal and vertical justification. One can override | 742 | """Draws a label with the given parameters, with the given horizontal and vertical justification. One can override |
| 743 | the color from ``fopts'' by passing something in to ``pattern'', which overrides the color with an arbitrary | 743 | the color from ``fopts'' by passing something in to ``pattern'', which overrides the color with an arbitrary |
| @@ -747,21 +747,21 @@ class CairoCanvas(Canvas): | |||
| 747 | if halign not in halign_factors: | 747 | if halign not in halign_factors: |
| 748 | raise ValueError('Invalid alignment value') | 748 | raise ValueError('Invalid alignment value') |
| 749 | x_bearing_factor, width_factor = halign_factors[halign] | 749 | x_bearing_factor, width_factor = halign_factors[halign] |
| 750 | 750 | ||
| 751 | valign_factors = {AlignMode.BOTTOM : (0.0, 0.0), AlignMode.CENTER : (1.0, 0.5), AlignMode.TOP : (1.0, 1.0)} | 751 | valign_factors = {AlignMode.BOTTOM : (0.0, 0.0), AlignMode.CENTER : (1.0, 0.5), AlignMode.TOP : (1.0, 1.0)} |
| 752 | if valign not in valign_factors: | 752 | if valign not in valign_factors: |
| 753 | raise ValueError('Invalid alignment value') | 753 | raise ValueError('Invalid alignment value') |
| 754 | f_descent_factor, f_height_factor = valign_factors[valign] | 754 | f_descent_factor, f_height_factor = valign_factors[valign] |
| 755 | 755 | ||
| 756 | self._draw_label_common(text, x, y, fopts, x_bearing_factor, \ | 756 | self._draw_label_common(text, x, y, fopts, x_bearing_factor, \ |
| 757 | f_descent_factor, width_factor, f_height_factor, do_snap) | 757 | f_descent_factor, width_factor, f_height_factor, do_snap) |
| 758 | 758 | ||
| 759 | def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ | 759 | def draw_label_with_sscripts(self, text, supscript, subscript, x, y, \ |
| 760 | textfopts=GraphFormat.DEF_FOPTS_LABEL, sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \ | 760 | textfopts=GraphFormat.DEF_FOPTS_LABEL, sscriptfopts=GraphFormat.DEF_FOPTS_LABEL_SSCRIPT, \ |
| 761 | halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True): | 761 | halign=AlignMode.LEFT, valign=AlignMode.BOTTOM, do_snap=True): |
| 762 | """Draws a label, but also optionally allows a superscript and subscript to be rendered.""" | 762 | """Draws a label, but also optionally allows a superscript and subscript to be rendered.""" |
| 763 | self.draw_label(text, x, y, textfopts, halign, valign) | 763 | self.draw_label(text, x, y, textfopts, halign, valign) |
| 764 | 764 | ||
| 765 | self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0) | 765 | self.surface.ctx.set_source_rgb(0.0, 0.0, 0.0) |
| 766 | self.surface.ctx.select_font_face(textfopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) | 766 | self.surface.ctx.select_font_face(textfopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) |
| 767 | self.surface.ctx.set_font_size(textfopts.size) | 767 | self.surface.ctx.set_font_size(textfopts.size) |
| @@ -781,7 +781,7 @@ class CairoCanvas(Canvas): | |||
| 781 | ytmp = y | 781 | ytmp = y |
| 782 | ytmp = y + f_height / 4.0 | 782 | ytmp = y + f_height / 4.0 |
| 783 | self.draw_label(subscript, xtmp, ytmp, sscriptfopts, halign, valign, do_snap) | 783 | self.draw_label(subscript, xtmp, ytmp, sscriptfopts, halign, valign, do_snap) |
| 784 | 784 | ||
| 785 | # represents a selectable region of the graph | 785 | # represents a selectable region of the graph |
| 786 | class SelectableRegion(object): | 786 | class SelectableRegion(object): |
| 787 | def __init__(self, x, y, width, height, event): | 787 | def __init__(self, x, y, width, height, event): |
| @@ -791,19 +791,19 @@ class SelectableRegion(object): | |||
| 791 | self.height = height | 791 | self.height = height |
| 792 | self.event = event | 792 | self.event = event |
| 793 | self.scale = 1.0 | 793 | self.scale = 1.0 |
| 794 | 794 | ||
| 795 | def get_dimensions(self): | 795 | def get_dimensions(self): |
| 796 | return (self.x, self.y, self.width, self.height) | 796 | return (self.x, self.y, self.width, self.height) |
| 797 | 797 | ||
| 798 | def get_event(self): | 798 | def get_event(self): |
| 799 | return self.event | 799 | return self.event |
| 800 | 800 | ||
| 801 | def set_scale(self, scale): | 801 | def set_scale(self, scale): |
| 802 | self.scale = scale | 802 | self.scale = scale |
| 803 | 803 | ||
| 804 | def intersects(self, x, y, width, height): | 804 | def intersects(self, x, y, width, height): |
| 805 | return x <= (self.x + self.width) * self.scale \ | 805 | return x <= (self.x + self.width) * self.scale \ |
| 806 | and x + width >= self.x * self.scale \ | 806 | and x + width >= self.x * self.scale \ |
| 807 | and y <= (self.y + self.height) * self.scale \ | 807 | and y <= (self.y + self.height) * self.scale \ |
| 808 | and y + height >= self.y * self.scale | 808 | and y + height >= self.y * self.scale |
| 809 | 809 | ||
diff --git a/unit_trace/viz/convert.py b/unit_trace/viz/convert.py index a7aff8b..d19bc73 100644 --- a/unit_trace/viz/convert.py +++ b/unit_trace/viz/convert.py | |||
| @@ -24,13 +24,13 @@ def _get_job_from_record(sched, record): | |||
| 24 | sched.get_tasks()[tname].add_job(Job(job_no, [])) | 24 | sched.get_tasks()[tname].add_job(Job(job_no, [])) |
| 25 | job = sched.get_tasks()[tname].get_jobs()[job_no] | 25 | job = sched.get_tasks()[tname].get_jobs()[job_no] |
| 26 | return job | 26 | return job |
| 27 | 27 | ||
| 28 | def convert_trace_to_schedule(stream): | 28 | def convert_trace_to_schedule(stream): |
| 29 | """The main function of interest in this module. Coverts a stream of records | 29 | """The main function of interest in this module. Coverts a stream of records |
| 30 | to a Schedule object.""" | 30 | to a Schedule object.""" |
| 31 | def noop(): | 31 | def noop(): |
| 32 | pass | 32 | pass |
| 33 | 33 | ||
| 34 | num_cpus, stream = _find_num_cpus(stream) | 34 | num_cpus, stream = _find_num_cpus(stream) |
| 35 | sched = Schedule('sched', num_cpus) | 35 | sched = Schedule('sched', num_cpus) |
| 36 | for record in stream: | 36 | for record in stream: |
| @@ -41,10 +41,10 @@ def convert_trace_to_schedule(stream): | |||
| 41 | if record.record_type == 'event': | 41 | if record.record_type == 'event': |
| 42 | job = _get_job_from_record(sched, record) | 42 | job = _get_job_from_record(sched, record) |
| 43 | cpu = record.cpu | 43 | cpu = record.cpu |
| 44 | 44 | ||
| 45 | if not hasattr(record, 'deadline'): | 45 | if not hasattr(record, 'deadline'): |
| 46 | record.deadline = None | 46 | record.deadline = None |
| 47 | 47 | ||
| 48 | actions = { | 48 | actions = { |
| 49 | 'name' : (noop), | 49 | 'name' : (noop), |
| 50 | 'params' : (noop), | 50 | 'params' : (noop), |
| @@ -64,27 +64,27 @@ def convert_trace_to_schedule(stream): | |||
| 64 | job.add_event(ResumeEvent(record.when, cpu))), | 64 | job.add_event(ResumeEvent(record.when, cpu))), |
| 65 | 'sys_release' : (noop) | 65 | 'sys_release' : (noop) |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | actions[record.type_name]() | 68 | actions[record.type_name]() |
| 69 | 69 | ||
| 70 | elif record.record_type == 'error': | 70 | elif record.record_type == 'error': |
| 71 | job = _get_job_from_record(sched, record.job) | 71 | job = _get_job_from_record(sched, record.job) |
| 72 | 72 | ||
| 73 | actions = { | 73 | actions = { |
| 74 | 'inversion_start' : (lambda : | 74 | 'inversion_start' : (lambda : |
| 75 | job.add_event(InversionStartEvent(record.job.inversion_start))), | 75 | job.add_event(InversionStartEvent(record.job.inversion_start))), |
| 76 | 'inversion_end' : (lambda : | 76 | 'inversion_end' : (lambda : |
| 77 | job.add_event(InversionEndEvent(record.job.inversion_end))) | 77 | job.add_event(InversionEndEvent(record.job.inversion_end))) |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | actions[record.type_name]() | 80 | actions[record.type_name]() |
| 81 | 81 | ||
| 82 | return sched | 82 | return sched |
| 83 | 83 | ||
| 84 | def _pid_to_task_name(pid): | 84 | def _pid_to_task_name(pid): |
| 85 | """Converts a PID to an appropriate name for a task.""" | 85 | """Converts a PID to an appropriate name for a task.""" |
| 86 | return str(pid) | 86 | return str(pid) |
| 87 | 87 | ||
| 88 | def _find_num_cpus(stream): | 88 | def _find_num_cpus(stream): |
| 89 | """Determines the number of CPUs used by scanning the binary format.""" | 89 | """Determines the number of CPUs used by scanning the binary format.""" |
| 90 | max = 0 | 90 | max = 0 |
| @@ -94,7 +94,7 @@ def _find_num_cpus(stream): | |||
| 94 | if record.record_type == 'event': | 94 | if record.record_type == 'event': |
| 95 | if record.cpu > max: | 95 | if record.cpu > max: |
| 96 | max = record.cpu | 96 | max = record.cpu |
| 97 | 97 | ||
| 98 | def recycle(l): | 98 | def recycle(l): |
| 99 | for record in l: | 99 | for record in l: |
| 100 | yield record | 100 | yield record |
diff --git a/unit_trace/viz/format.py b/unit_trace/viz/format.py index 33e0b03..c544e26 100644 --- a/unit_trace/viz/format.py +++ b/unit_trace/viz/format.py | |||
| @@ -13,41 +13,41 @@ class AlignMode(object): | |||
| 13 | LEFT = 0 | 13 | LEFT = 0 |
| 14 | CENTER = 1 | 14 | CENTER = 1 |
| 15 | RIGHT = 2 | 15 | RIGHT = 2 |
| 16 | 16 | ||
| 17 | BOTTOM = 3 | 17 | BOTTOM = 3 |
| 18 | TOP = 4 | 18 | TOP = 4 |
| 19 | 19 | ||
| 20 | class GraphFormat(object): | 20 | class GraphFormat(object): |
| 21 | """Container class for a bunch of optional and non-optional attributes to configure the appearance of the graph | 21 | """Container class for a bunch of optional and non-optional attributes to configure the appearance of the graph |
| 22 | (because it would be annoying to just have these all as raw arguments to the Graph constructor, and many people | 22 | (because it would be annoying to just have these all as raw arguments to the Graph constructor, and many people |
| 23 | probably don't care about most of them anyway).""" | 23 | probably don't care about most of them anyway).""" |
| 24 | 24 | ||
| 25 | GRID_COLOR = (0.7, 0.7, 0.7) | 25 | GRID_COLOR = (0.7, 0.7, 0.7) |
| 26 | HIGHLIGHT_COLOR = (0.85, 0.0, 0.0) | 26 | HIGHLIGHT_COLOR = (0.85, 0.0, 0.0) |
| 27 | BORDER_COLOR = (0.0, 0.0, 0.0) | 27 | BORDER_COLOR = (0.0, 0.0, 0.0) |
| 28 | LITE_BORDER_COLOR = (0.4, 0.4, 0.4) | 28 | LITE_BORDER_COLOR = (0.4, 0.4, 0.4) |
| 29 | 29 | ||
| 30 | BORDER_THICKNESS = 1 | 30 | BORDER_THICKNESS = 1 |
| 31 | GRID_THICKNESS = 1 | 31 | GRID_THICKNESS = 1 |
| 32 | AXIS_THICKNESS = 1 | 32 | AXIS_THICKNESS = 1 |
| 33 | 33 | ||
| 34 | BAND_THICKNESS = 1.5 | 34 | BAND_THICKNESS = 1.5 |
| 35 | BAND_COLOR = (0.85, 0.0, 0.0) | 35 | BAND_COLOR = (0.85, 0.0, 0.0) |
| 36 | 36 | ||
| 37 | X_AXIS_MEASURE_OFS = 30 | 37 | X_AXIS_MEASURE_OFS = 30 |
| 38 | X_AXIS_LABEL_GAP = 10 | 38 | X_AXIS_LABEL_GAP = 10 |
| 39 | Y_AXIS_ITEM_GAP = 10 | 39 | Y_AXIS_ITEM_GAP = 10 |
| 40 | MAJ_TICK_SIZE = 20 | 40 | MAJ_TICK_SIZE = 20 |
| 41 | MIN_TICK_SIZE = 12 | 41 | MIN_TICK_SIZE = 12 |
| 42 | 42 | ||
| 43 | BIG_ARROWHEAD_FACTOR = 0.2 | 43 | BIG_ARROWHEAD_FACTOR = 0.2 |
| 44 | SMALL_ARROWHEAD_FACTOR = 0.3 | 44 | SMALL_ARROWHEAD_FACTOR = 0.3 |
| 45 | TEE_FACTOR = 0.3 | 45 | TEE_FACTOR = 0.3 |
| 46 | 46 | ||
| 47 | DEF_FOPTS_LABEL = FontOptions("Times", 16, (0.0, 0.0, 0.0)) | 47 | DEF_FOPTS_LABEL = FontOptions("Times", 16, (0.0, 0.0, 0.0)) |
| 48 | DEF_FOPTS_LABEL_SSCRIPT = FontOptions("Times", 8, (0.0, 0.0, 0.0)) | 48 | DEF_FOPTS_LABEL_SSCRIPT = FontOptions("Times", 8, (0.0, 0.0, 0.0)) |
| 49 | DEF_FOPTS_MAJ = FontOptions("Times", 14, (0.1, 0.1, 0.1)) | 49 | DEF_FOPTS_MAJ = FontOptions("Times", 14, (0.1, 0.1, 0.1)) |
| 50 | DEF_FOPTS_MIN = FontOptions("Times", 9, (0.1, 0.1, 0.1)) | 50 | DEF_FOPTS_MIN = FontOptions("Times", 9, (0.1, 0.1, 0.1)) |
| 51 | DEF_FOPTS_ITEM = FontOptions("Times", 20, (0.0, 0.5, 0.1)) | 51 | DEF_FOPTS_ITEM = FontOptions("Times", 20, (0.0, 0.5, 0.1)) |
| 52 | DEF_FOPTS_BAR = FontOptions("Times", 14, (0.0, 0.0, 0.0)) | 52 | DEF_FOPTS_BAR = FontOptions("Times", 14, (0.0, 0.0, 0.0)) |
| 53 | DEF_FOPTS_BAR_SSCRIPT = FontOptions("Times", 7, (0.0, 0.0, 0.0)) | 53 | DEF_FOPTS_BAR_SSCRIPT = FontOptions("Times", 7, (0.0, 0.0, 0.0)) |
| @@ -55,31 +55,31 @@ class GraphFormat(object): | |||
| 55 | DEF_FOPTS_MINI_BAR_SSCRIPT = FontOptions("Times", 7, (0.0, 0.0, 0.0)) | 55 | DEF_FOPTS_MINI_BAR_SSCRIPT = FontOptions("Times", 7, (0.0, 0.0, 0.0)) |
| 56 | DEF_FOPTS_ARROW = FontOptions("Times", 12, (0.0, 0.0, 0.0)) | 56 | DEF_FOPTS_ARROW = FontOptions("Times", 12, (0.0, 0.0, 0.0)) |
| 57 | DEF_FOPTS_ARROW_SSCRIPT = FontOptions("Times", 7, (0.0, 0.0, 0.0)) | 57 | DEF_FOPTS_ARROW_SSCRIPT = FontOptions("Times", 7, (0.0, 0.0, 0.0)) |
| 58 | 58 | ||
| 59 | LEFT_SIDE_PAD = 30 | 59 | LEFT_SIDE_PAD = 30 |
| 60 | WIDTH_PAD = 50 | 60 | WIDTH_PAD = 50 |
| 61 | HEIGHT_PAD = 150 | 61 | HEIGHT_PAD = 150 |
| 62 | Y_ITEM_PAD_FACTOR = 0.5 | 62 | Y_ITEM_PAD_FACTOR = 0.5 |
| 63 | 63 | ||
| 64 | DEF_TIME_PER_MAJ = 10 | 64 | DEF_TIME_PER_MAJ = 10 |
| 65 | DEF_MAJ_SEP = 200 | 65 | DEF_MAJ_SEP = 200 |
| 66 | DEF_MIN_PER_MAJ = 5 | 66 | DEF_MIN_PER_MAJ = 5 |
| 67 | DEF_Y_ITEM_SIZE = 50 | 67 | DEF_Y_ITEM_SIZE = 50 |
| 68 | 68 | ||
| 69 | AXIS_LABEL_VERT_OFS = 30 | 69 | AXIS_LABEL_VERT_OFS = 30 |
| 70 | BAR_SIZE_FACTOR = 0.4 | 70 | BAR_SIZE_FACTOR = 0.4 |
| 71 | MINI_BAR_SIZE_FACTOR = 0.2 | 71 | MINI_BAR_SIZE_FACTOR = 0.2 |
| 72 | BAR_MINI_BAR_GAP_FACTOR = 0.1 | 72 | BAR_MINI_BAR_GAP_FACTOR = 0.1 |
| 73 | 73 | ||
| 74 | BAR_LABEL_OFS = 2 | 74 | BAR_LABEL_OFS = 2 |
| 75 | MINI_BAR_LABEL_OFS = 1 | 75 | MINI_BAR_LABEL_OFS = 1 |
| 76 | ARROW_LABEL_OFS = 2 | 76 | ARROW_LABEL_OFS = 2 |
| 77 | 77 | ||
| 78 | BLOCK_TRIANGLE_FACTOR = 0.7 | 78 | BLOCK_TRIANGLE_FACTOR = 0.7 |
| 79 | BIG_ARROW_FACTOR = 1.6 | 79 | BIG_ARROW_FACTOR = 1.6 |
| 80 | SMALL_ARROW_FACTOR = 0.6 | 80 | SMALL_ARROW_FACTOR = 0.6 |
| 81 | COMPLETION_MARKER_FACTOR = 1.6 | 81 | COMPLETION_MARKER_FACTOR = 1.6 |
| 82 | 82 | ||
| 83 | def __init__(self, time_per_maj=DEF_TIME_PER_MAJ, maj_sep=DEF_MAJ_SEP, \ | 83 | def __init__(self, time_per_maj=DEF_TIME_PER_MAJ, maj_sep=DEF_MAJ_SEP, \ |
| 84 | min_per_maj=DEF_MIN_PER_MAJ, y_item_size=DEF_Y_ITEM_SIZE, bar_fopts=DEF_FOPTS_BAR, \ | 84 | min_per_maj=DEF_MIN_PER_MAJ, y_item_size=DEF_Y_ITEM_SIZE, bar_fopts=DEF_FOPTS_BAR, \ |
| 85 | item_fopts=DEF_FOPTS_ITEM, show_min=False, majfopts=DEF_FOPTS_MAJ, \ | 85 | item_fopts=DEF_FOPTS_ITEM, show_min=False, majfopts=DEF_FOPTS_MAJ, \ |
diff --git a/unit_trace/viz/graph.py b/unit_trace/viz/graph.py index 6982e7c..a10d9bd 100644 --- a/unit_trace/viz/graph.py +++ b/unit_trace/viz/graph.py | |||
| @@ -13,7 +13,7 @@ class Graph(object): | |||
| 13 | Pattern([(0.75, 0.75, 0.75)])] | 13 | Pattern([(0.75, 0.75, 0.75)])] |
| 14 | DEF_ITEM_CLIST = [(0.3, 0.0, 0.0), (0.0, 0.3, 0.0), (0.0, 0.0, 0.3), (0.3, 0.3, 0.0), (0.0, 0.3, 0.3), | 14 | DEF_ITEM_CLIST = [(0.3, 0.0, 0.0), (0.0, 0.3, 0.0), (0.0, 0.0, 0.3), (0.3, 0.3, 0.0), (0.0, 0.3, 0.3), |
| 15 | (0.3, 0.0, 0.3)] | 15 | (0.3, 0.0, 0.3)] |
| 16 | 16 | ||
| 17 | def __init__(self, CanvasType, surface, start_time, end_time, y_item_list, attrs=GraphFormat(), | 17 | def __init__(self, CanvasType, surface, start_time, end_time, y_item_list, attrs=GraphFormat(), |
| 18 | item_clist=DEF_ITEM_CLIST, bar_plist=DEF_BAR_PLIST): | 18 | item_clist=DEF_ITEM_CLIST, bar_plist=DEF_BAR_PLIST): |
| 19 | # deal with possibly blank schedules | 19 | # deal with possibly blank schedules |
| @@ -21,16 +21,16 @@ class Graph(object): | |||
| 21 | start_time = 0 | 21 | start_time = 0 |
| 22 | if end_time is None: | 22 | if end_time is None: |
| 23 | end_time = 0 | 23 | end_time = 0 |
| 24 | 24 | ||
| 25 | if start_time > end_time: | 25 | if start_time > end_time: |
| 26 | raise ValueError("Litmus is not a time machine") | 26 | raise ValueError("Litmus is not a time machine") |
| 27 | 27 | ||
| 28 | self.attrs = attrs | 28 | self.attrs = attrs |
| 29 | self.start_time = start_time | 29 | self.start_time = start_time |
| 30 | self.end_time = end_time | 30 | self.end_time = end_time |
| 31 | self.y_item_list = y_item_list | 31 | self.y_item_list = y_item_list |
| 32 | self.num_maj = int(math.ceil((self.end_time - self.start_time) * 1.0 / self.attrs.time_per_maj)) + 1 | 32 | self.num_maj = int(math.ceil((self.end_time - self.start_time) * 1.0 / self.attrs.time_per_maj)) + 1 |
| 33 | 33 | ||
| 34 | width = self.num_maj * self.attrs.maj_sep + GraphFormat.X_AXIS_MEASURE_OFS + GraphFormat.WIDTH_PAD | 34 | width = self.num_maj * self.attrs.maj_sep + GraphFormat.X_AXIS_MEASURE_OFS + GraphFormat.WIDTH_PAD |
| 35 | height = (len(self.y_item_list) + 1) * self.attrs.y_item_size + GraphFormat.HEIGHT_PAD | 35 | height = (len(self.y_item_list) + 1) * self.attrs.y_item_size + GraphFormat.HEIGHT_PAD |
| 36 | 36 | ||
| @@ -40,7 +40,7 @@ class Graph(object): | |||
| 40 | extra_width = 0.0 | 40 | extra_width = 0.0 |
| 41 | dummy_surface = surface.__class__() | 41 | dummy_surface = surface.__class__() |
| 42 | dummy_surface.renew(10, 10) | 42 | dummy_surface.renew(10, 10) |
| 43 | 43 | ||
| 44 | dummy_surface.ctx.select_font_face(self.attrs.item_fopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) | 44 | dummy_surface.ctx.select_font_face(self.attrs.item_fopts.name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) |
| 45 | dummy_surface.ctx.set_font_size(self.attrs.item_fopts.size) | 45 | dummy_surface.ctx.set_font_size(self.attrs.item_fopts.size) |
| 46 | for item in self.y_item_list: | 46 | for item in self.y_item_list: |
| @@ -49,49 +49,49 @@ class Graph(object): | |||
| 49 | cur_width = te[2] | 49 | cur_width = te[2] |
| 50 | if cur_width > extra_width: | 50 | if cur_width > extra_width: |
| 51 | extra_width = cur_width | 51 | extra_width = cur_width |
| 52 | 52 | ||
| 53 | width += extra_width | 53 | width += extra_width |
| 54 | 54 | ||
| 55 | self.origin = (extra_width + GraphFormat.WIDTH_PAD / 2.0, height - GraphFormat.HEIGHT_PAD / 2.0) | 55 | self.origin = (extra_width + GraphFormat.WIDTH_PAD / 2.0, height - GraphFormat.HEIGHT_PAD / 2.0) |
| 56 | 56 | ||
| 57 | self.width = width | 57 | self.width = width |
| 58 | self.height = height | 58 | self.height = height |
| 59 | 59 | ||
| 60 | #if surface.ctx is None: | 60 | #if surface.ctx is None: |
| 61 | # surface.renew(width, height) | 61 | # surface.renew(width, height) |
| 62 | 62 | ||
| 63 | self.canvas = CanvasType(width, height, item_clist, bar_plist, surface) | 63 | self.canvas = CanvasType(width, height, item_clist, bar_plist, surface) |
| 64 | 64 | ||
| 65 | def get_selected_regions(self, real_x, real_y, width, height): | 65 | def get_selected_regions(self, real_x, real_y, width, height): |
| 66 | return self.canvas.get_selected_regions(real_x, real_y, width, height) | 66 | return self.canvas.get_selected_regions(real_x, real_y, width, height) |
| 67 | 67 | ||
| 68 | def get_width(self): | 68 | def get_width(self): |
| 69 | return self.width | 69 | return self.width |
| 70 | 70 | ||
| 71 | def get_height(self): | 71 | def get_height(self): |
| 72 | return self.height | 72 | return self.height |
| 73 | 73 | ||
| 74 | def get_origin(self): | 74 | def get_origin(self): |
| 75 | return self.origin | 75 | return self.origin |
| 76 | 76 | ||
| 77 | def get_attrs(self): | 77 | def get_attrs(self): |
| 78 | return self.attrs | 78 | return self.attrs |
| 79 | 79 | ||
| 80 | def add_sel_region(self, region): | 80 | def add_sel_region(self, region): |
| 81 | self.canvas.add_sel_region(region) | 81 | self.canvas.add_sel_region(region) |
| 82 | 82 | ||
| 83 | def get_sel_region(self, event): | 83 | def get_sel_region(self, event): |
| 84 | return self.canvas.get_sel_region(event) | 84 | return self.canvas.get_sel_region(event) |
| 85 | 85 | ||
| 86 | def has_sel_region(self, event): | 86 | def has_sel_region(self, event): |
| 87 | return self.canvas.has_sel_region(event) | 87 | return self.canvas.has_sel_region(event) |
| 88 | 88 | ||
| 89 | def update_view(self, x, y, width, height, scale, ctx): | 89 | def update_view(self, x, y, width, height, scale, ctx): |
| 90 | """Proxy into the surface's pan.""" | 90 | """Proxy into the surface's pan.""" |
| 91 | self.canvas.surface.pan(x, y, width, height) | 91 | self.canvas.surface.pan(x, y, width, height) |
| 92 | self.canvas.set_scale(scale) | 92 | self.canvas.set_scale(scale) |
| 93 | self.canvas.surface.change_ctx(ctx) | 93 | self.canvas.surface.change_ctx(ctx) |
| 94 | 94 | ||
| 95 | def _recomp_min_max(self, start_time, end_time, start_item, end_item): | 95 | def _recomp_min_max(self, start_time, end_time, start_item, end_item): |
| 96 | if self.min_time is None or start_time < self.min_time: | 96 | if self.min_time is None or start_time < self.min_time: |
| 97 | self.min_time = start_time | 97 | self.min_time = start_time |
| @@ -101,93 +101,93 @@ class Graph(object): | |||
| 101 | self.min_item = start_item | 101 | self.min_item = start_item |
| 102 | if self.max_item is None or end_item > self.max_item: | 102 | if self.max_item is None or end_item > self.max_item: |
| 103 | self.max_item = end_item | 103 | self.max_item = end_item |
| 104 | 104 | ||
| 105 | def _get_time_xpos(self, time): | 105 | def get_time_xpos(self, time): |
| 106 | """get x so that x is at instant ``time'' on the graph""" | 106 | """get x so that x is at instant ``time'' on the graph""" |
| 107 | return self.origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + 1.0 * (time - self.start_time) / self.attrs.time_per_maj * self.attrs.maj_sep | 107 | return self.origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + 1.0 * (time - self.start_time) / self.attrs.time_per_maj * self.attrs.maj_sep |
| 108 | 108 | ||
| 109 | def _get_item_ypos(self, item_no): | 109 | def get_item_ypos(self, item_no): |
| 110 | """get y so that y is where the top of a bar would be in item #n's area""" | 110 | """get y so that y is where the top of a bar would be in item #n's area""" |
| 111 | return self.origin[1] - self._get_y_axis_height() + self.attrs.y_item_size * (item_no + 0.5 - GraphFormat.BAR_SIZE_FACTOR / 2.0) | 111 | return self.origin[1] - self._get_y_axis_height() + self.attrs.y_item_size * (item_no + 0.5 - GraphFormat.BAR_SIZE_FACTOR / 2.0) |
| 112 | 112 | ||
| 113 | def _get_bar_width(self, start_time, end_time): | 113 | def _get_bar_width(self, start_time, end_time): |
| 114 | return 1.0 * (end_time - start_time) / self.attrs.time_per_maj * self.attrs.maj_sep | 114 | return 1.0 * (end_time - start_time) / self.attrs.time_per_maj * self.attrs.maj_sep |
| 115 | 115 | ||
| 116 | def _get_bar_height(self): | 116 | def _get_bar_height(self): |
| 117 | return self.attrs.y_item_size * GraphFormat.BAR_SIZE_FACTOR | 117 | return self.attrs.y_item_size * GraphFormat.BAR_SIZE_FACTOR |
| 118 | 118 | ||
| 119 | def _get_mini_bar_height(self): | 119 | def _get_mini_bar_height(self): |
| 120 | return self.attrs.y_item_size * GraphFormat.MINI_BAR_SIZE_FACTOR | 120 | return self.attrs.y_item_size * GraphFormat.MINI_BAR_SIZE_FACTOR |
| 121 | 121 | ||
| 122 | def _get_mini_bar_ofs(self): | 122 | def _get_mini_bar_ofs(self): |
| 123 | return self.attrs.y_item_size * (GraphFormat.MINI_BAR_SIZE_FACTOR + GraphFormat.BAR_MINI_BAR_GAP_FACTOR) | 123 | return self.attrs.y_item_size * (GraphFormat.MINI_BAR_SIZE_FACTOR + GraphFormat.BAR_MINI_BAR_GAP_FACTOR) |
| 124 | 124 | ||
| 125 | def _get_y_axis_height(self): | 125 | def _get_y_axis_height(self): |
| 126 | return (len(self.y_item_list) + 1) * self.attrs.y_item_size | 126 | return (len(self.y_item_list) + 1) * self.attrs.y_item_size |
| 127 | 127 | ||
| 128 | def _get_bottom_tick(self, time): | 128 | def _get_bottom_tick(self, time): |
| 129 | return int(math.floor((time - self.start_time) / self.attrs.time_per_maj)) | 129 | return int(math.floor((time - self.start_time) / self.attrs.time_per_maj)) |
| 130 | 130 | ||
| 131 | def _get_top_tick(self, time): | 131 | def _get_top_tick(self, time): |
| 132 | return int(math.ceil((time - self.start_time) / self.attrs.time_per_maj)) | 132 | return int(math.ceil((time - self.start_time) / self.attrs.time_per_maj)) |
| 133 | 133 | ||
| 134 | def get_surface(self): | 134 | def get_surface(self): |
| 135 | """Gets the underlying surface.""" | 135 | """Gets the underlying surface.""" |
| 136 | return self.canvas.get_surface() | 136 | return self.canvas.get_surface() |
| 137 | 137 | ||
| 138 | def xcoor_to_time(self, x): | 138 | def xcoor_to_time(self, x): |
| 139 | #x = self.origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + (time - self.start) / self.attrs.time_per_maj * self.attrs.maj_sep | 139 | #x = self.origin[0] + GraphFormat.X_AXIS_MEASURE_OFS + (time - self.start) / self.attrs.time_per_maj * self.attrs.maj_sep |
| 140 | return (x - self.origin[0] - GraphFormat.X_AXIS_MEASURE_OFS) / self.attrs.maj_sep \ | 140 | return (x - self.origin[0] - GraphFormat.X_AXIS_MEASURE_OFS) / self.attrs.maj_sep \ |
| 141 | * self.attrs.time_per_maj + self.start_time | 141 | * self.attrs.time_per_maj + self.start_time |
| 142 | 142 | ||
| 143 | def ycoor_to_item_no(self, y): | 143 | def ycoor_to_item_no(self, y): |
| 144 | return int((y - self.origin[1] + self._get_y_axis_height()) // self.attrs.y_item_size) | 144 | return int((y - self.origin[1] + self._get_y_axis_height()) // self.attrs.y_item_size) |
| 145 | 145 | ||
| 146 | def get_offset_params(self, real_x, real_y, width, height): | 146 | def get_offset_params(self, real_x, real_y, width, height): |
| 147 | x_start, y_start = self.canvas.surface.get_virt_coor_unscaled(real_x, real_y) | 147 | x_start, y_start = self.canvas.surface.get_virt_coor_unscaled(real_x, real_y) |
| 148 | x_end, y_end = self.canvas.surface.get_virt_coor_unscaled(real_x + width, real_y + height) | 148 | x_end, y_end = self.canvas.surface.get_virt_coor_unscaled(real_x + width, real_y + height) |
| 149 | 149 | ||
| 150 | start_time = self.xcoor_to_time(x_start) | 150 | start_time = self.xcoor_to_time(x_start) |
| 151 | end_time = self.xcoor_to_time(x_end) | 151 | end_time = self.xcoor_to_time(x_end) |
| 152 | 152 | ||
| 153 | start_item = self.ycoor_to_item_no(y_start) | 153 | start_item = self.ycoor_to_item_no(y_start) |
| 154 | end_item = 1 + self.ycoor_to_item_no(y_end) | 154 | end_item = 1 + self.ycoor_to_item_no(y_end) |
| 155 | 155 | ||
| 156 | return (start_time, end_time, start_item, end_item) | 156 | return (start_time, end_time, start_item, end_item) |
| 157 | 157 | ||
| 158 | def draw_skeleton(self, start_time, end_time, start_item, end_item): | 158 | def draw_skeleton(self, start_time, end_time, start_item, end_item): |
| 159 | self.draw_grid_at_time(start_time, end_time, start_item, end_item) | 159 | self.draw_grid_at_time(start_time, end_time, start_item, end_item) |
| 160 | self.draw_x_axis_with_labels_at_time(start_time, end_time) | 160 | self.draw_x_axis_with_labels_at_time(start_time, end_time) |
| 161 | self.draw_y_axis_with_labels() | 161 | self.draw_y_axis_with_labels() |
| 162 | 162 | ||
| 163 | def render_surface(self, sched, regions, selectable=False): | 163 | def render_surface(self, sched, regions, selectable=False): |
| 164 | raise NotImplementedError | 164 | raise NotImplementedError |
| 165 | 165 | ||
| 166 | def render_all(self, schedule): | 166 | def render_all(self, schedule): |
| 167 | raise NotImplementedError | 167 | raise NotImplementedError |
| 168 | 168 | ||
| 169 | def get_events_to_render(self, sched, regions, selectable=False): | 169 | def get_events_to_render(self, sched, regions, selectable=False): |
| 170 | raise NotImplementedError | 170 | raise NotImplementedError |
| 171 | 171 | ||
| 172 | def render_surface(self, sched, regions, selectable=False): | 172 | def render_surface(self, sched, regions, selectable=False): |
| 173 | if not selectable: | 173 | if not selectable: |
| 174 | self.canvas.whiteout() | 174 | self.canvas.whiteout() |
| 175 | else: | 175 | else: |
| 176 | self.canvas.clear_selectable_regions() | 176 | self.canvas.clear_selectable_regions() |
| 177 | self.render_events(self.get_events_to_render(sched, regions, selectable), selectable) | 177 | self.render_events(self.get_events_to_render(sched, regions, selectable), selectable) |
| 178 | 178 | ||
| 179 | def render_events(self, events, selectable=False): | 179 | def render_events(self, events, selectable=False): |
| 180 | for layer in Canvas.LAYERS: | 180 | for layer in Canvas.LAYERS: |
| 181 | prev_events = {} | 181 | prev_events = {} |
| 182 | if layer in events: | 182 | if layer in events: |
| 183 | for event in events[layer]: | 183 | for event in events[layer]: |
| 184 | event.render(self, layer, prev_events, selectable) | 184 | event.render(self, layer, prev_events, selectable) |
| 185 | 185 | ||
| 186 | def draw_axes(self, x_axis_label, y_axis_label): | 186 | def draw_axes(self, x_axis_label, y_axis_label): |
| 187 | """Draws and labels the axes according to the parameters that we were initialized | 187 | """Draws and labels the axes according to the parameters that we were initialized |
| 188 | with.""" | 188 | with.""" |
| 189 | self.draw_grid_at_time(self.start_time, self.end_time, 0, len(self.attrs.y_item_list) - 1) | 189 | self.draw_grid_at_time(self.start_time, self.end_time, 0, len(self.attrs.y_item_list) - 1) |
| 190 | 190 | ||
| 191 | self.canvas.draw_x_axis(self.origin[0], self.origin[1], self.num_maj, self.attrs.maj_sep, self.attrs.min_per_maj) | 191 | self.canvas.draw_x_axis(self.origin[0], self.origin[1], self.num_maj, self.attrs.maj_sep, self.attrs.min_per_maj) |
| 192 | self.canvas.draw_y_axis(self.origin[0], self.origin[1], self._get_y_axis_height()) | 192 | self.canvas.draw_y_axis(self.origin[0], self.origin[1], self._get_y_axis_height()) |
| 193 | self.canvas.draw_x_axis_labels(self.origin[0], self.origin[1], 0, self.num_maj - 1,\ | 193 | self.canvas.draw_x_axis_labels(self.origin[0], self.origin[1], 0, self.num_maj - 1,\ |
| @@ -195,425 +195,425 @@ class Graph(object): | |||
| 195 | self.attrs.time_per_maj, self.attrs.show_min, self.attrs.majfopts, self.attrs.minfopts) | 195 | self.attrs.time_per_maj, self.attrs.show_min, self.attrs.majfopts, self.attrs.minfopts) |
| 196 | self.canvas.draw_y_axis_labels(self.origin[0], self.origin[1], self._get_y_axis_height(), self.y_item_list, \ | 196 | self.canvas.draw_y_axis_labels(self.origin[0], self.origin[1], self._get_y_axis_height(), self.y_item_list, \ |
| 197 | self.attrs.y_item_size, self.attrs.item_fopts) | 197 | self.attrs.y_item_size, self.attrs.item_fopts) |
| 198 | 198 | ||
| 199 | def draw_grid_at_time(self, start_time, end_time, start_item, end_item): | 199 | def draw_grid_at_time(self, start_time, end_time, start_item, end_item): |
| 200 | """Draws the grid, but only in a certain time and item range.""" | 200 | """Draws the grid, but only in a certain time and item range.""" |
| 201 | start_tick = max(0, self._get_bottom_tick(start_time)) | 201 | start_tick = max(0, self._get_bottom_tick(start_time)) |
| 202 | end_tick = min(self.num_maj - 1, self._get_top_tick(end_time)) | 202 | end_tick = min(self.num_maj - 1, self._get_top_tick(end_time)) |
| 203 | 203 | ||
| 204 | start_item = max(0, start_item) | 204 | start_item = max(0, start_item) |
| 205 | end_item = min(len(self.y_item_list), end_item) | 205 | end_item = min(len(self.y_item_list), end_item) |
| 206 | 206 | ||
| 207 | self.canvas.draw_grid(self.origin[0], self.origin[1], self._get_y_axis_height(), | 207 | self.canvas.draw_grid(self.origin[0], self.origin[1], self._get_y_axis_height(), |
| 208 | start_tick, end_tick, start_item, end_item, self.attrs.maj_sep, self.attrs.y_item_size, \ | 208 | start_tick, end_tick, start_item, end_item, self.attrs.maj_sep, self.attrs.y_item_size, \ |
| 209 | self.attrs.min_per_maj, True) | 209 | self.attrs.min_per_maj, True) |
| 210 | 210 | ||
| 211 | def draw_x_axis_with_labels_at_time(self, start_time, end_time): | 211 | def draw_x_axis_with_labels_at_time(self, start_time, end_time): |
| 212 | start_tick = max(0, self._get_bottom_tick(start_time)) | 212 | start_tick = max(0, self._get_bottom_tick(start_time)) |
| 213 | end_tick = min(self.num_maj - 1, self._get_top_tick(end_time)) | 213 | end_tick = min(self.num_maj - 1, self._get_top_tick(end_time)) |
| 214 | 214 | ||
| 215 | self.canvas.draw_x_axis(self.origin[0], self.origin[1], start_tick, end_tick, \ | 215 | self.canvas.draw_x_axis(self.origin[0], self.origin[1], start_tick, end_tick, \ |
| 216 | self.attrs.maj_sep, self.attrs.min_per_maj) | 216 | self.attrs.maj_sep, self.attrs.min_per_maj) |
| 217 | self.canvas.draw_x_axis_labels(self.origin[0], self.origin[1], start_tick, \ | 217 | self.canvas.draw_x_axis_labels(self.origin[0], self.origin[1], start_tick, \ |
| 218 | end_tick, self.attrs.maj_sep, self.attrs.min_per_maj, | 218 | end_tick, self.attrs.maj_sep, self.attrs.min_per_maj, |
| 219 | self.start_time + start_tick * self.attrs.time_per_maj, | 219 | self.start_time + start_tick * self.attrs.time_per_maj, |
| 220 | self.attrs.time_per_maj, False) | 220 | self.attrs.time_per_maj, False) |
| 221 | 221 | ||
| 222 | def draw_y_axis_with_labels(self): | 222 | def draw_y_axis_with_labels(self): |
| 223 | self.canvas.draw_y_axis(self.origin[0], self.origin[1], self._get_y_axis_height()) | 223 | self.canvas.draw_y_axis(self.origin[0], self.origin[1], self._get_y_axis_height()) |
| 224 | self.canvas.draw_y_axis_labels(self.origin[0], self.origin[1], self._get_y_axis_height(), \ | 224 | self.canvas.draw_y_axis_labels(self.origin[0], self.origin[1], self._get_y_axis_height(), \ |
| 225 | self.y_item_list, self.attrs.y_item_size) | 225 | self.y_item_list, self.attrs.y_item_size) |
| 226 | 226 | ||
| 227 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): | 227 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): |
| 228 | """Draws a suspension symbol for a dcertain task at an instant in time.""" | 228 | """Draws a suspension symbol for a dcertain task at an instant in time.""" |
| 229 | raise NotImplementedError | 229 | raise NotImplementedError |
| 230 | 230 | ||
| 231 | def add_sel_suspend_triangle_at_time(self, time, task_no, cpu_no, event): | 231 | def add_sel_suspend_triangle_at_time(self, time, task_no, cpu_no, event): |
| 232 | """Same as above, except instead of drawing adds a selectable region at | 232 | """Same as above, except instead of drawing adds a selectable region at |
| 233 | a certain time.""" | 233 | a certain time.""" |
| 234 | raise NotImplementedError | 234 | raise NotImplementedError |
| 235 | 235 | ||
| 236 | def draw_resume_triangle_at_time(self, time, task_no, cpu_no, selected=False): | 236 | def draw_resume_triangle_at_time(self, time, task_no, cpu_no, selected=False): |
| 237 | """Draws a resumption symbol for a certain task at an instant in time.""" | 237 | """Draws a resumption symbol for a certain task at an instant in time.""" |
| 238 | raise NotImplementedError | 238 | raise NotImplementedError |
| 239 | 239 | ||
| 240 | def add_sel_resume_triangle_at_time(self, time, task_no, cpu_no, event): | 240 | def add_sel_resume_triangle_at_time(self, time, task_no, cpu_no, event): |
| 241 | """Same as above, except instead of drawing adds a selectable region at | 241 | """Same as above, except instead of drawing adds a selectable region at |
| 242 | a certain time.""" | 242 | a certain time.""" |
| 243 | raise NotImplementedError | 243 | raise NotImplementedError |
| 244 | 244 | ||
| 245 | def draw_completion_marker_at_time(self, time, task_no, cpu_no, selected=False): | 245 | def draw_completion_marker_at_time(self, time, task_no, cpu_no, selected=False): |
| 246 | """Draws a completion marker for a certain task at an instant in time.""" | 246 | """Draws a completion marker for a certain task at an instant in time.""" |
| 247 | raise NotImplementedError | 247 | raise NotImplementedError |
| 248 | 248 | ||
| 249 | def add_sel_completion_marker_at_time(self, time, task_no, cpu_no, event): | 249 | def add_sel_completion_marker_at_time(self, time, task_no, cpu_no, event): |
| 250 | """Same as above, except instead of drawing adds a selectable region at | 250 | """Same as above, except instead of drawing adds a selectable region at |
| 251 | a certain time.""" | 251 | a certain time.""" |
| 252 | raise NotImplementedError | 252 | raise NotImplementedError |
| 253 | 253 | ||
| 254 | def draw_release_arrow_at_time(self, time, task_no, job_no, selected=False): | 254 | def draw_release_arrow_at_time(self, time, task_no, job_no, selected=False): |
| 255 | """Draws a release arrow at a certain time for some task and job""" | 255 | """Draws a release arrow at a certain time for some task and job""" |
| 256 | raise NotImplementedError | 256 | raise NotImplementedError |
| 257 | 257 | ||
| 258 | def add_sel_release_arrow_at_time(self, time, task_no, event): | 258 | def add_sel_release_arrow_at_time(self, time, task_no, event): |
| 259 | """Same as above, except instead of drawing adds a selectable region at | 259 | """Same as above, except instead of drawing adds a selectable region at |
| 260 | a certain time.""" | 260 | a certain time.""" |
| 261 | raise NotImplementedError | 261 | raise NotImplementedError |
| 262 | 262 | ||
| 263 | def draw_deadline_arrow_at_time(self, time, task_no, job_no, selected=False): | 263 | def draw_deadline_arrow_at_time(self, time, task_no, job_no, selected=False): |
| 264 | """Draws a deadline arrow at a certain time for some task and job""" | 264 | """Draws a deadline arrow at a certain time for some task and job""" |
| 265 | raise NotImplementedError | 265 | raise NotImplementedError |
| 266 | 266 | ||
| 267 | def add_sel_deadline_arrow_at_time(self, time, task_no, event): | 267 | def add_sel_deadline_arrow_at_time(self, time, task_no, event): |
| 268 | """Same as above, except instead of drawing adds a selectable region at | 268 | """Same as above, except instead of drawing adds a selectable region at |
| 269 | a certain time.""" | 269 | a certain time.""" |
| 270 | raise NotImplementedError | 270 | raise NotImplementedError |
| 271 | 271 | ||
| 272 | def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None): | 272 | def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None): |
| 273 | """Draws a bar over a certain time period for some task, optionally labelling it.""" | 273 | """Draws a bar over a certain time period for some task, optionally labelling it.""" |
| 274 | raise NotImplementedError | 274 | raise NotImplementedError |
| 275 | 275 | ||
| 276 | def add_sel_bar_at_time(self, start_time, end_time, task_no, cpu_no, event): | 276 | def add_sel_bar_at_time(self, start_time, end_time, task_no, cpu_no, event): |
| 277 | """Same as above, except instead of drawing adds a selectable region at | 277 | """Same as above, except instead of drawing adds a selectable region at |
| 278 | a certain time.""" | 278 | a certain time.""" |
| 279 | raise NotImplementedError | 279 | raise NotImplementedError |
| 280 | 280 | ||
| 281 | def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, clip_side=None, job_no=None): | 281 | def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, clip_side=None, job_no=None): |
| 282 | """Draws a mini bar over a certain time period for some task, optionally labelling it.""" | 282 | """Draws a mini bar over a certain time period for some task, optionally labelling it.""" |
| 283 | raise NotImplementedError | 283 | raise NotImplementedError |
| 284 | 284 | ||
| 285 | def add_sel_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, event): | 285 | def add_sel_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, event): |
| 286 | """Same as above, except instead of drawing adds a selectable region at | 286 | """Same as above, except instead of drawing adds a selectable region at |
| 287 | a certain time.""" | 287 | a certain time.""" |
| 288 | raise NotImplementedError | 288 | raise NotImplementedError |
| 289 | 289 | ||
| 290 | class TaskGraph(Graph): | 290 | class TaskGraph(Graph): |
| 291 | def get_events_to_render(self, sched, regions, selectable=False): | 291 | def get_events_to_render(self, sched, regions, selectable=False): |
| 292 | slots = {} | 292 | slots = {} |
| 293 | 293 | ||
| 294 | self.min_time, self.max_time, self.min_item, self.max_item = None, None, None, None | 294 | self.min_time, self.max_time, self.min_item, self.max_item = None, None, None, None |
| 295 | for region in regions: | 295 | for region in regions: |
| 296 | x, y, width, height = region | 296 | x, y, width, height = region |
| 297 | start_time, end_time, start_item, end_item = self.get_offset_params(x, y, width, height) | 297 | start_time, end_time, start_item, end_item = self.get_offset_params(x, y, width, height) |
| 298 | self._recomp_min_max(start_time, end_time, start_item, end_item) | 298 | self._recomp_min_max(start_time, end_time, start_item, end_item) |
| 299 | 299 | ||
| 300 | sched.get_time_slot_array().get_slots(slots, | 300 | sched.get_time_slot_array().get_slots(slots, |
| 301 | start_time, end_time, start_item, end_item, | 301 | start_time, end_time, start_item, end_item, |
| 302 | schedule.TimeSlotArray.TASK_LIST) | 302 | schedule.TimeSlotArray.TASK_LIST) |
| 303 | 303 | ||
| 304 | 304 | ||
| 305 | if not selectable: | 305 | if not selectable: |
| 306 | self.draw_skeleton(self.min_time, self.max_time, | 306 | self.draw_skeleton(self.min_time, self.max_time, |
| 307 | self.min_item, self.max_item) | 307 | self.min_item, self.max_item) |
| 308 | 308 | ||
| 309 | events_to_render = {} | 309 | events_to_render = {} |
| 310 | for layer in Canvas.LAYERS: | 310 | for layer in Canvas.LAYERS: |
| 311 | events_to_render[layer] = {} | 311 | events_to_render[layer] = {} |
| 312 | 312 | ||
| 313 | for event in sched.get_time_slot_array().get_events(slots, | 313 | for event in sched.get_time_slot_array().get_events(slots, |
| 314 | schedule.TimeSlotArray.TASK_LIST, | 314 | schedule.TimeSlotArray.TASK_LIST, |
| 315 | schedule.EVENT_LIST): | 315 | schedule.EVENT_LIST): |
| 316 | events_to_render[event.get_layer()][event] = None | 316 | events_to_render[event.get_layer()][event] = None |
| 317 | 317 | ||
| 318 | return events_to_render | 318 | return events_to_render |
| 319 | 319 | ||
| 320 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): | 320 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): |
| 321 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR | 321 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR |
| 322 | x = self._get_time_xpos(time) | 322 | x = self.get_time_xpos(time) |
| 323 | y = self._get_item_ypos(task_no) + self._get_bar_height() / 2.0 - height / 2.0 | 323 | y = self.get_item_ypos(task_no) + self._get_bar_height() / 2.0 - height / 2.0 |
| 324 | self.canvas.draw_suspend_triangle(x, y, height, selected) | 324 | self.canvas.draw_suspend_triangle(x, y, height, selected) |
| 325 | 325 | ||
| 326 | def add_sel_suspend_triangle_at_time(self, time, task_no, cpu_no, event): | 326 | def add_sel_suspend_triangle_at_time(self, time, task_no, cpu_no, event): |
| 327 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR | 327 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR |
| 328 | x = self._get_time_xpos(time) | 328 | x = self.get_time_xpos(time) |
| 329 | y = self._get_item_ypos(task_no) + self._get_bar_height() / 2.0 - height / 2.0 | 329 | y = self.get_item_ypos(task_no) + self._get_bar_height() / 2.0 - height / 2.0 |
| 330 | 330 | ||
| 331 | self.canvas.add_sel_suspend_triangle(x, y, height, event) | 331 | self.canvas.add_sel_suspend_triangle(x, y, height, event) |
| 332 | 332 | ||
| 333 | def draw_resume_triangle_at_time(self, time, task_no, cpu_no, selected=False): | 333 | def draw_resume_triangle_at_time(self, time, task_no, cpu_no, selected=False): |
| 334 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR | 334 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR |
| 335 | x = self._get_time_xpos(time) | 335 | x = self.get_time_xpos(time) |
| 336 | y = self._get_item_ypos(task_no) + self._get_bar_height() / 2.0 - height / 2.0 | 336 | y = self.get_item_ypos(task_no) + self._get_bar_height() / 2.0 - height / 2.0 |
| 337 | 337 | ||
| 338 | self.canvas.draw_resume_triangle(x, y, height, selected) | 338 | self.canvas.draw_resume_triangle(x, y, height, selected) |
| 339 | 339 | ||
| 340 | def add_sel_resume_triangle_at_time(self, time, task_no, cpu_no, event): | 340 | def add_sel_resume_triangle_at_time(self, time, task_no, cpu_no, event): |
| 341 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR | 341 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR |
| 342 | x = self._get_time_xpos(time) | 342 | x = self.get_time_xpos(time) |
| 343 | y = self._get_item_ypos(task_no) + self._get_bar_height() / 2.0 - height / 2.0 | 343 | y = self.get_item_ypos(task_no) + self._get_bar_height() / 2.0 - height / 2.0 |
| 344 | 344 | ||
| 345 | self.canvas.add_sel_resume_triangle(x, y, height, event) | 345 | self.canvas.add_sel_resume_triangle(x, y, height, event) |
| 346 | 346 | ||
| 347 | def draw_completion_marker_at_time(self, time, task_no, cpu_no, selected=False): | 347 | def draw_completion_marker_at_time(self, time, task_no, cpu_no, selected=False): |
| 348 | height = self._get_bar_height() * GraphFormat.COMPLETION_MARKER_FACTOR | 348 | height = self._get_bar_height() * GraphFormat.COMPLETION_MARKER_FACTOR |
| 349 | x = self._get_time_xpos(time) | 349 | x = self.get_time_xpos(time) |
| 350 | y = self._get_item_ypos(task_no) + self._get_bar_height() - height | 350 | y = self.get_item_ypos(task_no) + self._get_bar_height() - height |
| 351 | 351 | ||
| 352 | self.canvas.draw_completion_marker(x, y, height, selected) | 352 | self.canvas.draw_completion_marker(x, y, height, selected) |
| 353 | 353 | ||
| 354 | def add_sel_completion_marker_at_time(self, time, task_no, cpu_no, event): | 354 | def add_sel_completion_marker_at_time(self, time, task_no, cpu_no, event): |
| 355 | height = self._get_bar_height() * GraphFormat.COMPLETION_MARKER_FACTOR | 355 | height = self._get_bar_height() * GraphFormat.COMPLETION_MARKER_FACTOR |
| 356 | 356 | ||
| 357 | x = self._get_time_xpos(time) | 357 | x = self.get_time_xpos(time) |
| 358 | y = self._get_item_ypos(task_no) + self._get_bar_height() - height | 358 | y = self.get_item_ypos(task_no) + self._get_bar_height() - height |
| 359 | 359 | ||
| 360 | self.canvas.add_sel_completion_marker(x, y, height, event) | 360 | self.canvas.add_sel_completion_marker(x, y, height, event) |
| 361 | 361 | ||
| 362 | def draw_release_arrow_at_time(self, time, task_no, job_no=None, selected=False): | 362 | def draw_release_arrow_at_time(self, time, task_no, job_no=None, selected=False): |
| 363 | height = self._get_bar_height() * GraphFormat.BIG_ARROW_FACTOR | 363 | height = self._get_bar_height() * GraphFormat.BIG_ARROW_FACTOR |
| 364 | 364 | ||
| 365 | x = self._get_time_xpos(time) | 365 | x = self.get_time_xpos(time) |
| 366 | y = self._get_item_ypos(task_no) + self._get_bar_height() - height | 366 | y = self.get_item_ypos(task_no) + self._get_bar_height() - height |
| 367 | 367 | ||
| 368 | self.canvas.draw_release_arrow_big(x, y, height, selected) | 368 | self.canvas.draw_release_arrow_big(x, y, height, selected) |
| 369 | 369 | ||
| 370 | def add_sel_release_arrow_at_time(self, time, task_no, event): | 370 | def add_sel_release_arrow_at_time(self, time, task_no, event): |
| 371 | height = self._get_bar_height() * GraphFormat.BIG_ARROW_FACTOR | 371 | height = self._get_bar_height() * GraphFormat.BIG_ARROW_FACTOR |
| 372 | 372 | ||
| 373 | x = self._get_time_xpos(time) | 373 | x = self.get_time_xpos(time) |
| 374 | y = self._get_item_ypos(task_no) + self._get_bar_height() - height | 374 | y = self.get_item_ypos(task_no) + self._get_bar_height() - height |
| 375 | 375 | ||
| 376 | self.canvas.add_sel_release_arrow_big(x, y, height, event) | 376 | self.canvas.add_sel_release_arrow_big(x, y, height, event) |
| 377 | 377 | ||
| 378 | def draw_deadline_arrow_at_time(self, time, task_no, job_no=None, selected=False): | 378 | def draw_deadline_arrow_at_time(self, time, task_no, job_no=None, selected=False): |
| 379 | height = self._get_bar_height() * GraphFormat.BIG_ARROW_FACTOR | 379 | height = self._get_bar_height() * GraphFormat.BIG_ARROW_FACTOR |
| 380 | 380 | ||
| 381 | x = self._get_time_xpos(time) | 381 | x = self.get_time_xpos(time) |
| 382 | y = self._get_item_ypos(task_no) | 382 | y = self.get_item_ypos(task_no) |
| 383 | 383 | ||
| 384 | self.canvas.draw_deadline_arrow_big(x, y, height, selected) | 384 | self.canvas.draw_deadline_arrow_big(x, y, height, selected) |
| 385 | 385 | ||
| 386 | def add_sel_deadline_arrow_at_time(self, time, task_no, event): | 386 | def add_sel_deadline_arrow_at_time(self, time, task_no, event): |
| 387 | height = self._get_bar_height() * GraphFormat.BIG_ARROW_FACTOR | 387 | height = self._get_bar_height() * GraphFormat.BIG_ARROW_FACTOR |
| 388 | 388 | ||
| 389 | x = self._get_time_xpos(time) | 389 | x = self.get_time_xpos(time) |
| 390 | y = self._get_item_ypos(task_no) | 390 | y = self.get_item_ypos(task_no) |
| 391 | 391 | ||
| 392 | self.canvas.add_sel_deadline_arrow_big(x, y, height, event) | 392 | self.canvas.add_sel_deadline_arrow_big(x, y, height, event) |
| 393 | 393 | ||
| 394 | def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): | 394 | def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): |
| 395 | if start_time > end_time: | 395 | if start_time > end_time: |
| 396 | raise ValueError("Litmus is not a time machine") | 396 | raise ValueError("Litmus is not a time machine") |
| 397 | 397 | ||
| 398 | x = self._get_time_xpos(start_time) | 398 | x = self.get_time_xpos(start_time) |
| 399 | y = self._get_item_ypos(task_no) | 399 | y = self.get_item_ypos(task_no) |
| 400 | width = self._get_bar_width(start_time, end_time) | 400 | width = self._get_bar_width(start_time, end_time) |
| 401 | height = self._get_bar_height() | 401 | height = self._get_bar_height() |
| 402 | 402 | ||
| 403 | self.canvas.draw_bar(x, y, width, height, cpu_no, clip_side, selected) | 403 | self.canvas.draw_bar(x, y, width, height, cpu_no, clip_side, selected) |
| 404 | 404 | ||
| 405 | # if a job number is specified, we want to draw a superscript and subscript for the task and job number, respectively | 405 | # if a job number is specified, we want to draw a superscript and subscript for the task and job number, respectively |
| 406 | if job_no is not None: | 406 | if job_no is not None: |
| 407 | x += GraphFormat.BAR_LABEL_OFS | 407 | x += GraphFormat.BAR_LABEL_OFS |
| 408 | y += self.attrs.y_item_size * GraphFormat.BAR_SIZE_FACTOR / 2.0 | 408 | y += self.attrs.y_item_size * GraphFormat.BAR_SIZE_FACTOR / 2.0 |
| 409 | self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \ | 409 | self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \ |
| 410 | GraphFormat.DEF_FOPTS_BAR, GraphFormat.DEF_FOPTS_BAR_SSCRIPT, AlignMode.LEFT, AlignMode.CENTER) | 410 | GraphFormat.DEF_FOPTS_BAR, GraphFormat.DEF_FOPTS_BAR_SSCRIPT, AlignMode.LEFT, AlignMode.CENTER) |
| 411 | 411 | ||
| 412 | def add_sel_bar_at_time(self, start_time, end_time, task_no, cpu_no, event): | 412 | def add_sel_bar_at_time(self, start_time, end_time, task_no, cpu_no, event): |
| 413 | if start_time > end_time: | 413 | if start_time > end_time: |
| 414 | raise ValueError("Litmus is not a time machine") | 414 | raise ValueError("Litmus is not a time machine") |
| 415 | 415 | ||
| 416 | x = self._get_time_xpos(start_time) | 416 | x = self.get_time_xpos(start_time) |
| 417 | y = self._get_item_ypos(task_no) | 417 | y = self.get_item_ypos(task_no) |
| 418 | width = self._get_bar_width(start_time, end_time) | 418 | width = self._get_bar_width(start_time, end_time) |
| 419 | height = self._get_bar_height() | 419 | height = self._get_bar_height() |
| 420 | 420 | ||
| 421 | self.canvas.add_sel_bar(x, y, width, height, event) | 421 | self.canvas.add_sel_bar(x, y, width, height, event) |
| 422 | 422 | ||
| 423 | def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): | 423 | def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): |
| 424 | if start_time > end_time: | 424 | if start_time > end_time: |
| 425 | raise ValueError("Litmus is not a time machine") | 425 | raise ValueError("Litmus is not a time machine") |
| 426 | 426 | ||
| 427 | x = self._get_time_xpos(start_time) | 427 | x = self.get_time_xpos(start_time) |
| 428 | y = self._get_item_ypos(task_no) - self._get_mini_bar_ofs() | 428 | y = self.get_item_ypos(task_no) - self._get_mini_bar_ofs() |
| 429 | width = self._get_bar_width(start_time, end_time) | 429 | width = self._get_bar_width(start_time, end_time) |
| 430 | height = self._get_mini_bar_height() | 430 | height = self._get_mini_bar_height() |
| 431 | 431 | ||
| 432 | self.canvas.draw_mini_bar(x, y, width, height, Canvas.NULL_PATTERN, clip_side, selected) | 432 | self.canvas.draw_mini_bar(x, y, width, height, Canvas.NULL_PATTERN, clip_side, selected) |
| 433 | 433 | ||
| 434 | if job_no is not None: | 434 | if job_no is not None: |
| 435 | x += GraphFormat.MINI_BAR_LABEL_OFS | 435 | x += GraphFormat.MINI_BAR_LABEL_OFS |
| 436 | y += self.attrs.y_item_size * GraphFormat.MINI_BAR_SIZE_FACTOR / 2.0 | 436 | y += self.attrs.y_item_size * GraphFormat.MINI_BAR_SIZE_FACTOR / 2.0 |
| 437 | self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \ | 437 | self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \ |
| 438 | GraphFormat.DEF_FOPTS_MINI_BAR, GraphFormat.DEF_FOPTS_MINI_BAR_SSCRIPT, AlignMode.LEFT, AlignMode.CENTER) | 438 | GraphFormat.DEF_FOPTS_MINI_BAR, GraphFormat.DEF_FOPTS_MINI_BAR_SSCRIPT, AlignMode.LEFT, AlignMode.CENTER) |
| 439 | 439 | ||
| 440 | def add_sel_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, event): | 440 | def add_sel_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, event): |
| 441 | x = self._get_time_xpos(start_time) | 441 | x = self.get_time_xpos(start_time) |
| 442 | y = self._get_item_ypos(task_no) - self._get_mini_bar_ofs() | 442 | y = self.get_item_ypos(task_no) - self._get_mini_bar_ofs() |
| 443 | width = self._get_bar_width(start_time, end_time) | 443 | width = self._get_bar_width(start_time, end_time) |
| 444 | height = self._get_mini_bar_height() | 444 | height = self._get_mini_bar_height() |
| 445 | 445 | ||
| 446 | self.canvas.add_sel_mini_bar(x, y, width, height, event) | 446 | self.canvas.add_sel_mini_bar(x, y, width, height, event) |
| 447 | 447 | ||
| 448 | class CpuGraph(Graph): | 448 | class CpuGraph(Graph): |
| 449 | def get_events_to_render(self, sched, regions, selectable=False): | 449 | def get_events_to_render(self, sched, regions, selectable=False): |
| 450 | BOTTOM_EVENTS = [schedule.ReleaseEvent, schedule.DeadlineEvent, schedule.InversionStartEvent, | 450 | BOTTOM_EVENTS = [schedule.ReleaseEvent, schedule.DeadlineEvent, schedule.InversionStartEvent, |
| 451 | schedule.InversionEndEvent, schedule.InversionDummy] | 451 | schedule.InversionEndEvent, schedule.InversionDummy] |
| 452 | TOP_EVENTS = [schedule.SuspendEvent, schedule.ResumeEvent, schedule.CompleteEvent, | 452 | TOP_EVENTS = [schedule.SuspendEvent, schedule.ResumeEvent, schedule.CompleteEvent, |
| 453 | schedule.SwitchAwayEvent, schedule.SwitchToEvent, schedule.IsRunningDummy] | 453 | schedule.SwitchAwayEvent, schedule.SwitchToEvent, schedule.IsRunningDummy] |
| 454 | 454 | ||
| 455 | if not regions: | 455 | if not regions: |
| 456 | return {} | 456 | return {} |
| 457 | 457 | ||
| 458 | top_slots = {} | 458 | top_slots = {} |
| 459 | bottom_slots = {} | 459 | bottom_slots = {} |
| 460 | 460 | ||
| 461 | self.min_time, self.max_time, self.min_item, self.max_item = None, None, None, None | 461 | self.min_time, self.max_time, self.min_item, self.max_item = None, None, None, None |
| 462 | for region in regions: | 462 | for region in regions: |
| 463 | x, y, width, height = region | 463 | x, y, width, height = region |
| 464 | start_time, end_time, start_item, end_item = self.get_offset_params(x, y, width, height) | 464 | start_time, end_time, start_item, end_item = self.get_offset_params(x, y, width, height) |
| 465 | self._recomp_min_max(start_time, end_time, start_item, end_item) | 465 | self._recomp_min_max(start_time, end_time, start_item, end_item) |
| 466 | 466 | ||
| 467 | sched.get_time_slot_array().get_slots(top_slots, | 467 | sched.get_time_slot_array().get_slots(top_slots, |
| 468 | start_time, end_time, start_item, end_item, | 468 | start_time, end_time, start_item, end_item, |
| 469 | schedule.TimeSlotArray.CPU_LIST) | 469 | schedule.TimeSlotArray.CPU_LIST) |
| 470 | 470 | ||
| 471 | if end_item >= len(self.y_item_list): | 471 | if end_item >= len(self.y_item_list): |
| 472 | # we are far down enough that we should render the releases and deadlines and inversions, | 472 | # we are far down enough that we should render the releases and deadlines and inversions, |
| 473 | # which appear near the x-axis | 473 | # which appear near the x-axis |
| 474 | sched.get_time_slot_array().get_slots(bottom_slots, | 474 | sched.get_time_slot_array().get_slots(bottom_slots, |
| 475 | start_time, end_time, 0, sched.get_num_cpus(), | 475 | start_time, end_time, 0, sched.get_num_cpus(), |
| 476 | schedule.TimeSlotArray.CPU_LIST) | 476 | schedule.TimeSlotArray.CPU_LIST) |
| 477 | 477 | ||
| 478 | if not selectable: | 478 | if not selectable: |
| 479 | self.draw_skeleton(self.min_time, self.max_time, | 479 | self.draw_skeleton(self.min_time, self.max_time, |
| 480 | self.min_item, self.max_item) | 480 | self.min_item, self.max_item) |
| 481 | 481 | ||
| 482 | events_to_render = {} | 482 | events_to_render = {} |
| 483 | for layer in Canvas.LAYERS: | 483 | for layer in Canvas.LAYERS: |
| 484 | events_to_render[layer] = {} | 484 | events_to_render[layer] = {} |
| 485 | 485 | ||
| 486 | for event in sched.get_time_slot_array().get_events(top_slots, | 486 | for event in sched.get_time_slot_array().get_events(top_slots, |
| 487 | schedule.TimeSlotArray.CPU_LIST, | 487 | schedule.TimeSlotArray.CPU_LIST, |
| 488 | TOP_EVENTS): | 488 | TOP_EVENTS): |
| 489 | events_to_render[event.get_layer()][event] = None | 489 | events_to_render[event.get_layer()][event] = None |
| 490 | for event in sched.get_time_slot_array().get_events(bottom_slots, | 490 | for event in sched.get_time_slot_array().get_events(bottom_slots, |
| 491 | schedule.TimeSlotArray.CPU_LIST, | 491 | schedule.TimeSlotArray.CPU_LIST, |
| 492 | BOTTOM_EVENTS): | 492 | BOTTOM_EVENTS): |
| 493 | events_to_render[event.get_layer()][event] = None | 493 | events_to_render[event.get_layer()][event] = None |
| 494 | 494 | ||
| 495 | return events_to_render | 495 | return events_to_render |
| 496 | 496 | ||
| 497 | def render(self, schedule, start_time=None, end_time=None): | 497 | def render(self, schedule, start_time=None, end_time=None): |
| 498 | if end_time < start_time: | 498 | if end_time < start_time: |
| 499 | raise ValueError('start must be less than end') | 499 | raise ValueError('start must be less than end') |
| 500 | 500 | ||
| 501 | if start_time is None: | 501 | if start_time is None: |
| 502 | start_time = self.start | 502 | start_time = self.start |
| 503 | if end_time is None: | 503 | if end_time is None: |
| 504 | end_time = self.end | 504 | end_time = self.end |
| 505 | start_slot = self.get_time_slot(start_time) | 505 | start_slot = self.get_time_slot(start_time) |
| 506 | end_slot = min(len(self.time_slots), self.get_time_slot(end_time) + 1) | 506 | end_slot = min(len(self.time_slots), self.get_time_slot(end_time) + 1) |
| 507 | 507 | ||
| 508 | for layer in Canvas.LAYERS: | 508 | for layer in Canvas.LAYERS: |
| 509 | prev_events = {} | 509 | prev_events = {} |
| 510 | for i in range(start_slot, end_slot): | 510 | for i in range(start_slot, end_slot): |
| 511 | for event in self.time_slots[i]: | 511 | for event in self.time_slots[i]: |
| 512 | event.render(graph, layer, prev_events) | 512 | event.render(graph, layer, prev_events) |
| 513 | 513 | ||
| 514 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): | 514 | def draw_suspend_triangle_at_time(self, time, task_no, cpu_no, selected=False): |
| 515 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR | 515 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR |
| 516 | x = self._get_time_xpos(time) | 516 | x = self.get_time_xpos(time) |
| 517 | y = self._get_item_ypos(cpu_no) + self._get_bar_height() / 2.0 - height / 2.0 | 517 | y = self.get_item_ypos(cpu_no) + self._get_bar_height() / 2.0 - height / 2.0 |
| 518 | self.canvas.draw_suspend_triangle(x, y, height, selected) | 518 | self.canvas.draw_suspend_triangle(x, y, height, selected) |
| 519 | 519 | ||
| 520 | def add_sel_suspend_triangle_at_time(self, time, task_no, cpu_no, event): | 520 | def add_sel_suspend_triangle_at_time(self, time, task_no, cpu_no, event): |
| 521 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR | 521 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR |
| 522 | x = self._get_time_xpos(time) | 522 | x = self.get_time_xpos(time) |
| 523 | y = self._get_item_ypos(cpu_no) + self._get_bar_height() / 2.0 - height / 2.0 | 523 | y = self.get_item_ypos(cpu_no) + self._get_bar_height() / 2.0 - height / 2.0 |
| 524 | 524 | ||
| 525 | self.canvas.add_sel_suspend_triangle(x, y, height, event) | 525 | self.canvas.add_sel_suspend_triangle(x, y, height, event) |
| 526 | 526 | ||
| 527 | def draw_resume_triangle_at_time(self, time, task_no, cpu_no, selected=False): | 527 | def draw_resume_triangle_at_time(self, time, task_no, cpu_no, selected=False): |
| 528 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR | 528 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR |
| 529 | x = self._get_time_xpos(time) | 529 | x = self.get_time_xpos(time) |
| 530 | y = self._get_item_ypos(cpu_no) + self._get_bar_height() / 2.0 - height / 2.0 | 530 | y = self.get_item_ypos(cpu_no) + self._get_bar_height() / 2.0 - height / 2.0 |
| 531 | 531 | ||
| 532 | self.canvas.draw_resume_triangle(x, y, height, selected) | 532 | self.canvas.draw_resume_triangle(x, y, height, selected) |
| 533 | 533 | ||
| 534 | def add_sel_resume_triangle_at_time(self, time, task_no, cpu_no, event): | 534 | def add_sel_resume_triangle_at_time(self, time, task_no, cpu_no, event): |
| 535 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR | 535 | height = self._get_bar_height() * GraphFormat.BLOCK_TRIANGLE_FACTOR |
| 536 | x = self._get_time_xpos(time) | 536 | x = self.get_time_xpos(time) |
| 537 | y = self._get_item_ypos(cpu_no) + self._get_bar_height() / 2.0 - height / 2.0 | 537 | y = self.get_item_ypos(cpu_no) + self._get_bar_height() / 2.0 - height / 2.0 |
| 538 | 538 | ||
| 539 | self.canvas.add_sel_resume_triangle(x, y, height, event) | 539 | self.canvas.add_sel_resume_triangle(x, y, height, event) |
| 540 | 540 | ||
| 541 | def draw_completion_marker_at_time(self, time, task_no, cpu_no, selected=False): | 541 | def draw_completion_marker_at_time(self, time, task_no, cpu_no, selected=False): |
| 542 | height = self._get_bar_height() * GraphFormat.COMPLETION_MARKER_FACTOR | 542 | height = self._get_bar_height() * GraphFormat.COMPLETION_MARKER_FACTOR |
| 543 | x = self._get_time_xpos(time) | 543 | x = self.get_time_xpos(time) |
| 544 | y = self._get_item_ypos(cpu_no) + self._get_bar_height() - height | 544 | y = self.get_item_ypos(cpu_no) + self._get_bar_height() - height |
| 545 | 545 | ||
| 546 | self.canvas.draw_completion_marker(x, y, height, selected) | 546 | self.canvas.draw_completion_marker(x, y, height, selected) |
| 547 | 547 | ||
| 548 | def add_sel_completion_marker_at_time(self, time, task_no, cpu_no, event): | 548 | def add_sel_completion_marker_at_time(self, time, task_no, cpu_no, event): |
| 549 | height = self._get_bar_height() * GraphFormat.COMPLETION_MARKER_FACTOR | 549 | height = self._get_bar_height() * GraphFormat.COMPLETION_MARKER_FACTOR |
| 550 | 550 | ||
| 551 | x = self._get_time_xpos(time) | 551 | x = self.get_time_xpos(time) |
| 552 | y = self._get_item_ypos(cpu_no) + self._get_bar_height() - height | 552 | y = self.get_item_ypos(cpu_no) + self._get_bar_height() - height |
| 553 | 553 | ||
| 554 | self.canvas.add_sel_completion_marker(x, y, height, event) | 554 | self.canvas.add_sel_completion_marker(x, y, height, event) |
| 555 | 555 | ||
| 556 | def draw_release_arrow_at_time(self, time, task_no, job_no=None, selected=False): | 556 | def draw_release_arrow_at_time(self, time, task_no, job_no=None, selected=False): |
| 557 | if job_no is None and task_no is not None: | 557 | if job_no is None and task_no is not None: |
| 558 | raise ValueError("Must specify a job number along with the task number") | 558 | raise ValueError("Must specify a job number along with the task number") |
| 559 | 559 | ||
| 560 | height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR | 560 | height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR |
| 561 | 561 | ||
| 562 | x = self._get_time_xpos(time) | 562 | x = self.get_time_xpos(time) |
| 563 | y = self.origin[1] - height | 563 | y = self.origin[1] - height |
| 564 | 564 | ||
| 565 | self.canvas.draw_release_arrow_small(x, y, height, selected) | 565 | self.canvas.draw_release_arrow_small(x, y, height, selected) |
| 566 | 566 | ||
| 567 | if task_no is not None: | 567 | if task_no is not None: |
| 568 | y -= GraphFormat.ARROW_LABEL_OFS | 568 | y -= GraphFormat.ARROW_LABEL_OFS |
| 569 | self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \ | 569 | self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \ |
| 570 | GraphFormat.DEF_FOPTS_ARROW, GraphFormat.DEF_FOPTS_ARROW_SSCRIPT, \ | 570 | GraphFormat.DEF_FOPTS_ARROW, GraphFormat.DEF_FOPTS_ARROW_SSCRIPT, \ |
| 571 | AlignMode.CENTER, AlignMode.BOTTOM) | 571 | AlignMode.CENTER, AlignMode.BOTTOM) |
| 572 | 572 | ||
| 573 | def add_sel_release_arrow_at_time(self, time, task_no, event): | 573 | def add_sel_release_arrow_at_time(self, time, task_no, event): |
| 574 | height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR | 574 | height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR |
| 575 | 575 | ||
| 576 | x = self._get_time_xpos(time) | 576 | x = self.get_time_xpos(time) |
| 577 | y = self.origin[1] - height | 577 | y = self.origin[1] - height |
| 578 | 578 | ||
| 579 | self.canvas.add_sel_release_arrow_small(x, y, height, event) | 579 | self.canvas.add_sel_release_arrow_small(x, y, height, event) |
| 580 | 580 | ||
| 581 | def draw_deadline_arrow_at_time(self, time, task_no, job_no=None, selected=False): | 581 | def draw_deadline_arrow_at_time(self, time, task_no, job_no=None, selected=False): |
| 582 | if job_no is None and task_no is not None: | 582 | if job_no is None and task_no is not None: |
| 583 | raise ValueError("Must specify a job number along with the task number") | 583 | raise ValueError("Must specify a job number along with the task number") |
| 584 | 584 | ||
| 585 | height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR | 585 | height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR |
| 586 | 586 | ||
| 587 | x = self._get_time_xpos(time) | 587 | x = self.get_time_xpos(time) |
| 588 | y = self.origin[1] - height | 588 | y = self.origin[1] - height |
| 589 | 589 | ||
| 590 | self.canvas.draw_deadline_arrow_small(x, y, height, selected) | 590 | self.canvas.draw_deadline_arrow_small(x, y, height, selected) |
| 591 | 591 | ||
| 592 | if task_no is not None: | 592 | if task_no is not None: |
| 593 | y -= GraphFormat.ARROW_LABEL_OFS | 593 | y -= GraphFormat.ARROW_LABEL_OFS |
| 594 | self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \ | 594 | self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \ |
| 595 | GraphFormat.DEF_FOPTS_ARROW, GraphFormat.DEF_FOPTS_ARROW_SSCRIPT, \ | 595 | GraphFormat.DEF_FOPTS_ARROW, GraphFormat.DEF_FOPTS_ARROW_SSCRIPT, \ |
| 596 | AlignMode.CENTER, AlignMode.BOTTOM) | 596 | AlignMode.CENTER, AlignMode.BOTTOM) |
| 597 | 597 | ||
| 598 | def add_sel_deadline_arrow_at_time(self, time, task_no, event): | 598 | def add_sel_deadline_arrow_at_time(self, time, task_no, event): |
| 599 | height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR | 599 | height = self._get_bar_height() * GraphFormat.SMALL_ARROW_FACTOR |
| 600 | 600 | ||
| 601 | x = self._get_time_xpos(time) | 601 | x = self.get_time_xpos(time) |
| 602 | y = self.origin[1] - height | 602 | y = self.origin[1] - height |
| 603 | 603 | ||
| 604 | self.canvas.add_sel_deadline_arrow_small(x, y, height, event) | 604 | self.canvas.add_sel_deadline_arrow_small(x, y, height, event) |
| 605 | 605 | ||
| 606 | def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): | 606 | def draw_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): |
| 607 | if start_time > end_time: | 607 | if start_time > end_time: |
| 608 | raise ValueError("Litmus is not a time machine") | 608 | raise ValueError("Litmus is not a time machine") |
| 609 | 609 | ||
| 610 | x = self._get_time_xpos(start_time) | 610 | x = self.get_time_xpos(start_time) |
| 611 | y = self._get_item_ypos(cpu_no) | 611 | y = self.get_item_ypos(cpu_no) |
| 612 | width = self._get_bar_width(start_time, end_time) | 612 | width = self._get_bar_width(start_time, end_time) |
| 613 | height = self._get_bar_height() | 613 | height = self._get_bar_height() |
| 614 | 614 | ||
| 615 | self.canvas.draw_bar(x, y, width, height, task_no, clip_side, selected) | 615 | self.canvas.draw_bar(x, y, width, height, task_no, clip_side, selected) |
| 616 | 616 | ||
| 617 | # if a job number is specified, we want to draw a superscript and subscript for the task and job number, respectively | 617 | # if a job number is specified, we want to draw a superscript and subscript for the task and job number, respectively |
| 618 | if job_no is not None: | 618 | if job_no is not None: |
| 619 | x += GraphFormat.BAR_LABEL_OFS | 619 | x += GraphFormat.BAR_LABEL_OFS |
| @@ -621,37 +621,37 @@ class CpuGraph(Graph): | |||
| 621 | self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \ | 621 | self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \ |
| 622 | GraphFormat.DEF_FOPTS_BAR, GraphFormat.DEF_FOPTS_BAR_SSCRIPT, \ | 622 | GraphFormat.DEF_FOPTS_BAR, GraphFormat.DEF_FOPTS_BAR_SSCRIPT, \ |
| 623 | AlignMode.LEFT, AlignMode.CENTER) | 623 | AlignMode.LEFT, AlignMode.CENTER) |
| 624 | 624 | ||
| 625 | def add_sel_bar_at_time(self, start_time, end_time, task_no, cpu_no, event): | 625 | def add_sel_bar_at_time(self, start_time, end_time, task_no, cpu_no, event): |
| 626 | x = self._get_time_xpos(start_time) | 626 | x = self.get_time_xpos(start_time) |
| 627 | y = self._get_item_ypos(cpu_no) | 627 | y = self.get_item_ypos(cpu_no) |
| 628 | width = self._get_bar_width(start_time, end_time) | 628 | width = self._get_bar_width(start_time, end_time) |
| 629 | height = self._get_bar_height() | 629 | height = self._get_bar_height() |
| 630 | 630 | ||
| 631 | self.canvas.add_sel_bar(x, y, width, height, event) | 631 | self.canvas.add_sel_bar(x, y, width, height, event) |
| 632 | 632 | ||
| 633 | def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): | 633 | def draw_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, job_no=None, clip_side=None, selected=False): |
| 634 | if start_time > end_time: | 634 | if start_time > end_time: |
| 635 | raise ValueError("Litmus is not a time machine") | 635 | raise ValueError("Litmus is not a time machine") |
| 636 | 636 | ||
| 637 | x = self._get_time_xpos(start_time) | 637 | x = self.get_time_xpos(start_time) |
| 638 | y = self._get_item_ypos(len(self.y_item_list)) | 638 | y = self.get_item_ypos(len(self.y_item_list)) |
| 639 | width = self._get_bar_width(start_time, end_time) | 639 | width = self._get_bar_width(start_time, end_time) |
| 640 | height = self._get_mini_bar_height() | 640 | height = self._get_mini_bar_height() |
| 641 | 641 | ||
| 642 | self.canvas.draw_mini_bar(x, y, width, height, task_no, clip_side, selected) | 642 | self.canvas.draw_mini_bar(x, y, width, height, task_no, clip_side, selected) |
| 643 | 643 | ||
| 644 | if job_no is not None: | 644 | if job_no is not None: |
| 645 | x += GraphFormat.MINI_BAR_LABEL_OFS | 645 | x += GraphFormat.MINI_BAR_LABEL_OFS |
| 646 | y += self.attrs.y_item_size * GraphFormat.MINI_BAR_SIZE_FACTOR / 2.0 | 646 | y += self.attrs.y_item_size * GraphFormat.MINI_BAR_SIZE_FACTOR / 2.0 |
| 647 | self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \ | 647 | self.canvas.draw_label_with_sscripts('T', str(task_no), str(job_no), x, y, \ |
| 648 | GraphFormat.DEF_FOPTS_MINI_BAR, GraphFormat.DEF_FOPTS_MINI_BAR_SSCRIPT, AlignMode.LEFT, AlignMode.CENTER) | 648 | GraphFormat.DEF_FOPTS_MINI_BAR, GraphFormat.DEF_FOPTS_MINI_BAR_SSCRIPT, AlignMode.LEFT, AlignMode.CENTER) |
| 649 | 649 | ||
| 650 | def add_sel_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, event): | 650 | def add_sel_mini_bar_at_time(self, start_time, end_time, task_no, cpu_no, event): |
| 651 | x = self._get_time_xpos(start_time) | 651 | x = self.get_time_xpos(start_time) |
| 652 | y = self._get_item_ypos(len(self.y_item_list)) | 652 | y = self.get_item_ypos(len(self.y_item_list)) |
| 653 | width = self._get_bar_width(start_time, end_time) | 653 | width = self._get_bar_width(start_time, end_time) |
| 654 | height = self._get_mini_bar_height() | 654 | height = self._get_mini_bar_height() |
| 655 | 655 | ||
| 656 | self.canvas.add_sel_mini_bar(x, y, width, height, event) | 656 | self.canvas.add_sel_mini_bar(x, y, width, height, event) |
| 657 | 657 | ||
diff --git a/unit_trace/viz/renderer.py b/unit_trace/viz/renderer.py index 4b231c4..056e1a5 100644 --- a/unit_trace/viz/renderer.py +++ b/unit_trace/viz/renderer.py | |||
| @@ -4,17 +4,17 @@ from graph import * | |||
| 4 | 4 | ||
| 5 | """The renderer, a glue object which converts a schedule to its representation | 5 | """The renderer, a glue object which converts a schedule to its representation |
| 6 | on a graph.""" | 6 | on a graph.""" |
| 7 | 7 | ||
| 8 | class Renderer(object): | 8 | class Renderer(object): |
| 9 | def __init__(self, schedule): | 9 | def __init__(self, schedule): |
| 10 | self.schedule = schedule | 10 | self.schedule = schedule |
| 11 | 11 | ||
| 12 | def prepare_task_graph(self, SurfaceType=ImageSurface, attrs=GraphFormat()): | 12 | def prepare_task_graph(self, SurfaceType=ImageSurface, attrs=GraphFormat()): |
| 13 | """Outputs the fully-rendered graph (y-axis = tasks) to a Cairo ImageSurface""" | 13 | """Outputs the fully-rendered graph (y-axis = tasks) to a Cairo ImageSurface""" |
| 14 | item_list = self.get_task_item_list() | 14 | item_list = self.get_task_item_list() |
| 15 | start, end = self.schedule.get_time_bounds() | 15 | start, end = self.schedule.get_time_bounds() |
| 16 | self.graph = TaskGraph(CairoCanvas, SurfaceType(), start, end, item_list, attrs) | 16 | self.graph = TaskGraph(CairoCanvas, SurfaceType(), start, end, item_list, attrs) |
| 17 | 17 | ||
| 18 | def prepare_cpu_graph(self, SurfaceType=ImageSurface, attrs=GraphFormat()): | 18 | def prepare_cpu_graph(self, SurfaceType=ImageSurface, attrs=GraphFormat()): |
| 19 | item_list = ['CPU %d' % i for i in range(0, self.schedule.get_num_cpus())] | 19 | item_list = ['CPU %d' % i for i in range(0, self.schedule.get_num_cpus())] |
| 20 | start, end = self.schedule.get_time_bounds() | 20 | start, end = self.schedule.get_time_bounds() |
| @@ -25,16 +25,16 @@ class Renderer(object): | |||
| 25 | and drawing it piece by piece""" | 25 | and drawing it piece by piece""" |
| 26 | #graph.draw_axes('Time', '') | 26 | #graph.draw_axes('Time', '') |
| 27 | self.schedule.render(self.graph) | 27 | self.schedule.render(self.graph) |
| 28 | 28 | ||
| 29 | def write_out(self, fname): | 29 | def write_out(self, fname): |
| 30 | self.graph.surface.write_out(fname) | 30 | self.graph.surface.write_out(fname) |
| 31 | 31 | ||
| 32 | def get_graph(self): | 32 | def get_graph(self): |
| 33 | return self.graph | 33 | return self.graph |
| 34 | 34 | ||
| 35 | def get_schedule(self): | 35 | def get_schedule(self): |
| 36 | return self.schedule | 36 | return self.schedule |
| 37 | 37 | ||
| 38 | def get_task_item_list(self): | 38 | def get_task_item_list(self): |
| 39 | return [task.get_name() for task in self.schedule.get_task_list()] | 39 | return [task.get_name() for task in self.schedule.get_task_list()] |
| 40 | 40 | ||
diff --git a/unit_trace/viz/schedule.py b/unit_trace/viz/schedule.py index 81269fa..a44ce8d 100644 --- a/unit_trace/viz/schedule.py +++ b/unit_trace/viz/schedule.py | |||
| @@ -18,19 +18,19 @@ class TimeSlotArray(object): | |||
| 18 | the (approximate) time at which they occur. Events that occur at approximately the same | 18 | the (approximate) time at which they occur. Events that occur at approximately the same |
| 19 | time are assigned the same ``slot'', and each slot organizes its events by task number | 19 | time are assigned the same ``slot'', and each slot organizes its events by task number |
| 20 | as well as by CPU.""" | 20 | as well as by CPU.""" |
| 21 | 21 | ||
| 22 | TASK_LIST = 0 | 22 | TASK_LIST = 0 |
| 23 | CPU_LIST = 1 | 23 | CPU_LIST = 1 |
| 24 | 24 | ||
| 25 | def __init__(self, time_per_maj=None, num_tasks=0, num_cpus=0): | 25 | def __init__(self, time_per_maj=None, num_tasks=0, num_cpus=0): |
| 26 | if time_per_maj is None: | 26 | if time_per_maj is None: |
| 27 | self.array = None | 27 | self.array = None |
| 28 | return | 28 | return |
| 29 | 29 | ||
| 30 | self.time_per_maj = time_per_maj | 30 | self.time_per_maj = time_per_maj |
| 31 | self.list_sizes = { TimeSlotArray.TASK_LIST : num_tasks, TimeSlotArray.CPU_LIST : num_cpus } | 31 | self.list_sizes = { TimeSlotArray.TASK_LIST : num_tasks, TimeSlotArray.CPU_LIST : num_cpus } |
| 32 | self.array = {} | 32 | self.array = {} |
| 33 | 33 | ||
| 34 | for type in self.list_sizes: | 34 | for type in self.list_sizes: |
| 35 | num = self.list_sizes[type] | 35 | num = self.list_sizes[type] |
| 36 | self.array[type] = [] | 36 | self.array[type] = [] |
| @@ -40,26 +40,26 @@ class TimeSlotArray(object): | |||
| 40 | # by task). | 40 | # by task). |
| 41 | self.array[type].append(dict(zip(EVENT_LIST, \ | 41 | self.array[type].append(dict(zip(EVENT_LIST, \ |
| 42 | [{} for j in range(0, len(EVENT_LIST))]))) | 42 | [{} for j in range(0, len(EVENT_LIST))]))) |
| 43 | 43 | ||
| 44 | def get_time_slot(self, time): | 44 | def get_time_slot(self, time): |
| 45 | return int(time // self.time_per_maj) | 45 | return int(time // self.time_per_maj) |
| 46 | 46 | ||
| 47 | def _put_event_in_slot(self, list_type, no, klass, slot, event): | 47 | def _put_event_in_slot(self, list_type, no, klass, slot, event): |
| 48 | if slot not in self.array[list_type][no][klass]: | 48 | if slot not in self.array[list_type][no][klass]: |
| 49 | self.array[list_type][no][klass][slot] = [] | 49 | self.array[list_type][no][klass][slot] = [] |
| 50 | self.array[list_type][no][klass][slot].append(event) | 50 | self.array[list_type][no][klass][slot].append(event) |
| 51 | 51 | ||
| 52 | def add_event_to_time_slot(self, event): | 52 | def add_event_to_time_slot(self, event): |
| 53 | task_no = event.get_job().get_task().get_task_no() | 53 | task_no = event.get_job().get_task().get_task_no() |
| 54 | cpu = event.get_cpu() | 54 | cpu = event.get_cpu() |
| 55 | time_slot = self.get_time_slot(event.get_time()) | 55 | time_slot = self.get_time_slot(event.get_time()) |
| 56 | 56 | ||
| 57 | self._put_event_in_slot(TimeSlotArray.TASK_LIST, task_no, event.__class__, time_slot, event) | 57 | self._put_event_in_slot(TimeSlotArray.TASK_LIST, task_no, event.__class__, time_slot, event) |
| 58 | self._put_event_in_slot(TimeSlotArray.CPU_LIST, cpu, event.__class__, time_slot, event) | 58 | self._put_event_in_slot(TimeSlotArray.CPU_LIST, cpu, event.__class__, time_slot, event) |
| 59 | 59 | ||
| 60 | if event.__class__ in SPAN_END_EVENTS: | 60 | if event.__class__ in SPAN_END_EVENTS: |
| 61 | self.fill_span_event_from_end(event) | 61 | self.fill_span_event_from_end(event) |
| 62 | 62 | ||
| 63 | def fill_span_event_from_end(self, event): | 63 | def fill_span_event_from_end(self, event): |
| 64 | start_slot = None | 64 | start_slot = None |
| 65 | if event.corresp_start_event is None: | 65 | if event.corresp_start_event is None: |
| @@ -67,18 +67,18 @@ class TimeSlotArray(object): | |||
| 67 | else: | 67 | else: |
| 68 | start_slot = self.get_time_slot(event.corresp_start_event.get_time()) | 68 | start_slot = self.get_time_slot(event.corresp_start_event.get_time()) |
| 69 | end_slot = self.get_time_slot(event.get_time()) | 69 | end_slot = self.get_time_slot(event.get_time()) |
| 70 | 70 | ||
| 71 | for slot in range(start_slot + 1, end_slot): | 71 | for slot in range(start_slot + 1, end_slot): |
| 72 | task_no = event.get_job().get_task().get_task_no() | 72 | task_no = event.get_job().get_task().get_task_no() |
| 73 | cpu = event.get_cpu() | 73 | cpu = event.get_cpu() |
| 74 | 74 | ||
| 75 | dummy = SPAN_END_EVENTS[event.__class__](task_no, cpu) | 75 | dummy = SPAN_END_EVENTS[event.__class__](task_no, cpu) |
| 76 | dummy.corresp_start_event = event.corresp_start_event | 76 | dummy.corresp_start_event = event.corresp_start_event |
| 77 | dummy.corresp_end_event = event | 77 | dummy.corresp_end_event = event |
| 78 | 78 | ||
| 79 | self._put_event_in_slot(TimeSlotArray.TASK_LIST, task_no, dummy.__class__, slot, dummy) | 79 | self._put_event_in_slot(TimeSlotArray.TASK_LIST, task_no, dummy.__class__, slot, dummy) |
| 80 | self._put_event_in_slot(TimeSlotArray.CPU_LIST, cpu, dummy.__class__, slot, dummy) | 80 | self._put_event_in_slot(TimeSlotArray.CPU_LIST, cpu, dummy.__class__, slot, dummy) |
| 81 | 81 | ||
| 82 | def fill_span_event_from_start(self, event): | 82 | def fill_span_event_from_start(self, event): |
| 83 | end_slot = None | 83 | end_slot = None |
| 84 | if event.corresp_end_event is None: | 84 | if event.corresp_end_event is None: |
| @@ -86,18 +86,18 @@ class TimeSlotArray(object): | |||
| 86 | else: | 86 | else: |
| 87 | end_slot = self.get_time_slot(event.corresp_end_event.get_time()) | 87 | end_slot = self.get_time_slot(event.corresp_end_event.get_time()) |
| 88 | start_slot = self.get_time_slot(event.get_time()) | 88 | start_slot = self.get_time_slot(event.get_time()) |
| 89 | 89 | ||
| 90 | for slot in range(start_slot + 1, end_slot): | 90 | for slot in range(start_slot + 1, end_slot): |
| 91 | task_no = event.get_job().get_task().get_task_no() | 91 | task_no = event.get_job().get_task().get_task_no() |
| 92 | cpu = event.get_cpu() | 92 | cpu = event.get_cpu() |
| 93 | 93 | ||
| 94 | dummy = SPAN_START_EVENTS[event.__class__](task_no, cpu) | 94 | dummy = SPAN_START_EVENTS[event.__class__](task_no, cpu) |
| 95 | dummy.corresp_start_event = event | 95 | dummy.corresp_start_event = event |
| 96 | dummy.corresp_end_event = event.corresp_end_event | 96 | dummy.corresp_end_event = event.corresp_end_event |
| 97 | 97 | ||
| 98 | self._put_event_in_slot(TimeSlotArray.TASK_LIST, task_no, dummy.__class__, slot, dummy) | 98 | self._put_event_in_slot(TimeSlotArray.TASK_LIST, task_no, dummy.__class__, slot, dummy) |
| 99 | self._put_event_in_slot(TimeSlotArray.CPU_LIST, cpu, dummy.__class__, slot, dummy) | 99 | self._put_event_in_slot(TimeSlotArray.CPU_LIST, cpu, dummy.__class__, slot, dummy) |
| 100 | 100 | ||
| 101 | def get_events(self, slots, list_type, event_types): | 101 | def get_events(self, slots, list_type, event_types): |
| 102 | for type in event_types: | 102 | for type in event_types: |
| 103 | for slot in slots: | 103 | for slot in slots: |
| @@ -105,31 +105,31 @@ class TimeSlotArray(object): | |||
| 105 | if slot in self.array[list_type][no][type]: | 105 | if slot in self.array[list_type][no][type]: |
| 106 | for event in self.array[list_type][no][type][slot]: | 106 | for event in self.array[list_type][no][type][slot]: |
| 107 | yield event | 107 | yield event |
| 108 | 108 | ||
| 109 | def get_slots(self, slots, start, end, start_no, end_no, list_type): | 109 | def get_slots(self, slots, start, end, start_no, end_no, list_type): |
| 110 | if self.array is None: | 110 | if self.array is None: |
| 111 | return # empty schedule | 111 | return # empty schedule |
| 112 | 112 | ||
| 113 | if start > end: | 113 | if start > end: |
| 114 | raise ValueError('Litmus is not a time machine') | 114 | raise ValueError('Litmus is not a time machine') |
| 115 | if start_no > end_no: | 115 | if start_no > end_no: |
| 116 | raise ValueError('start no should be less than end no') | 116 | raise ValueError('start no should be less than end no') |
| 117 | 117 | ||
| 118 | start_slot = self.get_time_slot(start) | 118 | start_slot = self.get_time_slot(start) |
| 119 | end_slot = self.get_time_slot(end) + 1 | 119 | end_slot = self.get_time_slot(end) + 1 |
| 120 | start_no = max(0, start_no) | 120 | start_no = max(0, start_no) |
| 121 | end_no = min(self.list_sizes[list_type] - 1, end_no) | 121 | end_no = min(self.list_sizes[list_type] - 1, end_no) |
| 122 | 122 | ||
| 123 | for slot in xrange(start_slot, end_slot + 1): | 123 | for slot in xrange(start_slot, end_slot + 1): |
| 124 | if slot not in slots: | 124 | if slot not in slots: |
| 125 | slots[slot] = {} | 125 | slots[slot] = {} |
| 126 | for no in xrange(start_no, end_no + 1): | 126 | for no in xrange(start_no, end_no + 1): |
| 127 | slots[slot][no] = None | 127 | slots[slot][no] = None |
| 128 | 128 | ||
| 129 | class Schedule(object): | 129 | class Schedule(object): |
| 130 | """The total schedule (task system), consisting of a certain number of | 130 | """The total schedule (task system), consisting of a certain number of |
| 131 | tasks.""" | 131 | tasks.""" |
| 132 | 132 | ||
| 133 | def __init__(self, name, num_cpus, task_list=[]): | 133 | def __init__(self, name, num_cpus, task_list=[]): |
| 134 | self.name = name | 134 | self.name = name |
| 135 | self.tasks = {} | 135 | self.tasks = {} |
| @@ -140,13 +140,13 @@ class Schedule(object): | |||
| 140 | self.num_cpus = num_cpus | 140 | self.num_cpus = num_cpus |
| 141 | for task in task_list: | 141 | for task in task_list: |
| 142 | self.add_task(task) | 142 | self.add_task(task) |
| 143 | 143 | ||
| 144 | def get_selected(self): | 144 | def get_selected(self): |
| 145 | return self.selected | 145 | return self.selected |
| 146 | 146 | ||
| 147 | def set_selected(self, selected): | 147 | def set_selected(self, selected): |
| 148 | self.selected = selected | 148 | self.selected = selected |
| 149 | 149 | ||
| 150 | def add_selected(self, selected): | 150 | def add_selected(self, selected): |
| 151 | for layer in selected: | 151 | for layer in selected: |
| 152 | if layer not in self.selected: | 152 | if layer not in self.selected: |
| @@ -156,35 +156,35 @@ class Schedule(object): | |||
| 156 | self.selected[layer][event] = {} | 156 | self.selected[layer][event] = {} |
| 157 | for graph in selected[layer][event]: | 157 | for graph in selected[layer][event]: |
| 158 | self.selected[layer][event][graph] = selected[layer][event][graph] | 158 | self.selected[layer][event][graph] = selected[layer][event][graph] |
| 159 | 159 | ||
| 160 | def remove_selected(self, selected): | 160 | def remove_selected(self, selected): |
| 161 | for layer in selected: | 161 | for layer in selected: |
| 162 | if layer in self.selected: | 162 | if layer in self.selected: |
| 163 | for event in selected[layer]: | 163 | for event in selected[layer]: |
| 164 | if event in self.selected[layer]: | 164 | if event in self.selected[layer]: |
| 165 | del self.selected[layer][event] | 165 | del self.selected[layer][event] |
| 166 | 166 | ||
| 167 | def set_time_params(self, time_per_maj=None): | 167 | def set_time_params(self, time_per_maj=None): |
| 168 | self.time_per_maj = time_per_maj | 168 | self.time_per_maj = time_per_maj |
| 169 | if self.time_per_maj is None: | 169 | if self.time_per_maj is None: |
| 170 | self.time_slot_array = TimeSlotArray() | 170 | self.time_slot_array = TimeSlotArray() |
| 171 | return | 171 | return |
| 172 | 172 | ||
| 173 | self.time_slot_array = TimeSlotArray(self.time_per_maj, \ | 173 | self.time_slot_array = TimeSlotArray(self.time_per_maj, \ |
| 174 | len(self.task_list), self.num_cpus) | 174 | len(self.task_list), self.num_cpus) |
| 175 | 175 | ||
| 176 | def get_time_slot_array(self): | 176 | def get_time_slot_array(self): |
| 177 | return self.time_slot_array | 177 | return self.time_slot_array |
| 178 | 178 | ||
| 179 | def get_time_bounds(self): | 179 | def get_time_bounds(self): |
| 180 | return (self.start, self.end) | 180 | return (self.start, self.end) |
| 181 | 181 | ||
| 182 | def scan(self, time_per_maj): | 182 | def scan(self, time_per_maj): |
| 183 | self.start = None | 183 | self.start = None |
| 184 | self.end = None | 184 | self.end = None |
| 185 | 185 | ||
| 186 | self.set_time_params(time_per_maj) | 186 | self.set_time_params(time_per_maj) |
| 187 | 187 | ||
| 188 | # we scan the graph task by task, and job by job | 188 | # we scan the graph task by task, and job by job |
| 189 | for task_no, task in enumerate(self.get_task_list()): | 189 | for task_no, task in enumerate(self.get_task_list()): |
| 190 | switches = {} | 190 | switches = {} |
| @@ -192,12 +192,12 @@ class Schedule(object): | |||
| 192 | switches[event] = None | 192 | switches[event] = None |
| 193 | cur_cpu = [Event.NO_CPU] | 193 | cur_cpu = [Event.NO_CPU] |
| 194 | for job_no in sorted(task.get_jobs().keys()): | 194 | for job_no in sorted(task.get_jobs().keys()): |
| 195 | job = task.get_jobs()[job_no] | 195 | job = task.get_jobs()[job_no] |
| 196 | for event_time in sorted(job.get_events().keys()): | 196 | for event_time in sorted(job.get_events().keys()): |
| 197 | # could have multiple events at the same time (unlikely but possible) | 197 | # could have multiple events at the same time (unlikely but possible) |
| 198 | for event in job.get_events()[event_time]: | 198 | for event in job.get_events()[event_time]: |
| 199 | event.scan(cur_cpu, switches) | 199 | event.scan(cur_cpu, switches) |
| 200 | 200 | ||
| 201 | # What if one of the initial "span events" (switch to or inversion starting) never got a | 201 | # What if one of the initial "span events" (switch to or inversion starting) never got a |
| 202 | # corresponding end event? Well, then we assume that the end event was simply outside of | 202 | # corresponding end event? Well, then we assume that the end event was simply outside of |
| 203 | # the range of whatever we read in. So we need to fill dummies starting from the initial | 203 | # the range of whatever we read in. So we need to fill dummies starting from the initial |
| @@ -207,7 +207,7 @@ class Schedule(object): | |||
| 207 | event = switches[span_event] | 207 | event = switches[span_event] |
| 208 | if event is not None: | 208 | if event is not None: |
| 209 | self.time_slot_array.fill_span_event_from_start(event) | 209 | self.time_slot_array.fill_span_event_from_start(event) |
| 210 | 210 | ||
| 211 | def add_task(self, task): | 211 | def add_task(self, task): |
| 212 | if task.name in self.tasks: | 212 | if task.name in self.tasks: |
| 213 | raise ValueError("task already in list!") | 213 | raise ValueError("task already in list!") |
| @@ -216,29 +216,29 @@ class Schedule(object): | |||
| 216 | task.schedule = self | 216 | task.schedule = self |
| 217 | task.task_no = self.cur_task_no | 217 | task.task_no = self.cur_task_no |
| 218 | self.cur_task_no += 1 | 218 | self.cur_task_no += 1 |
| 219 | 219 | ||
| 220 | def get_tasks(self): | 220 | def get_tasks(self): |
| 221 | return self.tasks | 221 | return self.tasks |
| 222 | 222 | ||
| 223 | def get_task_list(self): | 223 | def get_task_list(self): |
| 224 | return self.task_list | 224 | return self.task_list |
| 225 | 225 | ||
| 226 | def get_name(self): | 226 | def get_name(self): |
| 227 | return self.name | 227 | return self.name |
| 228 | 228 | ||
| 229 | def get_num_cpus(self): | 229 | def get_num_cpus(self): |
| 230 | return self.num_cpus | 230 | return self.num_cpus |
| 231 | 231 | ||
| 232 | def deepcopy_selected(selected): | 232 | def deepcopy_selected(selected): |
| 233 | selected_copy = {} | 233 | selected_copy = {} |
| 234 | for layer in selected: | 234 | for layer in selected: |
| 235 | selected_copy[layer] = copy.copy(selected[layer]) | 235 | selected_copy[layer] = copy.copy(selected[layer]) |
| 236 | return selected_copy | 236 | return selected_copy |
| 237 | 237 | ||
| 238 | class Task(object): | 238 | class Task(object): |
| 239 | """Represents a task, including the set of jobs that were run under | 239 | """Represents a task, including the set of jobs that were run under |
| 240 | this task.""" | 240 | this task.""" |
| 241 | 241 | ||
| 242 | def __init__(self, name, job_list=[]): | 242 | def __init__(self, name, job_list=[]): |
| 243 | self.name = name | 243 | self.name = name |
| 244 | self.jobs = {} | 244 | self.jobs = {} |
| @@ -246,25 +246,25 @@ class Task(object): | |||
| 246 | self.schedule = None | 246 | self.schedule = None |
| 247 | for job in job_list: | 247 | for job in job_list: |
| 248 | self.add_job(job) | 248 | self.add_job(job) |
| 249 | 249 | ||
| 250 | def add_job(self, job): | 250 | def add_job(self, job): |
| 251 | if job.job_no in self.jobs: | 251 | if job.job_no in self.jobs: |
| 252 | raise ScheduleError("a job is already being released at this time for this task") | 252 | raise ScheduleError("a job is already being released at this time for this task") |
| 253 | self.jobs[job.job_no] = job | 253 | self.jobs[job.job_no] = job |
| 254 | job.task = self | 254 | job.task = self |
| 255 | 255 | ||
| 256 | def get_schedule(self): | 256 | def get_schedule(self): |
| 257 | return self.schedule | 257 | return self.schedule |
| 258 | 258 | ||
| 259 | def get_jobs(self): | 259 | def get_jobs(self): |
| 260 | return self.jobs | 260 | return self.jobs |
| 261 | 261 | ||
| 262 | def get_task_no(self): | 262 | def get_task_no(self): |
| 263 | return self.task_no | 263 | return self.task_no |
| 264 | 264 | ||
| 265 | def get_name(self): | 265 | def get_name(self): |
| 266 | return self.name | 266 | return self.name |
| 267 | 267 | ||
| 268 | class Job(object): | 268 | class Job(object): |
| 269 | """Represents a job, including everything that happens related to the job""" | 269 | """Represents a job, including everything that happens related to the job""" |
| 270 | def __init__(self, job_no, event_list=[]): | 270 | def __init__(self, job_no, event_list=[]): |
| @@ -273,19 +273,19 @@ class Job(object): | |||
| 273 | self.task = None | 273 | self.task = None |
| 274 | for event in event_list: | 274 | for event in event_list: |
| 275 | self.add_event(event) | 275 | self.add_event(event) |
| 276 | 276 | ||
| 277 | def add_event(self, event): | 277 | def add_event(self, event): |
| 278 | if event.time not in self.events: | 278 | if event.time not in self.events: |
| 279 | self.events[event.time] = [] | 279 | self.events[event.time] = [] |
| 280 | self.events[event.time].append(event) | 280 | self.events[event.time].append(event) |
| 281 | event.job = self | 281 | event.job = self |
| 282 | 282 | ||
| 283 | def get_events(self): | 283 | def get_events(self): |
| 284 | return self.events | 284 | return self.events |
| 285 | 285 | ||
| 286 | def get_task(self): | 286 | def get_task(self): |
| 287 | return self.task | 287 | return self.task |
| 288 | 288 | ||
| 289 | def get_job_no(self): | 289 | def get_job_no(self): |
| 290 | return self.job_no | 290 | return self.job_no |
| 291 | 291 | ||
| @@ -301,61 +301,73 @@ class DummyEvent(object): | |||
| 301 | self.cpu = cpu | 301 | self.cpu = cpu |
| 302 | self.job = None | 302 | self.job = None |
| 303 | self.layer = None | 303 | self.layer = None |
| 304 | 304 | ||
| 305 | def __str__(self): | 305 | def __str__(self): |
| 306 | return '[Dummy Event]' | 306 | return '[Dummy Event]' |
| 307 | 307 | ||
| 308 | def get_time(self): | 308 | def get_time(self): |
| 309 | return self.time | 309 | return self.time |
| 310 | 310 | ||
| 311 | def get_cpu(self): | 311 | def get_cpu(self): |
| 312 | return self.cpu | 312 | return self.cpu |
| 313 | 313 | ||
| 314 | def get_job(self): | 314 | def get_job(self): |
| 315 | return self.job | 315 | return self.job |
| 316 | 316 | ||
| 317 | def get_layer(self): | 317 | def get_layer(self): |
| 318 | return self.layer | 318 | return self.layer |
| 319 | 319 | ||
| 320 | def render(self, graph, layer, prev_events, selectable=False): | 320 | def render(self, graph, layer, prev_events, selectable=False): |
| 321 | """Method that the visualizer calls to tell the event to render itself | 321 | """Method that the visualizer calls to tell the event to render itself |
| 322 | Obviously only implemented by subclasses (actual event types) | 322 | Obviously only implemented by subclasses (actual event types) |
| 323 | 323 | ||
| 324 | ``Rendering'' can mean either actually drawing the event or just | 324 | ``Rendering'' can mean either actually drawing the event or just |
| 325 | adding it as a selectable region. This is controlled by the | 325 | adding it as a selectable region. This is controlled by the |
| 326 | ``selectable'' parameter""" | 326 | ``selectable'' parameter""" |
| 327 | raise NotImplementdError | 327 | raise NotImplementdError |
| 328 | 328 | ||
| 329 | class Event(DummyEvent): | 329 | class Event(DummyEvent): |
| 330 | """Represents an event that occurs while a job is running (e.g. get scheduled | 330 | """Represents an event that occurs while a job is running (e.g. get scheduled |
| 331 | on a CPU, block, ...)""" | 331 | on a CPU, block, ...)""" |
| 332 | NO_CPU = -1 | 332 | NO_CPU = -1 |
| 333 | NUM_DEC_PLACES = 2 | 333 | NUM_DEC_PLACES = 2 |
| 334 | 334 | ||
| 335 | def __init__(self, time, cpu): | 335 | def __init__(self, time, cpu): |
| 336 | super(Event, self).__init__(time, cpu) | 336 | super(Event, self).__init__(time, cpu) |
| 337 | self.erroneous = False | 337 | self.erroneous = False |
| 338 | 338 | ||
| 339 | def get_name(self): | ||
| 340 | raise NotImplementedError | ||
| 341 | |||
| 339 | def __str__(self): | 342 | def __str__(self): |
| 340 | return '[Event]' | 343 | return self.get_name() + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) |
| 341 | 344 | ||
| 345 | def str_long(self): | ||
| 346 | """Prints the event as a string, in ``long'' form.""" | ||
| 347 | return 'Event Type: ' + self.get_name() + \ | ||
| 348 | '\nTask Name: ' + str(self.get_job().get_task().get_name()) + \ | ||
| 349 | '\n(Task no., Job no.): ' + str((self.get_job().get_task().get_task_no(), \ | ||
| 350 | self.get_job().get_job_no())) + \ | ||
| 351 | '\nCPU: ' + str(self.get_cpu()) + \ | ||
| 352 | '\nTime: ' + str(self.get_time()) | ||
| 353 | |||
| 342 | def _common_str(self): | 354 | def _common_str(self): |
| 343 | job = self.get_job() | 355 | job = self.get_job() |
| 344 | task = job.get_task() | 356 | task = job.get_task() |
| 345 | return ' for task ' + str(task.get_name()) + ': (TASK, JOB)=' + str((task.get_task_no(), \ | 357 | return ' for task ' + str(task.get_name()) + ': (TASK, JOB)=' + str((task.get_task_no(), \ |
| 346 | job.get_job_no())) + ', CPU=' + str(self.get_cpu()) | 358 | job.get_job_no())) + ', CPU=' + str(self.get_cpu()) |
| 347 | 359 | ||
| 348 | def is_erroneous(self): | 360 | def is_erroneous(self): |
| 349 | """An erroneous event is where something with the event is not quite right, | 361 | """An erroneous event is where something with the event is not quite right, |
| 350 | something significantly wrong that we don't have logical information telling | 362 | something significantly wrong that we don't have logical information telling |
| 351 | us how we should render the event.""" | 363 | us how we should render the event.""" |
| 352 | return self.erroneous | 364 | return self.erroneous |
| 353 | 365 | ||
| 354 | def is_selected(self): | 366 | def is_selected(self): |
| 355 | """Returns whether the event has been selected by the user. (needed for rendering)""" | 367 | """Returns whether the event has been selected by the user. (needed for rendering)""" |
| 356 | selected = self.get_job().get_task().get_schedule().get_selected() | 368 | selected = self.get_job().get_task().get_schedule().get_selected() |
| 357 | return self.get_layer() in selected and self in selected[self.get_layer()] | 369 | return self.get_layer() in selected and self in selected[self.get_layer()] |
| 358 | 370 | ||
| 359 | def scan(self, cur_cpu, switches): | 371 | def scan(self, cur_cpu, switches): |
| 360 | """Part of the procedure that walks through all the events and sets | 372 | """Part of the procedure that walks through all the events and sets |
| 361 | some parameters that are unknown at first. For instance, a SwitchAwayEvent | 373 | some parameters that are unknown at first. For instance, a SwitchAwayEvent |
| @@ -370,26 +382,26 @@ class Event(DummyEvent): | |||
| 370 | sched.start = time | 382 | sched.start = time |
| 371 | if sched.end is None or time > sched.end: | 383 | if sched.end is None or time > sched.end: |
| 372 | sched.end = time | 384 | sched.end = time |
| 373 | 385 | ||
| 374 | sched.get_time_slot_array().add_event_to_time_slot(self) | 386 | sched.get_time_slot_array().add_event_to_time_slot(self) |
| 375 | 387 | ||
| 376 | class ErrorEvent(Event): | 388 | class ErrorEvent(Event): |
| 377 | pass | 389 | pass |
| 378 | 390 | ||
| 379 | class SuspendEvent(Event): | 391 | class SuspendEvent(Event): |
| 380 | def __init__(self, time, cpu): | 392 | def __init__(self, time, cpu): |
| 381 | super(SuspendEvent, self).__init__(time, cpu) | 393 | super(SuspendEvent, self).__init__(time, cpu) |
| 382 | self.layer = Canvas.MIDDLE_LAYER | 394 | self.layer = Canvas.MIDDLE_LAYER |
| 383 | 395 | ||
| 384 | def __str__(self): | 396 | def get_name(self): |
| 385 | return 'Suspend' + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | 397 | return 'Suspend' |
| 386 | 398 | ||
| 387 | def scan(self, cur_cpu, switches): | 399 | def scan(self, cur_cpu, switches): |
| 388 | if self.get_cpu() != cur_cpu[0]: | 400 | if self.get_cpu() != cur_cpu[0]: |
| 389 | self.erroneous = True | 401 | self.erroneous = True |
| 390 | #fprint "suspending on a CPU different from the CPU we are on!" | 402 | #fprint "suspending on a CPU different from the CPU we are on!" |
| 391 | super(SuspendEvent, self).scan(cur_cpu, switches) | 403 | super(SuspendEvent, self).scan(cur_cpu, switches) |
| 392 | 404 | ||
| 393 | def render(self, graph, layer, prev_events, selectable=False): | 405 | def render(self, graph, layer, prev_events, selectable=False): |
| 394 | if layer == self.layer: | 406 | if layer == self.layer: |
| 395 | prev_events[self] = None | 407 | prev_events[self] = None |
| @@ -399,22 +411,22 @@ class SuspendEvent(Event): | |||
| 399 | else: | 411 | else: |
| 400 | graph.draw_suspend_triangle_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | 412 | graph.draw_suspend_triangle_at_time(self.get_time(), self.get_job().get_task().get_task_no(), |
| 401 | self.get_cpu(), self.is_selected()) | 413 | self.get_cpu(), self.is_selected()) |
| 402 | 414 | ||
| 403 | 415 | ||
| 404 | class ResumeEvent(Event): | 416 | class ResumeEvent(Event): |
| 405 | def __init__(self, time, cpu): | 417 | def __init__(self, time, cpu): |
| 406 | super(ResumeEvent, self).__init__(time, cpu) | 418 | super(ResumeEvent, self).__init__(time, cpu) |
| 407 | self.layer = Canvas.MIDDLE_LAYER | 419 | self.layer = Canvas.MIDDLE_LAYER |
| 408 | 420 | ||
| 409 | def __str__(self): | 421 | def get_name(self): |
| 410 | return 'Resume' + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | 422 | return 'Resume' |
| 411 | 423 | ||
| 412 | def scan(self, cur_cpu, switches): | 424 | def scan(self, cur_cpu, switches): |
| 413 | if cur_cpu[0] != Event.NO_CPU and cur_cpu[0] != self.get_cpu(): | 425 | if cur_cpu[0] != Event.NO_CPU and cur_cpu[0] != self.get_cpu(): |
| 414 | self.erroneous = True | 426 | self.erroneous = True |
| 415 | #print "Resuming when currently scheduled on a CPU, but on a different CPU from the current CPU!" | 427 | #print "Resuming when currently scheduled on a CPU, but on a different CPU from the current CPU!" |
| 416 | super(ResumeEvent, self).scan(cur_cpu, switches) | 428 | super(ResumeEvent, self).scan(cur_cpu, switches) |
| 417 | 429 | ||
| 418 | def render(self, graph, layer, prev_events, selectable=False): | 430 | def render(self, graph, layer, prev_events, selectable=False): |
| 419 | if layer == self.layer: | 431 | if layer == self.layer: |
| 420 | prev_events[self] = None | 432 | prev_events[self] = None |
| @@ -424,19 +436,19 @@ class ResumeEvent(Event): | |||
| 424 | else: | 436 | else: |
| 425 | graph.draw_resume_triangle_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | 437 | graph.draw_resume_triangle_at_time(self.get_time(), self.get_job().get_task().get_task_no(), |
| 426 | self.get_cpu(), self.is_selected()) | 438 | self.get_cpu(), self.is_selected()) |
| 427 | 439 | ||
| 428 | 440 | ||
| 429 | class CompleteEvent(Event): | 441 | class CompleteEvent(Event): |
| 430 | def __init__(self, time, cpu): | 442 | def __init__(self, time, cpu): |
| 431 | super(CompleteEvent, self).__init__(time, cpu) | 443 | super(CompleteEvent, self).__init__(time, cpu) |
| 432 | self.layer = Canvas.TOP_LAYER | 444 | self.layer = Canvas.TOP_LAYER |
| 433 | 445 | ||
| 434 | def __str__(self): | 446 | def get_name(self): |
| 435 | return 'Complete' + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | 447 | return 'Complete' |
| 436 | 448 | ||
| 437 | def scan(self, cur_cpu, switches): | 449 | def scan(self, cur_cpu, switches): |
| 438 | super(CompleteEvent, self).scan(cur_cpu, switches) | 450 | super(CompleteEvent, self).scan(cur_cpu, switches) |
| 439 | 451 | ||
| 440 | def render(self, graph, layer, prev_events, selectable=False): | 452 | def render(self, graph, layer, prev_events, selectable=False): |
| 441 | if layer == Canvas.TOP_LAYER: | 453 | if layer == Canvas.TOP_LAYER: |
| 442 | prev_events[self] = None | 454 | prev_events[self] = None |
| @@ -452,27 +464,44 @@ class SwitchToEvent(Event): | |||
| 452 | super(SwitchToEvent, self).__init__(time, cpu) | 464 | super(SwitchToEvent, self).__init__(time, cpu) |
| 453 | self.layer = Canvas.BOTTOM_LAYER | 465 | self.layer = Canvas.BOTTOM_LAYER |
| 454 | self.corresp_end_event = None | 466 | self.corresp_end_event = None |
| 455 | 467 | ||
| 468 | def get_name(self): | ||
| 469 | if self.corresp_end_event is None: | ||
| 470 | return 'Switch To (w/o Switch Away)' | ||
| 471 | else: | ||
| 472 | return 'Scheduled' | ||
| 473 | |||
| 456 | def __str__(self): | 474 | def __str__(self): |
| 457 | if self.corresp_end_event is None: | 475 | if self.corresp_end_event is None: |
| 458 | return 'Switch To (w/o Switch Away)' + self._common_str() + ', TIME=' \ | 476 | return super(SwitchToEvent, self).__str__() |
| 459 | + str(self.get_time()) | 477 | return self.get_name() + self._common_str() + ', START=' \ |
| 460 | return 'Scheduled' + self._common_str() + ', START=' \ | ||
| 461 | + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) \ | 478 | + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) \ |
| 462 | + ', END=' + util.format_float(self.corresp_end_event.get_time(), Event.NUM_DEC_PLACES) | 479 | + ', END=' + util.format_float(self.corresp_end_event.get_time(), Event.NUM_DEC_PLACES) |
| 463 | 480 | ||
| 481 | def str_long(self): | ||
| 482 | if self.corresp_end_event is None: | ||
| 483 | return super(SwitchToEvent, self).str_long() | ||
| 484 | else : | ||
| 485 | return 'Event Type: ' + self.get_name() + \ | ||
| 486 | '\nTask Name: ' + str(self.get_job().get_task().get_name()) + \ | ||
| 487 | '\n(Task no., Job no.): ' + str((self.get_job().get_task().get_task_no(), \ | ||
| 488 | self.get_job().get_job_no())) + \ | ||
| 489 | '\nCPU: ' + str(self.get_cpu()) + \ | ||
| 490 | '\nStart: ' + str(self.get_time()) + \ | ||
| 491 | '\nEnd: ' + str(self.corresp_end_event.get_time()) | ||
| 492 | |||
| 464 | def scan(self, cur_cpu, switches): | 493 | def scan(self, cur_cpu, switches): |
| 465 | old_cur_cpu = cur_cpu[0] | 494 | old_cur_cpu = cur_cpu[0] |
| 466 | cur_cpu[0] = self.get_cpu() | 495 | cur_cpu[0] = self.get_cpu() |
| 467 | switches[SwitchToEvent] = self | 496 | switches[SwitchToEvent] = self |
| 468 | self.corresp_end_event = None | 497 | self.corresp_end_event = None |
| 469 | 498 | ||
| 470 | if old_cur_cpu != Event.NO_CPU: | 499 | if old_cur_cpu != Event.NO_CPU: |
| 471 | self.erroneous = True | 500 | self.erroneous = True |
| 472 | #print "currently scheduled somewhere, can't switch to a CPU" | 501 | #print "currently scheduled somewhere, can't switch to a CPU" |
| 473 | 502 | ||
| 474 | super(SwitchToEvent, self).scan(cur_cpu, switches) | 503 | super(SwitchToEvent, self).scan(cur_cpu, switches) |
| 475 | 504 | ||
| 476 | def render(self, graph, layer, prev_events, selectable=False): | 505 | def render(self, graph, layer, prev_events, selectable=False): |
| 477 | if layer == self.layer: | 506 | if layer == self.layer: |
| 478 | end_time = None | 507 | end_time = None |
| @@ -482,7 +511,7 @@ class SwitchToEvent(Event): | |||
| 482 | clip = AlignMode.RIGHT | 511 | clip = AlignMode.RIGHT |
| 483 | else: | 512 | else: |
| 484 | end_time = self.corresp_end_event.get_time() | 513 | end_time = self.corresp_end_event.get_time() |
| 485 | 514 | ||
| 486 | prev_events[self] = None | 515 | prev_events[self] = None |
| 487 | cpu = self.get_cpu() | 516 | cpu = self.get_cpu() |
| 488 | task_no = self.get_job().get_task().get_task_no() | 517 | task_no = self.get_job().get_task().get_task_no() |
| @@ -493,30 +522,41 @@ class SwitchToEvent(Event): | |||
| 493 | graph.draw_bar_at_time(self.get_time(), end_time, | 522 | graph.draw_bar_at_time(self.get_time(), end_time, |
| 494 | task_no, cpu, self.get_job().get_job_no(), | 523 | task_no, cpu, self.get_job().get_job_no(), |
| 495 | clip, self.is_selected()) | 524 | clip, self.is_selected()) |
| 496 | 525 | ||
| 497 | class SwitchAwayEvent(Event): | 526 | class SwitchAwayEvent(Event): |
| 498 | def __init__(self, time, cpu): | 527 | def __init__(self, time, cpu): |
| 499 | super(SwitchAwayEvent, self).__init__(time, cpu) | 528 | super(SwitchAwayEvent, self).__init__(time, cpu) |
| 500 | self.layer = Canvas.BOTTOM_LAYER | 529 | self.layer = Canvas.BOTTOM_LAYER |
| 501 | self.corresp_start_event = None | 530 | self.corresp_start_event = None |
| 502 | 531 | ||
| 532 | def get_name(self): | ||
| 533 | if self.corresp_start_event is None: | ||
| 534 | return 'Switch Away (w/o Switch To)' | ||
| 535 | else: | ||
| 536 | return 'Scheduled' | ||
| 537 | |||
| 503 | def __str__(self): | 538 | def __str__(self): |
| 504 | if self.corresp_start_event is None: | 539 | if self.corresp_start_event is None: |
| 505 | return 'Switch Away (w/o Switch To)' + self._common_str() + ', TIME=' \ | 540 | return super(SwitchAwayEvent, self).__str__() |
| 506 | + str(self.get_time()) | ||
| 507 | return str(self.corresp_start_event) | 541 | return str(self.corresp_start_event) |
| 508 | 542 | ||
| 543 | def str_long(self): | ||
| 544 | if self.corresp_start_event is None: | ||
| 545 | return super(SwitchAwayEvent, self).str_long() | ||
| 546 | |||
| 547 | return self.corresp_start_event.str_long() | ||
| 548 | |||
| 509 | def scan(self, cur_cpu, switches): | 549 | def scan(self, cur_cpu, switches): |
| 510 | old_cur_cpu = cur_cpu[0] | 550 | old_cur_cpu = cur_cpu[0] |
| 511 | 551 | ||
| 512 | self.corresp_start_event = switches[SwitchToEvent] | 552 | self.corresp_start_event = switches[SwitchToEvent] |
| 513 | 553 | ||
| 514 | cur_cpu[0] = Event.NO_CPU | 554 | cur_cpu[0] = Event.NO_CPU |
| 515 | switches[SwitchToEvent] = None | 555 | switches[SwitchToEvent] = None |
| 516 | 556 | ||
| 517 | if self.corresp_start_event is not None: | 557 | if self.corresp_start_event is not None: |
| 518 | self.corresp_start_event.corresp_end_event = self | 558 | self.corresp_start_event.corresp_end_event = self |
| 519 | 559 | ||
| 520 | if self.get_cpu() != old_cur_cpu: | 560 | if self.get_cpu() != old_cur_cpu: |
| 521 | self.erroneous = True | 561 | self.erroneous = True |
| 522 | #print "switching away from a CPU different from the CPU we are currently on" | 562 | #print "switching away from a CPU different from the CPU we are currently on" |
| @@ -526,9 +566,9 @@ class SwitchAwayEvent(Event): | |||
| 526 | elif self.get_time() < self.corresp_start_event.get_time(): | 566 | elif self.get_time() < self.corresp_start_event.get_time(): |
| 527 | self.erroneous = True | 567 | self.erroneous = True |
| 528 | #print "switching away from a processor before we switched to it?!" | 568 | #print "switching away from a processor before we switched to it?!" |
| 529 | 569 | ||
| 530 | super(SwitchAwayEvent, self).scan(cur_cpu, switches) | 570 | super(SwitchAwayEvent, self).scan(cur_cpu, switches) |
| 531 | 571 | ||
| 532 | def render(self, graph, layer, prev_events, selectable=False): | 572 | def render(self, graph, layer, prev_events, selectable=False): |
| 533 | if self.corresp_start_event is None: | 573 | if self.corresp_start_event is None: |
| 534 | # We never found a corresponding start event. In that case, we can assume it lies | 574 | # We never found a corresponding start event. In that case, we can assume it lies |
| @@ -550,18 +590,18 @@ class SwitchAwayEvent(Event): | |||
| 550 | if self.corresp_start_event in prev_events: | 590 | if self.corresp_start_event in prev_events: |
| 551 | return # already rendered the bar | 591 | return # already rendered the bar |
| 552 | self.corresp_start_event.render(graph, layer, prev_events, selectable) | 592 | self.corresp_start_event.render(graph, layer, prev_events, selectable) |
| 553 | 593 | ||
| 554 | class ReleaseEvent(Event): | 594 | class ReleaseEvent(Event): |
| 555 | def __init__(self, time, cpu): | 595 | def __init__(self, time, cpu): |
| 556 | super(ReleaseEvent, self).__init__(time, cpu) | 596 | super(ReleaseEvent, self).__init__(time, cpu) |
| 557 | self.layer = Canvas.TOP_LAYER | 597 | self.layer = Canvas.TOP_LAYER |
| 558 | 598 | ||
| 559 | def __str__(self): | 599 | def get_name(self): |
| 560 | return 'Release' + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | 600 | return 'Release' |
| 561 | 601 | ||
| 562 | def scan(self, cur_cpu, switches): | 602 | def scan(self, cur_cpu, switches): |
| 563 | super(ReleaseEvent, self).scan(cur_cpu, switches) | 603 | super(ReleaseEvent, self).scan(cur_cpu, switches) |
| 564 | 604 | ||
| 565 | def render(self, graph, layer, prev_events, selectable=False): | 605 | def render(self, graph, layer, prev_events, selectable=False): |
| 566 | prev_events[self] = None | 606 | prev_events[self] = None |
| 567 | if layer == Canvas.TOP_LAYER: | 607 | if layer == Canvas.TOP_LAYER: |
| @@ -571,19 +611,19 @@ class ReleaseEvent(Event): | |||
| 571 | else: | 611 | else: |
| 572 | graph.draw_release_arrow_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | 612 | graph.draw_release_arrow_at_time(self.get_time(), self.get_job().get_task().get_task_no(), |
| 573 | self.get_job().get_job_no(), self.is_selected()) | 613 | self.get_job().get_job_no(), self.is_selected()) |
| 574 | 614 | ||
| 575 | 615 | ||
| 576 | class DeadlineEvent(Event): | 616 | class DeadlineEvent(Event): |
| 577 | def __init__(self, time, cpu): | 617 | def __init__(self, time, cpu): |
| 578 | super(DeadlineEvent, self).__init__(time, cpu) | 618 | super(DeadlineEvent, self).__init__(time, cpu) |
| 579 | self.layer = Canvas.TOP_LAYER | 619 | self.layer = Canvas.TOP_LAYER |
| 580 | 620 | ||
| 581 | def __str__(self): | 621 | def get_name(self): |
| 582 | return 'Deadline' + self._common_str() + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | 622 | return 'Deadline' |
| 583 | 623 | ||
| 584 | def scan(self, cur_cpu, switches): | 624 | def scan(self, cur_cpu, switches): |
| 585 | super(DeadlineEvent, self).scan(cur_cpu, switches) | 625 | super(DeadlineEvent, self).scan(cur_cpu, switches) |
| 586 | 626 | ||
| 587 | def render(self, graph, layer, prev_events, selectable=False): | 627 | def render(self, graph, layer, prev_events, selectable=False): |
| 588 | prev_events[self] = None | 628 | prev_events[self] = None |
| 589 | if layer == Canvas.TOP_LAYER: | 629 | if layer == Canvas.TOP_LAYER: |
| @@ -593,29 +633,46 @@ class DeadlineEvent(Event): | |||
| 593 | else: | 633 | else: |
| 594 | graph.draw_deadline_arrow_at_time(self.get_time(), self.get_job().get_task().get_task_no(), | 634 | graph.draw_deadline_arrow_at_time(self.get_time(), self.get_job().get_task().get_task_no(), |
| 595 | self.get_job().get_job_no(), self.is_selected()) | 635 | self.get_job().get_job_no(), self.is_selected()) |
| 596 | 636 | ||
| 597 | 637 | ||
| 598 | class InversionStartEvent(ErrorEvent): | 638 | class InversionStartEvent(ErrorEvent): |
| 599 | def __init__(self, time): | 639 | def __init__(self, time): |
| 600 | super(InversionStartEvent, self).__init__(time, Event.NO_CPU) | 640 | super(InversionStartEvent, self).__init__(time, Event.NO_CPU) |
| 601 | self.layer = Canvas.BOTTOM_LAYER | 641 | self.layer = Canvas.BOTTOM_LAYER |
| 602 | self.corresp_end_event = None | 642 | self.corresp_end_event = None |
| 603 | 643 | ||
| 644 | def get_name(self): | ||
| 645 | if self.corresp_end_event is None: | ||
| 646 | return 'Inversion Start (w/o Inversion End)' | ||
| 647 | else: | ||
| 648 | return 'Priority Inversion' | ||
| 649 | |||
| 604 | def __str__(self): | 650 | def __str__(self): |
| 605 | if self.corresp_end_event is None: | 651 | if self.corresp_end_event is None: |
| 606 | return 'Inversion Start (w/o Inversion End)' + self._common_str() \ | 652 | return super(InversionStartEvent, self).__str__() |
| 607 | + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | 653 | return self.get_name() + self._common_str() + ', START=' \ |
| 608 | return 'Priority Inversion' + self._common_str() + ', START=' \ | ||
| 609 | + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) \ | 654 | + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) \ |
| 610 | + ', END=' + util.format_float(self.corresp_end_event.get_time(), Event.NUM_DEC_PLACES) | 655 | + ', END=' + util.format_float(self.corresp_end_event.get_time(), Event.NUM_DEC_PLACES) |
| 611 | 656 | ||
| 657 | def str_long(self): | ||
| 658 | if self.corresp_end_event is None: | ||
| 659 | return super(InversionStartEvent, self).str_long() | ||
| 660 | else : | ||
| 661 | return 'Event Type: ' + self.get_name() + \ | ||
| 662 | '\nTask Name: ' + str(self.get_job().get_task().get_name()) + \ | ||
| 663 | '\n(Task no., Job no.): ' + str((self.get_job().get_task().get_task_no(), \ | ||
| 664 | self.get_job().get_job_no())) + \ | ||
| 665 | '\nCPU: ' + str(self.get_cpu()) + \ | ||
| 666 | '\nStart: ' + str(self.get_time()) + \ | ||
| 667 | '\nEnd: ' + str(self.corresp_end_event.get_time()) | ||
| 668 | |||
| 612 | def scan(self, cur_cpu, switches): | 669 | def scan(self, cur_cpu, switches): |
| 613 | switches[InversionStartEvent] = self | 670 | switches[InversionStartEvent] = self |
| 614 | self.corresp_end_event = None | 671 | self.corresp_end_event = None |
| 615 | 672 | ||
| 616 | # the corresp_end_event should already be set | 673 | # the corresp_end_event should already be set |
| 617 | super(InversionStartEvent, self).scan(cur_cpu, switches) | 674 | super(InversionStartEvent, self).scan(cur_cpu, switches) |
| 618 | 675 | ||
| 619 | def render(self, graph, layer, prev_events, selectable=False): | 676 | def render(self, graph, layer, prev_events, selectable=False): |
| 620 | if layer == self.layer: | 677 | if layer == self.layer: |
| 621 | end_time = None | 678 | end_time = None |
| @@ -625,7 +682,7 @@ class InversionStartEvent(ErrorEvent): | |||
| 625 | clip = AlignMode.RIGHT | 682 | clip = AlignMode.RIGHT |
| 626 | else: | 683 | else: |
| 627 | end_time = self.corresp_end_event.get_time() | 684 | end_time = self.corresp_end_event.get_time() |
| 628 | 685 | ||
| 629 | if layer == self.layer: | 686 | if layer == self.layer: |
| 630 | prev_events[self] = None | 687 | prev_events[self] = None |
| 631 | cpu = self.get_cpu() | 688 | cpu = self.get_cpu() |
| @@ -637,36 +694,47 @@ class InversionStartEvent(ErrorEvent): | |||
| 637 | graph.draw_mini_bar_at_time(self.get_time(), end_time, | 694 | graph.draw_mini_bar_at_time(self.get_time(), end_time, |
| 638 | task_no, cpu, self.get_job().get_job_no(), | 695 | task_no, cpu, self.get_job().get_job_no(), |
| 639 | clip, self.is_selected()) | 696 | clip, self.is_selected()) |
| 640 | 697 | ||
| 641 | 698 | ||
| 642 | class InversionEndEvent(ErrorEvent): | 699 | class InversionEndEvent(ErrorEvent): |
| 643 | def __init__(self, time): | 700 | def __init__(self, time): |
| 644 | super(InversionEndEvent, self).__init__(time, Event.NO_CPU) | 701 | super(InversionEndEvent, self).__init__(time, Event.NO_CPU) |
| 645 | self.layer = Canvas.BOTTOM_LAYER | 702 | self.layer = Canvas.BOTTOM_LAYER |
| 646 | self.corresp_start_event = None | 703 | self.corresp_start_event = None |
| 647 | 704 | ||
| 705 | def get_name(self): | ||
| 706 | if self.corresp_start_event is None: | ||
| 707 | return 'Inversion End (w/o Inversion Start)' | ||
| 708 | else: | ||
| 709 | return 'Priority Inversion' | ||
| 710 | |||
| 648 | def __str__(self): | 711 | def __str__(self): |
| 649 | if self.corresp_start_event is None: | 712 | if self.corresp_start_event is None: |
| 650 | return 'Inversion End (w/o Inversion Start)' + self._common_str() \ | 713 | return super(InversionEndEvent, self).__str__() |
| 651 | + ', TIME=' + util.format_float(self.get_time(), Event.NUM_DEC_PLACES) | 714 | |
| 652 | |||
| 653 | return str(self.corresp_start_event) | 715 | return str(self.corresp_start_event) |
| 654 | 716 | ||
| 717 | def str_long(self): | ||
| 718 | if self.corresp_start_event is None: | ||
| 719 | return super(InversionEndEvent, self).str_long() | ||
| 720 | |||
| 721 | return self.corresp_start_event.str_long() | ||
| 722 | |||
| 655 | def scan(self, cur_cpu, switches): | 723 | def scan(self, cur_cpu, switches): |
| 656 | self.corresp_start_event = switches[InversionStartEvent] | 724 | self.corresp_start_event = switches[InversionStartEvent] |
| 657 | 725 | ||
| 658 | cur_cpu[0] = Event.NO_CPU | 726 | cur_cpu[0] = Event.NO_CPU |
| 659 | switches[InversionStartEvent] = None | 727 | switches[InversionStartEvent] = None |
| 660 | 728 | ||
| 661 | if self.corresp_start_event is not None: | 729 | if self.corresp_start_event is not None: |
| 662 | self.corresp_start_event.corresp_end_event = self | 730 | self.corresp_start_event.corresp_end_event = self |
| 663 | 731 | ||
| 664 | if self.corresp_start_event is None: | 732 | if self.corresp_start_event is None: |
| 665 | self.erroneous = True | 733 | self.erroneous = True |
| 666 | #print "inversion end was not matched by a corresponding inversion start" | 734 | #print "inversion end was not matched by a corresponding inversion start" |
| 667 | 735 | ||
| 668 | super(InversionEndEvent, self).scan(cur_cpu, switches) | 736 | super(InversionEndEvent, self).scan(cur_cpu, switches) |
| 669 | 737 | ||
| 670 | def render(self, graph, layer, prev_events, selectable=False): | 738 | def render(self, graph, layer, prev_events, selectable=False): |
| 671 | if self.corresp_start_event is None: | 739 | if self.corresp_start_event is None: |
| 672 | # We never found a corresponding start event. In that case, we can assume it lies | 740 | # We never found a corresponding start event. In that case, we can assume it lies |
| @@ -688,27 +756,27 @@ class InversionEndEvent(ErrorEvent): | |||
| 688 | if self.corresp_start_event in prev_events: | 756 | if self.corresp_start_event in prev_events: |
| 689 | return # already rendered the bar | 757 | return # already rendered the bar |
| 690 | self.corresp_start_event.render(graph, layer, prev_events, selectable) | 758 | self.corresp_start_event.render(graph, layer, prev_events, selectable) |
| 691 | 759 | ||
| 692 | class InversionDummy(DummyEvent): | 760 | class InversionDummy(DummyEvent): |
| 693 | def __init__(self, time, cpu): | 761 | def __init__(self, time, cpu): |
| 694 | super(InversionDummy, self).__init__(time, Event.NO_CPU) | 762 | super(InversionDummy, self).__init__(time, Event.NO_CPU) |
| 695 | self.layer = Canvas.BOTTOM_LAYER | 763 | self.layer = Canvas.BOTTOM_LAYER |
| 696 | 764 | ||
| 697 | def render(self, graph, layer, prev_events, selectable=False): | 765 | def render(self, graph, layer, prev_events, selectable=False): |
| 698 | if self.corresp_start_event is None: | 766 | if self.corresp_start_event is None: |
| 699 | if self.corresp_end_event in prev_events: | 767 | if self.corresp_end_event in prev_events: |
| 700 | return # we have already been rendered | 768 | return # we have already been rendered |
| 701 | self.corresp_end_event.render(graph, layer, prev_events, selectable) | 769 | self.corresp_end_event.render(graph, layer, prev_events, selectable) |
| 702 | else: | 770 | else: |
| 703 | if self.corresp_start_event in prev_events: | 771 | if self.corresp_start_event in prev_events: |
| 704 | return # we have already been rendered | 772 | return # we have already been rendered |
| 705 | self.corresp_start_event.render(graph, layer, prev_events, selectable) | 773 | self.corresp_start_event.render(graph, layer, prev_events, selectable) |
| 706 | 774 | ||
| 707 | class IsRunningDummy(DummyEvent): | 775 | class IsRunningDummy(DummyEvent): |
| 708 | def __init__(self, time, cpu): | 776 | def __init__(self, time, cpu): |
| 709 | super(IsRunningDummy, self).__init__(time, Event.NO_CPU) | 777 | super(IsRunningDummy, self).__init__(time, Event.NO_CPU) |
| 710 | self.layer = Canvas.BOTTOM_LAYER | 778 | self.layer = Canvas.BOTTOM_LAYER |
| 711 | 779 | ||
| 712 | def render(self, graph, layer, prev_events, selectable=False): | 780 | def render(self, graph, layer, prev_events, selectable=False): |
| 713 | if self.corresp_start_event is None: | 781 | if self.corresp_start_event is None: |
| 714 | if self.corresp_end_event in prev_events: | 782 | if self.corresp_end_event in prev_events: |
diff --git a/unit_trace/viz/viewer.py b/unit_trace/viz/viewer.py index 9b8502a..4d1fb7f 100644 --- a/unit_trace/viz/viewer.py +++ b/unit_trace/viz/viewer.py | |||
| @@ -3,8 +3,8 @@ | |||
| 3 | """GUI stuff.""" | 3 | """GUI stuff.""" |
| 4 | 4 | ||
| 5 | from schedule import * | 5 | from schedule import * |
| 6 | |||
| 7 | from renderer import * | 6 | from renderer import * |
| 7 | from windows import * | ||
| 8 | 8 | ||
| 9 | import pygtk | 9 | import pygtk |
| 10 | import gtk | 10 | import gtk |
| @@ -13,145 +13,157 @@ import copy | |||
| 13 | 13 | ||
| 14 | class GraphContextMenu(gtk.Menu): | 14 | class GraphContextMenu(gtk.Menu): |
| 15 | MAX_STR_LEN = 80 | 15 | MAX_STR_LEN = 80 |
| 16 | 16 | ||
| 17 | def __init__(self, selected): | 17 | def __init__(self, selected, info_win): |
| 18 | super(GraphContextMenu, self).__init__() | 18 | super(GraphContextMenu, self).__init__() |
| 19 | 19 | ||
| 20 | self.info_win = info_win | ||
| 21 | |||
| 20 | if not selected: | 22 | if not selected: |
| 21 | item = gtk.MenuItem("(No events selected)") | 23 | item = gtk.MenuItem("(No events selected)") |
| 22 | item.set_sensitive(False) | 24 | item.set_sensitive(False) |
| 23 | self.append(item) | 25 | self.append(item) |
| 24 | item.show() | 26 | item.show() |
| 25 | else: | 27 | else: |
| 26 | for layer in selected: | 28 | for layer in selected: |
| 27 | for event in selected[layer]: | 29 | for event in selected[layer]: |
| 28 | string = str(event) | 30 | string = str(event) |
| 29 | if len(string) > GraphContextMenu.MAX_STR_LEN - 3: | 31 | if len(string) > GraphContextMenu.MAX_STR_LEN - 3: |
| 30 | string = string[:GraphContextMenu.MAX_STR_LEN - 3] + '...' | 32 | string = string[:GraphContextMenu.MAX_STR_LEN - 3] + '...' |
| 31 | item = gtk.MenuItem(string) | 33 | item = gtk.MenuItem(string) |
| 34 | item.connect('activate', self.update_info_window, event) | ||
| 32 | self.append(item) | 35 | self.append(item) |
| 33 | item.show() | 36 | item.show() |
| 34 | 37 | ||
| 38 | def update_info_window(self, widget, data): | ||
| 39 | self.info_win.set_event(data) | ||
| 40 | self.info_win.present() | ||
| 41 | |||
| 35 | class GraphArea(gtk.DrawingArea): | 42 | class GraphArea(gtk.DrawingArea): |
| 36 | HORIZ_PAGE_SCROLL_FACTOR = 10.8 | 43 | HORIZ_PAGE_SCROLL_FACTOR = 10.8 |
| 37 | HORIZ_STEP_SCROLL_FACTOR = 0.8 | 44 | HORIZ_STEP_SCROLL_FACTOR = 0.8 |
| 38 | VERT_PAGE_SCROLL_FACTOR = 3.0 | 45 | VERT_PAGE_SCROLL_FACTOR = 3.0 |
| 39 | VERT_STEP_SCROLL_FACTOR = 0.5 | 46 | VERT_STEP_SCROLL_FACTOR = 0.5 |
| 40 | 47 | ||
| 41 | REFRESH_INFLATION_FACTOR = 4.0 | 48 | REFRESH_INFLATION_FACTOR = 4.0 |
| 42 | 49 | ||
| 50 | MIN_ZOOM_OUT = 0.25 | ||
| 51 | MAX_ZOOM_IN = 4.0 | ||
| 52 | ZOOM_INCR = 0.25 | ||
| 53 | |||
| 43 | def __init__(self, renderer): | 54 | def __init__(self, renderer): |
| 44 | super(GraphArea, self).__init__() | 55 | super(GraphArea, self).__init__() |
| 45 | 56 | ||
| 46 | self.renderer = renderer | 57 | self.renderer = renderer |
| 47 | 58 | ||
| 48 | self.cur_x = 0 | 59 | self.cur_x = 0 |
| 49 | self.cur_y = 0 | 60 | self.cur_y = 0 |
| 50 | self.width = 0 | 61 | self.width = 0 |
| 51 | self.height = 0 | 62 | self.height = 0 |
| 52 | self.scale = 1.0 | 63 | self.scale = 1.0 |
| 53 | 64 | ||
| 54 | self.set_set_scroll_adjustments_signal('set-scroll-adjustments') | 65 | self.set_set_scroll_adjustments_signal('set-scroll-adjustments') |
| 55 | 66 | ||
| 56 | self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | | 67 | self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | |
| 57 | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.EXPOSURE_MASK) | 68 | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.EXPOSURE_MASK) |
| 58 | 69 | ||
| 59 | self.band_rect = None | 70 | self.band_rect = None |
| 60 | self.ctrl_clicked = False | 71 | self.ctrl_clicked = False |
| 61 | self.last_selected = {} | 72 | self.last_selected = {} |
| 62 | self.dirtied_regions = [] | 73 | self.dirtied_regions = [] |
| 63 | 74 | ||
| 64 | self.connect('expose-event', self.expose) | 75 | self.connect('expose-event', self.expose) |
| 65 | self.connect('size-allocate', self.size_allocate) | 76 | self.connect('size-allocate', self.size_allocate) |
| 66 | self.connect('set-scroll-adjustments', self.set_scroll_adjustments) | 77 | self.connect('set-scroll-adjustments', self.set_scroll_adjustments) |
| 67 | self.connect('button-press-event', self.button_press) | 78 | self.connect('button-press-event', self.button_press) |
| 68 | self.connect('button-release-event', self.button_release) | 79 | self.connect('button-release-event', self.button_release) |
| 69 | self.connect('motion-notify-event', self.motion_notify) | 80 | self.connect('motion-notify-event', self.motion_notify) |
| 70 | 81 | ||
| 71 | def expose(self, widget, expose_event, data=None): | 82 | def expose(self, widget, expose_event, data=None): |
| 72 | ctx = widget.window.cairo_create() | 83 | ctx = widget.window.cairo_create() |
| 73 | graph = self.renderer.get_graph() | 84 | graph = self.renderer.get_graph() |
| 74 | graph.update_view(self.cur_x, self.cur_y, self.width, self.height, self.scale, ctx) | 85 | graph.update_view(self.cur_x, self.cur_y, self.width, self.height, self.scale, ctx) |
| 75 | 86 | ||
| 76 | # We ourselves didn't update dirtied_regions, so this means that X or the | 87 | # If X caused the expose event, we need to update the entire area, not just the |
| 77 | # window manager must have caused the expose event. So just update the | 88 | # changes we might have made. An expose event caused by X needs to take priority |
| 78 | # expose_event's bounding area. | 89 | # over any expose events caused by updates to the state of the graph because |
| 79 | if not self.dirtied_regions or expose_event.send_event: | 90 | # the areas we marked as dirty only include the state changes, which is completely |
| 91 | # unrelated to the area that X indicates must be updated. | ||
| 92 | if expose_event.type == gtk.gdk.EXPOSE: | ||
| 80 | self.dirtied_regions = [(expose_event.area.x, expose_event.area.y, | 93 | self.dirtied_regions = [(expose_event.area.x, expose_event.area.y, |
| 81 | expose_event.area.width, expose_event.area.height)] | 94 | expose_event.area.width, expose_event.area.height)] |
| 82 | 95 | ||
| 83 | graph.render_surface(self.renderer.get_schedule(), self.dirtied_regions) | 96 | graph.render_surface(self.renderer.get_schedule(), self.dirtied_regions) |
| 84 | 97 | ||
| 85 | # render dragging band rectangle, if there is one | 98 | # render dragging band rectangle, if there is one |
| 86 | if self.band_rect is not None: | 99 | if self.band_rect is not None: |
| 87 | x, y, width, height = self.band_rect | 100 | x, y, width, height = self.band_rect |
| 88 | thickness = GraphFormat.BAND_THICKNESS | 101 | thickness = GraphFormat.BAND_THICKNESS |
| 89 | color = GraphFormat.BAND_COLOR | 102 | color = GraphFormat.BAND_COLOR |
| 90 | 103 | ||
| 91 | ctx.rectangle(x, y, width, height) | 104 | ctx.rectangle(x, y, width, height) |
| 92 | ctx.set_line_width(thickness) | 105 | ctx.set_line_width(thickness) |
| 93 | ctx.set_source_rgb(color[0], color[1], color[2]) | 106 | ctx.set_source_rgb(color[0], color[1], color[2]) |
| 94 | ctx.stroke() | 107 | ctx.stroke() |
| 95 | 108 | ||
| 96 | self.dirtied_regions = [] | 109 | self.dirtied_regions = [] |
| 97 | 110 | ||
| 98 | def get_renderer(self): | 111 | def get_renderer(self): |
| 99 | return self.renderer | 112 | return self.renderer |
| 100 | 113 | ||
| 101 | def get_graph(self): | 114 | def get_graph(self): |
| 102 | return self.renderer.get_graph() | 115 | return self.renderer.get_graph() |
| 103 | 116 | ||
| 104 | MIN_ZOOM_OUT = 0.25 | 117 | def get_schedule(self): |
| 105 | MAX_ZOOM_IN = 4.0 | 118 | return self.renderer.get_schedule() |
| 106 | ZOOM_INCR = 0.25 | 119 | |
| 107 | |||
| 108 | def zoom_in(self): | 120 | def zoom_in(self): |
| 109 | scale = self.scale + GraphArea.ZOOM_INCR | 121 | scale = self.scale + GraphArea.ZOOM_INCR |
| 110 | if scale > GraphArea.MAX_ZOOM_IN: | 122 | if scale > GraphArea.MAX_ZOOM_IN: |
| 111 | scale = GraphArea.MAX_ZOOM_IN | 123 | scale = GraphArea.MAX_ZOOM_IN |
| 112 | self.set_scale(scale) | 124 | self.set_scale(scale) |
| 113 | 125 | ||
| 114 | def zoom_out(self): | 126 | def zoom_out(self): |
| 115 | scale = self.scale - GraphArea.ZOOM_INCR | 127 | scale = self.scale - GraphArea.ZOOM_INCR |
| 116 | if scale < GraphArea.MIN_ZOOM_OUT: | 128 | if scale < GraphArea.MIN_ZOOM_OUT: |
| 117 | scale = GraphArea.MIN_ZOOM_OUT | 129 | scale = GraphArea.MIN_ZOOM_OUT |
| 118 | self.set_scale(scale) | 130 | self.set_scale(scale) |
| 119 | 131 | ||
| 120 | def set_scale(self, scale): | 132 | def set_scale(self, scale): |
| 121 | if scale == self.scale: | 133 | if scale == self.scale: |
| 122 | return | 134 | return |
| 123 | 135 | ||
| 124 | self.scale = scale | 136 | self.scale = scale |
| 125 | self._dirty(0, 0, self.width, self.height) | 137 | self._dirty(0, 0, self.width, self.height) |
| 126 | 138 | ||
| 127 | def set_scroll_adjustments(self, widget, horizontal, vertical, data=None): | 139 | def set_scroll_adjustments(self, widget, horizontal, vertical, data=None): |
| 128 | graph = self.renderer.get_graph() | 140 | graph = self.renderer.get_graph() |
| 129 | width = graph.get_width() | 141 | width = graph.get_width() |
| 130 | height = graph.get_height() | 142 | height = graph.get_height() |
| 131 | 143 | ||
| 132 | self.horizontal = horizontal | 144 | self.horizontal = horizontal |
| 133 | self.vertical = vertical | 145 | self.vertical = vertical |
| 134 | self.config_scrollbars(self.cur_x, self.cur_y) | 146 | self.config_scrollbars(self.cur_x, self.cur_y) |
| 135 | 147 | ||
| 136 | if self.horizontal is not None: | 148 | if self.horizontal is not None: |
| 137 | self.horizontal.connect('value-changed', self.horizontal_value_changed) | 149 | self.horizontal.connect('value-changed', self.horizontal_value_changed) |
| 138 | if self.vertical is not None: | 150 | if self.vertical is not None: |
| 139 | self.vertical.connect('value-changed', self.vertical_value_changed) | 151 | self.vertical.connect('value-changed', self.vertical_value_changed) |
| 140 | 152 | ||
| 141 | def horizontal_value_changed(self, adjustment): | 153 | def horizontal_value_changed(self, adjustment): |
| 142 | self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width()) | 154 | self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width()) |
| 143 | self.cur_x = max(adjustment.value, 0.0) | 155 | self.cur_x = max(adjustment.value, 0.0) |
| 144 | 156 | ||
| 145 | self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True) | 157 | self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True) |
| 146 | self._dirty(0, 0, self.width, self.height) | 158 | self._dirty(0, 0, self.width, self.height) |
| 147 | 159 | ||
| 148 | def vertical_value_changed(self, adjustment): | 160 | def vertical_value_changed(self, adjustment): |
| 149 | self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height()) | 161 | self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height()) |
| 150 | self.cur_y = max(adjustment.value, 0.0) | 162 | self.cur_y = max(adjustment.value, 0.0) |
| 151 | 163 | ||
| 152 | self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True) | 164 | self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True) |
| 153 | self._dirty(0, 0, self.width, self.height) | 165 | self._dirty(0, 0, self.width, self.height) |
| 154 | 166 | ||
| 155 | def size_allocate(self, widget, allocation): | 167 | def size_allocate(self, widget, allocation): |
| 156 | self.width = allocation.width | 168 | self.width = allocation.width |
| 157 | self.height = allocation.height | 169 | self.height = allocation.height |
| @@ -161,7 +173,7 @@ class GraphArea(gtk.DrawingArea): | |||
| 161 | graph = self.renderer.get_graph() | 173 | graph = self.renderer.get_graph() |
| 162 | width = graph.get_width() | 174 | width = graph.get_width() |
| 163 | height = graph.get_height() | 175 | height = graph.get_height() |
| 164 | 176 | ||
| 165 | if self.horizontal is not None: | 177 | if self.horizontal is not None: |
| 166 | self.horizontal.set_all(hvalue, 0.0, width + self.width, | 178 | self.horizontal.set_all(hvalue, 0.0, width + self.width, |
| 167 | graph.get_attrs().maj_sep * GraphArea.HORIZ_STEP_SCROLL_FACTOR, | 179 | graph.get_attrs().maj_sep * GraphArea.HORIZ_STEP_SCROLL_FACTOR, |
| @@ -169,8 +181,8 @@ class GraphArea(gtk.DrawingArea): | |||
| 169 | if self.vertical is not None: | 181 | if self.vertical is not None: |
| 170 | self.vertical.set_all(vvalue, 0.0, height + self.height, | 182 | self.vertical.set_all(vvalue, 0.0, height + self.height, |
| 171 | graph.get_attrs().y_item_size * GraphArea.VERT_STEP_SCROLL_FACTOR, | 183 | graph.get_attrs().y_item_size * GraphArea.VERT_STEP_SCROLL_FACTOR, |
| 172 | graph.get_attrs().y_item_size * GraphArea.VERT_PAGE_SCROLL_FACTOR, self.height) | 184 | graph.get_attrs().y_item_size * GraphArea.VERT_PAGE_SCROLL_FACTOR, self.height) |
| 173 | 185 | ||
| 174 | def refresh_events(self, sender, new, old, replace): | 186 | def refresh_events(self, sender, new, old, replace): |
| 175 | """Even if the selected areas change on one graph, they change | 187 | """Even if the selected areas change on one graph, they change |
| 176 | everywhere, and different events might be in different regions on | 188 | everywhere, and different events might be in different regions on |
| @@ -182,9 +194,9 @@ class GraphArea(gtk.DrawingArea): | |||
| 182 | refreshes the drawing of the graph that requested the change.""" | 194 | refreshes the drawing of the graph that requested the change.""" |
| 183 | if self is not sender: | 195 | if self is not sender: |
| 184 | self.renderer.get_graph().render_events(new, True) | 196 | self.renderer.get_graph().render_events(new, True) |
| 185 | 197 | ||
| 186 | self._tag_events(new) | 198 | self._tag_events(new) |
| 187 | 199 | ||
| 188 | if self is sender: | 200 | if self is sender: |
| 189 | self._copy_tags(old) | 201 | self._copy_tags(old) |
| 190 | self._dirty_events(new) | 202 | self._dirty_events(new) |
| @@ -194,14 +206,14 @@ class GraphArea(gtk.DrawingArea): | |||
| 194 | else: | 206 | else: |
| 195 | self.renderer.get_schedule().remove_selected(old) | 207 | self.renderer.get_schedule().remove_selected(old) |
| 196 | self.renderer.get_schedule().add_selected(new) | 208 | self.renderer.get_schedule().add_selected(new) |
| 197 | 209 | ||
| 198 | def _find_max_layer(self, regions): | 210 | def _find_max_layer(self, regions): |
| 199 | max_layer = Canvas.BOTTOM_LAYER | 211 | max_layer = Canvas.BOTTOM_LAYER |
| 200 | for event in regions: | 212 | for event in regions: |
| 201 | if event.get_layer() > max_layer: | 213 | if event.get_layer() > max_layer: |
| 202 | max_layer = event.get_layer() | 214 | max_layer = event.get_layer() |
| 203 | return max_layer | 215 | return max_layer |
| 204 | 216 | ||
| 205 | def _dirty_events(self, events): | 217 | def _dirty_events(self, events): |
| 206 | # if an event changed selected status, update the bounding area | 218 | # if an event changed selected status, update the bounding area |
| 207 | for layer in events: | 219 | for layer in events: |
| @@ -212,7 +224,7 @@ class GraphArea(gtk.DrawingArea): | |||
| 212 | width * self.scale, | 224 | width * self.scale, |
| 213 | height * self.scale, | 225 | height * self.scale, |
| 214 | GraphFormat.BORDER_THICKNESS * self.scale) | 226 | GraphFormat.BORDER_THICKNESS * self.scale) |
| 215 | 227 | ||
| 216 | def _tag_events(self, selected): | 228 | def _tag_events(self, selected): |
| 217 | """Some of the events in the collection of selected events might be new. | 229 | """Some of the events in the collection of selected events might be new. |
| 218 | In this case, these events are not yet associated with the region on | 230 | In this case, these events are not yet associated with the region on |
| @@ -224,7 +236,7 @@ class GraphArea(gtk.DrawingArea): | |||
| 224 | # note that each graph has its own region associated | 236 | # note that each graph has its own region associated |
| 225 | # with the event | 237 | # with the event |
| 226 | selected[layer][event][self] = graph.get_sel_region(event) | 238 | selected[layer][event][self] = graph.get_sel_region(event) |
| 227 | 239 | ||
| 228 | def _copy_tags(self, selected): | 240 | def _copy_tags(self, selected): |
| 229 | """When we want to specify a collection of selected events to perform | 241 | """When we want to specify a collection of selected events to perform |
| 230 | an operation on, we usually do not know ahead of time what regions (in | 242 | an operation on, we usually do not know ahead of time what regions (in |
| @@ -235,21 +247,21 @@ class GraphArea(gtk.DrawingArea): | |||
| 235 | for layer in selected: | 247 | for layer in selected: |
| 236 | for event in selected[layer]: | 248 | for event in selected[layer]: |
| 237 | selected[layer][event] = cur_selected[layer][event] | 249 | selected[layer][event] = cur_selected[layer][event] |
| 238 | 250 | ||
| 239 | def _select_event(self, coll, event): | 251 | def _select_event(self, coll, event): |
| 240 | if event.get_layer() not in coll: | 252 | if event.get_layer() not in coll: |
| 241 | coll[event.get_layer()] = {} | 253 | coll[event.get_layer()] = {} |
| 242 | if event not in coll[event.get_layer()]: | 254 | if event not in coll[event.get_layer()]: |
| 243 | coll[event.get_layer()][event] = {} | 255 | coll[event.get_layer()][event] = {} |
| 244 | 256 | ||
| 245 | def motion_notify(self, widget, motion_event, data=None): | 257 | def motion_notify(self, widget, motion_event, data=None): |
| 246 | msg = None | 258 | msg = None |
| 247 | 259 | ||
| 248 | graph = self.renderer.get_graph() | 260 | graph = self.renderer.get_graph() |
| 249 | 261 | ||
| 250 | graph.render_surface(self.renderer.get_schedule(), [(motion_event.x, motion_event.y, | 262 | graph.render_surface(self.renderer.get_schedule(), [(motion_event.x, motion_event.y, |
| 251 | 0, 0)], True) | 263 | 0, 0)], True) |
| 252 | 264 | ||
| 253 | just_selected = graph.get_selected_regions(motion_event.x, motion_event.y, 0, 0) | 265 | just_selected = graph.get_selected_regions(motion_event.x, motion_event.y, 0, 0) |
| 254 | was_selected = self.renderer.get_schedule().get_selected() | 266 | was_selected = self.renderer.get_schedule().get_selected() |
| 255 | if not just_selected: | 267 | if not just_selected: |
| @@ -257,38 +269,38 @@ class GraphArea(gtk.DrawingArea): | |||
| 257 | the_event = None | 269 | the_event = None |
| 258 | else: | 270 | else: |
| 259 | max_layer = self._find_max_layer(just_selected) | 271 | max_layer = self._find_max_layer(just_selected) |
| 260 | 272 | ||
| 261 | for event in just_selected: | 273 | for event in just_selected: |
| 262 | if event.get_layer() == max_layer: | 274 | if event.get_layer() == max_layer: |
| 263 | the_event = event | 275 | the_event = event |
| 264 | break | 276 | break |
| 265 | 277 | ||
| 266 | msg = str(the_event) | 278 | msg = str(the_event) |
| 267 | 279 | ||
| 268 | self.emit('update-event-description', the_event, msg) | 280 | self.emit('update-event-description', the_event, msg) |
| 269 | 281 | ||
| 270 | if self.band_rect is not None: | 282 | if self.band_rect is not None: |
| 271 | remove_selected = {} | 283 | remove_selected = {} |
| 272 | add_selected = {} | 284 | add_selected = {} |
| 273 | 285 | ||
| 274 | # dragging a rectangle | 286 | # dragging a rectangle |
| 275 | x = self.band_rect[0] | 287 | x = self.band_rect[0] |
| 276 | y = self.band_rect[1] | 288 | y = self.band_rect[1] |
| 277 | width = motion_event.x - self.band_rect[0] | 289 | width = motion_event.x - self.band_rect[0] |
| 278 | height = motion_event.y - self.band_rect[1] | 290 | height = motion_event.y - self.band_rect[1] |
| 279 | old_x, old_y, old_width, old_height = self.band_rect | 291 | old_x, old_y, old_width, old_height = self.band_rect |
| 280 | 292 | ||
| 281 | x_p, y_p, width_p, height_p = self._positivify(x, y, width, height) | 293 | x_p, y_p, width_p, height_p = self._positivify(x, y, width, height) |
| 282 | old_x_p, old_y_p, old_width_p, old_height_p = self._positivify(old_x, old_y, old_width, old_height) | 294 | old_x_p, old_y_p, old_width_p, old_height_p = self._positivify(old_x, old_y, old_width, old_height) |
| 283 | x_p, y_p, width_p, height_p = int(x_p), int(y_p), int(width_p), int(height_p) | 295 | x_p, y_p, width_p, height_p = int(x_p), int(y_p), int(width_p), int(height_p) |
| 284 | old_x_p, old_y_p, old_width_p, old_height_p = int(old_x_p), int(old_y_p), int(old_width_p), int(old_height_p) | 296 | old_x_p, old_y_p, old_width_p, old_height_p = int(old_x_p), int(old_y_p), int(old_width_p), int(old_height_p) |
| 285 | 297 | ||
| 286 | new_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p)) | 298 | new_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p)) |
| 287 | old_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p)) | 299 | old_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p)) |
| 288 | 300 | ||
| 289 | # To find the events that should be deselected and the new events that should be selected, compute | 301 | # To find the events that should be deselected and the new events that should be selected, compute |
| 290 | # the set differences between the old and new selection rectangles | 302 | # the set differences between the old and new selection rectangles |
| 291 | 303 | ||
| 292 | remove_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p)) | 304 | remove_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p)) |
| 293 | remove_reg.subtract(new_reg) | 305 | remove_reg.subtract(new_reg) |
| 294 | dirty_list = [] | 306 | dirty_list = [] |
| @@ -300,7 +312,7 @@ class GraphArea(gtk.DrawingArea): | |||
| 300 | for event in graph.get_selected_regions(rx, ry, rwidth, rheight): | 312 | for event in graph.get_selected_regions(rx, ry, rwidth, rheight): |
| 301 | if event.get_layer() in was_selected and event in was_selected[event.get_layer()]: | 313 | if event.get_layer() in was_selected and event in was_selected[event.get_layer()]: |
| 302 | self._select_event(remove_selected, event) | 314 | self._select_event(remove_selected, event) |
| 303 | 315 | ||
| 304 | add_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p)) | 316 | add_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p)) |
| 305 | add_reg.subtract(old_reg) | 317 | add_reg.subtract(old_reg) |
| 306 | dirty_list = [(x_p, y_p, width_p, 0), (x_p, y_p, 0, height_p), | 318 | dirty_list = [(x_p, y_p, width_p, 0), (x_p, y_p, 0, height_p), |
| @@ -312,38 +324,38 @@ class GraphArea(gtk.DrawingArea): | |||
| 312 | rx, ry, rwidth, rheight = rect | 324 | rx, ry, rwidth, rheight = rect |
| 313 | for event in graph.get_selected_regions(rx, ry, rwidth, rheight): | 325 | for event in graph.get_selected_regions(rx, ry, rwidth, rheight): |
| 314 | self._select_event(add_selected, event) | 326 | self._select_event(add_selected, event) |
| 315 | 327 | ||
| 316 | self.band_rect = x, y, width, height | 328 | self.band_rect = x, y, width, height |
| 317 | self.emit('request-refresh-events', self, add_selected, remove_selected, False) | 329 | self.emit('request-refresh-events', self, add_selected, remove_selected, False) |
| 318 | 330 | ||
| 319 | self._dirty_rect_border(old_x, old_y, old_width, old_height, GraphFormat.BAND_THICKNESS) | 331 | self._dirty_rect_border(old_x, old_y, old_width, old_height, GraphFormat.BAND_THICKNESS) |
| 320 | self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS) | 332 | self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS) |
| 321 | 333 | ||
| 322 | def button_press(self, widget, button_event, data=None): | 334 | def button_press(self, widget, button_event, data=None): |
| 323 | graph = self.renderer.get_graph() | 335 | graph = self.renderer.get_graph() |
| 324 | 336 | ||
| 325 | self.ctrl_clicked = button_event.state & gtk.gdk.CONTROL_MASK | 337 | self.ctrl_clicked = button_event.state & gtk.gdk.CONTROL_MASK |
| 326 | 338 | ||
| 327 | if button_event.button == 1: | 339 | if button_event.button == 1: |
| 328 | self.left_button_start_coor = (button_event.x, button_event.y) | 340 | self.left_button_start_coor = (button_event.x, button_event.y) |
| 329 | graph.render_surface(self.renderer.get_schedule(), \ | 341 | graph.render_surface(self.renderer.get_schedule(), \ |
| 330 | [(button_event.x, button_event.y, 0, 0)], True) | 342 | [(button_event.x, button_event.y, 0, 0)], True) |
| 331 | 343 | ||
| 332 | just_selected = graph.get_selected_regions(button_event.x, button_event.y, 0, 0) | 344 | just_selected = graph.get_selected_regions(button_event.x, button_event.y, 0, 0) |
| 333 | 345 | ||
| 334 | max_layer = self._find_max_layer(just_selected) | 346 | max_layer = self._find_max_layer(just_selected) |
| 335 | 347 | ||
| 336 | was_selected = self.renderer.get_schedule().get_selected() | 348 | was_selected = self.renderer.get_schedule().get_selected() |
| 337 | if not self.ctrl_clicked: | 349 | if not self.ctrl_clicked: |
| 338 | new_now_selected = {} | 350 | new_now_selected = {} |
| 339 | 351 | ||
| 340 | more_than_one = 0 | 352 | more_than_one = 0 |
| 341 | for layer in was_selected: | 353 | for layer in was_selected: |
| 342 | for event in was_selected[layer]: | 354 | for event in was_selected[layer]: |
| 343 | more_than_one += 1 | 355 | more_than_one += 1 |
| 344 | if more_than_one > 1: | 356 | if more_than_one > 1: |
| 345 | break | 357 | break |
| 346 | 358 | ||
| 347 | # only select those events which were in the top layer (it's | 359 | # only select those events which were in the top layer (it's |
| 348 | # not intuitive to click something and then have something | 360 | # not intuitive to click something and then have something |
| 349 | # below it get selected). Also, clicking something that | 361 | # below it get selected). Also, clicking something that |
| @@ -353,12 +365,12 @@ class GraphArea(gtk.DrawingArea): | |||
| 353 | if not (more_than_one == 1 and event in was_selected): | 365 | if not (more_than_one == 1 and event in was_selected): |
| 354 | self._select_event(new_now_selected, event) | 366 | self._select_event(new_now_selected, event) |
| 355 | break # only pick one event when just clicking | 367 | break # only pick one event when just clicking |
| 356 | 368 | ||
| 357 | self.emit('request-refresh-events', self, new_now_selected, was_selected, True) | 369 | self.emit('request-refresh-events', self, new_now_selected, was_selected, True) |
| 358 | else: | 370 | else: |
| 359 | remove_selected = {} | 371 | remove_selected = {} |
| 360 | add_selected = {} | 372 | add_selected = {} |
| 361 | 373 | ||
| 362 | for event in just_selected: | 374 | for event in just_selected: |
| 363 | layer = event.get_layer() | 375 | layer = event.get_layer() |
| 364 | if layer == max_layer: | 376 | if layer == max_layer: |
| @@ -367,39 +379,45 @@ class GraphArea(gtk.DrawingArea): | |||
| 367 | else: | 379 | else: |
| 368 | self._select_event(add_selected, event) | 380 | self._select_event(add_selected, event) |
| 369 | break # again, only pick one event because we are just clicking | 381 | break # again, only pick one event because we are just clicking |
| 370 | 382 | ||
| 371 | self.emit('request-refresh-events', self, add_selected, remove_selected, False) | 383 | self.emit('request-refresh-events', self, add_selected, remove_selected, False) |
| 372 | 384 | ||
| 373 | if self.band_rect is None: | 385 | if self.band_rect is None: |
| 374 | self.band_rect = (button_event.x, button_event.y, 0, 0) | 386 | self.band_rect = (button_event.x, button_event.y, 0, 0) |
| 375 | 387 | ||
| 376 | elif button_event.button == 3: | 388 | elif button_event.button == 3: |
| 377 | self._release_band() | 389 | self._release_band() |
| 378 | self.emit('request-context-menu', button_event, self.renderer.get_schedule().get_selected()) | 390 | self.emit('request-context-menu', button_event, self.renderer.get_schedule().get_selected()) |
| 379 | 391 | ||
| 380 | def button_release(self, widget, button_event, data=None): | 392 | def button_release(self, widget, button_event, data=None): |
| 381 | self.ctrl_clicked = False | 393 | self.ctrl_clicked = False |
| 382 | 394 | ||
| 383 | if button_event.button == 1: | 395 | if button_event.button == 1: |
| 384 | self._release_band() | 396 | self._release_band() |
| 385 | 397 | ||
| 398 | def get_width(self): | ||
| 399 | return self.width | ||
| 400 | |||
| 401 | def get_height(self): | ||
| 402 | return self.height | ||
| 403 | |||
| 386 | def _release_band(self): | 404 | def _release_band(self): |
| 387 | if self.band_rect is not None: | 405 | if self.band_rect is not None: |
| 388 | x, y, width, height = self.band_rect | 406 | x, y, width, height = self.band_rect |
| 389 | self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS) | 407 | self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS) |
| 390 | self.band_rect = None | 408 | self.band_rect = None |
| 391 | 409 | ||
| 392 | def _dirty(self, x, y, width, height): | 410 | def _dirty(self, x, y, width, height): |
| 393 | x = max(int(math.floor(x)), 0) | 411 | x = max(int(math.floor(x)), 0) |
| 394 | y = max(int(math.floor(y)), 0) | 412 | y = max(int(math.floor(y)), 0) |
| 395 | width = min(int(math.ceil(width)), self.width) | 413 | width = min(int(math.ceil(width)), self.width) |
| 396 | height = min(int(math.ceil(height)), self.height) | 414 | height = min(int(math.ceil(height)), self.height) |
| 397 | 415 | ||
| 398 | self.dirtied_regions.append((x, y, width, height)) | 416 | self.dirtied_regions.append((x, y, width, height)) |
| 399 | 417 | ||
| 400 | rect = gtk.gdk.Rectangle(x, y, width, height) | 418 | rect = gtk.gdk.Rectangle(x, y, width, height) |
| 401 | self.window.invalidate_rect(rect, True) | 419 | self.window.invalidate_rect(rect, True) |
| 402 | 420 | ||
| 403 | def _dirty_inflate(self, x, y, width, height, thickness): | 421 | def _dirty_inflate(self, x, y, width, height, thickness): |
| 404 | t = thickness * GraphArea.REFRESH_INFLATION_FACTOR | 422 | t = thickness * GraphArea.REFRESH_INFLATION_FACTOR |
| 405 | x -= t / 2.0 | 423 | x -= t / 2.0 |
| @@ -407,17 +425,17 @@ class GraphArea(gtk.DrawingArea): | |||
| 407 | width += t | 425 | width += t |
| 408 | height += t | 426 | height += t |
| 409 | self._dirty(x, y, width, height) | 427 | self._dirty(x, y, width, height) |
| 410 | 428 | ||
| 411 | def _dirty_rect_border(self, x, y, width, height, thickness): | 429 | def _dirty_rect_border(self, x, y, width, height, thickness): |
| 412 | # support rectangles with negative width and height (i.e. -width = width, but going leftwards | 430 | # support rectangles with negative width and height (i.e. -width = width, but going leftwards |
| 413 | # instead of rightwards) | 431 | # instead of rightwards) |
| 414 | x, y, width, height = self._positivify(x, y, width, height) | 432 | x, y, width, height = self._positivify(x, y, width, height) |
| 415 | 433 | ||
| 416 | self._dirty_inflate(x, y, width, 0, thickness) | 434 | self._dirty_inflate(x, y, width, 0, thickness) |
| 417 | self._dirty_inflate(x, y, 0, height, thickness) | 435 | self._dirty_inflate(x, y, 0, height, thickness) |
| 418 | self._dirty_inflate(x, y + height, width, 0, thickness) | 436 | self._dirty_inflate(x, y + height, width, 0, thickness) |
| 419 | self._dirty_inflate(x + width, y, 0, height, thickness) | 437 | self._dirty_inflate(x + width, y, 0, height, thickness) |
| 420 | 438 | ||
| 421 | def _positivify(self, x, y, width, height): | 439 | def _positivify(self, x, y, width, height): |
| 422 | if width < 0: | 440 | if width < 0: |
| 423 | x += width | 441 | x += width |
| @@ -425,53 +443,65 @@ class GraphArea(gtk.DrawingArea): | |||
| 425 | if height < 0: | 443 | if height < 0: |
| 426 | y += height | 444 | y += height |
| 427 | height = -height | 445 | height = -height |
| 428 | 446 | ||
| 429 | return x, y, width, height | 447 | return x, y, width, height |
| 430 | 448 | ||
| 431 | class GraphWindow(gtk.ScrolledWindow): | 449 | class GraphWindow(gtk.ScrolledWindow): |
| 432 | def __init__(self, renderer): | 450 | def __init__(self, renderer): |
| 433 | super(GraphWindow, self).__init__(None, None) | 451 | super(GraphWindow, self).__init__(None, None) |
| 434 | 452 | ||
| 435 | self.add_events(gtk.gdk.KEY_PRESS_MASK | gtk.gdk.SCROLL_MASK) | 453 | self.add_events(gtk.gdk.KEY_PRESS_MASK | gtk.gdk.SCROLL_MASK) |
| 436 | 454 | ||
| 437 | self.ctr = 0 | 455 | self.ctr = 0 |
| 438 | self.connect('key-press-event', self.key_press) | 456 | self.connect('key-press-event', self.key_press) |
| 439 | self.connect('scroll-event', self.scroll) | 457 | self.connect('scroll-event', self.scroll) |
| 440 | 458 | ||
| 441 | self.garea = GraphArea(renderer) | 459 | self.garea = GraphArea(renderer) |
| 442 | self.add(self.garea) | 460 | self.add(self.garea) |
| 443 | self.garea.show() | 461 | self.garea.show() |
| 444 | 462 | ||
| 445 | def key_press(self, widget, key_event): | 463 | def key_press(self, widget, key_event): |
| 446 | hadj = self.get_hadjustment() | 464 | hadj = self.get_hadjustment() |
| 447 | vadj = self.get_vadjustment() | 465 | vadj = self.get_vadjustment() |
| 448 | if hadj is None or vadj is None: | 466 | if hadj is None or vadj is None: |
| 449 | return | 467 | return |
| 450 | 468 | ||
| 451 | ctrl_clicked = key_event.state & gtk.gdk.CONTROL_MASK | 469 | ctrl_clicked = key_event.state & gtk.gdk.CONTROL_MASK |
| 452 | 470 | ||
| 453 | keystr = None | 471 | keystr = None |
| 454 | keymap = {gtk.keysyms.Up : 'up', gtk.keysyms.Down : 'down', | 472 | keymap = {gtk.keysyms.Up : 'up', gtk.keysyms.Down : 'down', |
| 455 | gtk.keysyms.Left : 'left', gtk.keysyms.Right : 'right'} | 473 | gtk.keysyms.Left : 'left', gtk.keysyms.Right : 'right'} |
| 456 | if key_event.keyval in keymap: | 474 | if key_event.keyval in keymap: |
| 457 | keystr = keymap[key_event.keyval] | 475 | keystr = keymap[key_event.keyval] |
| 458 | else: | 476 | else: |
| 459 | return True | 477 | return True |
| 460 | 478 | ||
| 461 | if ctrl_clicked: | 479 | if ctrl_clicked: |
| 462 | keystr = 'ctrl-' + keystr | 480 | keystr = 'ctrl-' + keystr |
| 463 | 481 | ||
| 464 | if keystr is not None: | 482 | if keystr is not None: |
| 465 | self._scroll_direction(keystr) | 483 | self._scroll_direction(keystr) |
| 466 | 484 | ||
| 467 | return True | 485 | return True |
| 468 | 486 | ||
| 487 | def set_hvalue(self, value): | ||
| 488 | if self.get_hadjustment() is None: | ||
| 489 | return | ||
| 490 | |||
| 491 | self.get_hadjustment().set_value(value) | ||
| 492 | |||
| 493 | def set_vvalue(self, value): | ||
| 494 | if self.get_vadjustment() is None: | ||
| 495 | return | ||
| 496 | |||
| 497 | self.get_vadjustment().set_value(value) | ||
| 498 | |||
| 469 | def _scroll_direction(self, keystr): | 499 | def _scroll_direction(self, keystr): |
| 470 | hadj = self.get_hadjustment() | 500 | hadj = self.get_hadjustment() |
| 471 | vadj = self.get_vadjustment() | 501 | vadj = self.get_vadjustment() |
| 472 | if hadj is None or vadj is None: | 502 | if hadj is None or vadj is None: |
| 473 | return | 503 | return |
| 474 | 504 | ||
| 475 | hupper = hadj.get_upper() | 505 | hupper = hadj.get_upper() |
| 476 | hlower = hadj.get_lower() | 506 | hlower = hadj.get_lower() |
| 477 | hpincr = hadj.get_page_increment() | 507 | hpincr = hadj.get_page_increment() |
| @@ -484,7 +514,7 @@ class GraphWindow(gtk.ScrolledWindow): | |||
| 484 | vpincr = vadj.get_page_increment() | 514 | vpincr = vadj.get_page_increment() |
| 485 | vsincr = vadj.get_step_increment() | 515 | vsincr = vadj.get_step_increment() |
| 486 | vpsize = vadj.get_page_size() | 516 | vpsize = vadj.get_page_size() |
| 487 | 517 | ||
| 488 | adj_tuple = {'up' : (vadj, -vsincr, 0, vval, max), | 518 | adj_tuple = {'up' : (vadj, -vsincr, 0, vval, max), |
| 489 | 'ctrl-up' : (vadj, -vpincr, 0, vval, max), | 519 | 'ctrl-up' : (vadj, -vpincr, 0, vval, max), |
| 490 | 'down' : (vadj, vsincr, vupper - vpsize, vval, min), | 520 | 'down' : (vadj, vsincr, vupper - vpsize, vval, min), |
| @@ -493,10 +523,10 @@ class GraphWindow(gtk.ScrolledWindow): | |||
| 493 | 'ctrl-left' : (hadj, -hpincr, 0, hval, max), | 523 | 'ctrl-left' : (hadj, -hpincr, 0, hval, max), |
| 494 | 'right' : (hadj, hsincr, hupper - hpsize, hval, min), | 524 | 'right' : (hadj, hsincr, hupper - hpsize, hval, min), |
| 495 | 'ctrl-right' : (hadj, hpincr, hupper - hpsize, hval, min)} | 525 | 'ctrl-right' : (hadj, hpincr, hupper - hpsize, hval, min)} |
| 496 | 526 | ||
| 497 | adj, inc, lim, val, extr = adj_tuple[keystr] | 527 | adj, inc, lim, val, extr = adj_tuple[keystr] |
| 498 | adj.set_value(extr(val + inc, lim)) | 528 | adj.set_value(extr(val + inc, lim)) |
| 499 | 529 | ||
| 500 | def scroll(self, widget, scroll_event): | 530 | def scroll(self, widget, scroll_event): |
| 501 | if scroll_event.state & gtk.gdk.CONTROL_MASK: | 531 | if scroll_event.state & gtk.gdk.CONTROL_MASK: |
| 502 | if scroll_event.direction == gtk.gdk.SCROLL_UP: | 532 | if scroll_event.direction == gtk.gdk.SCROLL_UP: |
| @@ -508,106 +538,119 @@ class GraphWindow(gtk.ScrolledWindow): | |||
| 508 | self._scroll_direction('up') | 538 | self._scroll_direction('up') |
| 509 | elif scroll_event.direction == gtk.gdk.SCROLL_DOWN: | 539 | elif scroll_event.direction == gtk.gdk.SCROLL_DOWN: |
| 510 | self._scroll_direction('down') | 540 | self._scroll_direction('down') |
| 511 | 541 | ||
| 512 | return True | 542 | return True |
| 513 | 543 | ||
| 514 | def get_graph_area(self): | 544 | def get_graph_area(self): |
| 515 | return self.garea | 545 | return self.garea |
| 516 | 546 | ||
| 517 | class MainWindow(gtk.Window): | 547 | class MainWindow(gtk.Window): |
| 518 | WINDOW_WIDTH_REQ = 500 | 548 | WINDOW_WIDTH_REQ = 500 |
| 519 | WINDOW_HEIGHT_REQ = 300 | 549 | WINDOW_HEIGHT_REQ = 300 |
| 520 | 550 | ||
| 521 | def __init__(self): | 551 | def __init__(self): |
| 522 | super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL) | 552 | super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL) |
| 523 | 553 | ||
| 524 | self.add_events(gtk.gdk.BUTTON_PRESS_MASK) | 554 | self.add_events(gtk.gdk.BUTTON_PRESS_MASK) |
| 525 | 555 | ||
| 526 | self.connect('delete_event', self.delete_event) | 556 | self.connect('delete_event', self.delete_event) |
| 527 | self.connect('destroy', self.die) | 557 | self.connect('destroy', self.die) |
| 528 | 558 | ||
| 529 | file_menu = gtk.Menu() | 559 | file_menu = gtk.Menu() |
| 530 | view_menu = gtk.Menu() | 560 | view_menu = gtk.Menu() |
| 531 | 561 | ||
| 532 | agr = gtk.AccelGroup() | 562 | agr = gtk.AccelGroup() |
| 533 | self.add_accel_group(agr) | 563 | self.add_accel_group(agr) |
| 534 | 564 | ||
| 535 | quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT, agr) | 565 | quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT, agr) |
| 536 | key, mod = gtk.accelerator_parse('Q') | 566 | key, mod = gtk.accelerator_parse('Q') |
| 537 | quit_item.add_accelerator('activate', agr, key, mod, | 567 | quit_item.add_accelerator('activate', agr, key, mod, |
| 538 | gtk.ACCEL_VISIBLE) | 568 | gtk.ACCEL_VISIBLE) |
| 539 | quit_item.connect('activate', self.quit_item_activate) | 569 | quit_item.connect('activate', self.quit_item_activate) |
| 540 | quit_item.show() | 570 | quit_item.show() |
| 541 | 571 | ||
| 542 | file_menu.append(quit_item) | 572 | file_menu.append(quit_item) |
| 543 | 573 | ||
| 544 | file_item = gtk.MenuItem('_File', True) | 574 | file_item = gtk.MenuItem('_File', True) |
| 545 | file_item.set_submenu(file_menu) | 575 | file_item.set_submenu(file_menu) |
| 546 | file_item.show() | 576 | file_item.show() |
| 547 | 577 | ||
| 578 | self.move_item = gtk.ImageMenuItem('_Move to Time') | ||
| 579 | key, mod = gtk.accelerator_parse('<Ctrl>M') | ||
| 580 | self.move_item.add_accelerator('activate', agr, key, mod, | ||
| 581 | gtk.ACCEL_VISIBLE) | ||
| 582 | self.move_item.set_sensitive(False) | ||
| 583 | |||
| 584 | self.move_item.connect('activate', self.move_to_time_activate) | ||
| 585 | self.move_item.show() | ||
| 586 | |||
| 548 | zoom_in_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_IN, agr) | 587 | zoom_in_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_IN, agr) |
| 549 | key, mod = gtk.accelerator_parse('<Ctrl>plus') | 588 | key, mod = gtk.accelerator_parse('<Ctrl>plus') |
| 550 | zoom_in_item.add_accelerator('activate', agr, key, mod, | 589 | zoom_in_item.add_accelerator('activate', agr, key, mod, |
| 551 | gtk.ACCEL_VISIBLE) | 590 | gtk.ACCEL_VISIBLE) |
| 552 | key, mod = gtk.accelerator_parse('<Ctrl>equal') | 591 | key, mod = gtk.accelerator_parse('<Ctrl>equal') |
| 553 | zoom_in_item.add_accelerator('activate', agr, key, mod, 0) | 592 | zoom_in_item.add_accelerator('activate', agr, key, mod, 0) |
| 554 | 593 | ||
| 555 | zoom_in_item.connect('activate', self.zoom_in_item_activate) | 594 | zoom_in_item.connect('activate', self.zoom_in_item_activate) |
| 556 | zoom_in_item.show() | 595 | zoom_in_item.show() |
| 557 | 596 | ||
| 558 | zoom_out_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_OUT, agr) | 597 | zoom_out_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_OUT, agr) |
| 559 | key, mod = gtk.accelerator_parse('<Ctrl>minus') | 598 | key, mod = gtk.accelerator_parse('<Ctrl>minus') |
| 560 | zoom_out_item.add_accelerator('activate', agr, key, mod, | 599 | zoom_out_item.add_accelerator('activate', agr, key, mod, |
| 561 | gtk.ACCEL_VISIBLE) | 600 | gtk.ACCEL_VISIBLE) |
| 562 | key, mod = gtk.accelerator_parse('<Ctrl>underscore') | 601 | key, mod = gtk.accelerator_parse('<Ctrl>underscore') |
| 563 | zoom_out_item.add_accelerator('activate', agr, key, mod, 0) | 602 | zoom_out_item.add_accelerator('activate', agr, key, mod, 0) |
| 564 | 603 | ||
| 565 | zoom_out_item.connect('activate', self.zoom_out_item_activate) | 604 | zoom_out_item.connect('activate', self.zoom_out_item_activate) |
| 566 | zoom_out_item.show() | 605 | zoom_out_item.show() |
| 567 | 606 | ||
| 607 | view_menu.append(self.move_item) | ||
| 568 | view_menu.append(zoom_in_item) | 608 | view_menu.append(zoom_in_item) |
| 569 | view_menu.append(zoom_out_item) | 609 | view_menu.append(zoom_out_item) |
| 570 | 610 | ||
| 571 | view_item = gtk.MenuItem('_View', True) | 611 | view_item = gtk.MenuItem('_View', True) |
| 572 | view_item.set_submenu(view_menu) | 612 | view_item.set_submenu(view_menu) |
| 573 | view_item.show() | 613 | view_item.show() |
| 574 | 614 | ||
| 575 | menu_bar = gtk.MenuBar() | 615 | menu_bar = gtk.MenuBar() |
| 576 | menu_bar.append(file_item) | 616 | menu_bar.append(file_item) |
| 577 | menu_bar.append(view_item) | 617 | menu_bar.append(view_item) |
| 578 | 618 | ||
| 579 | menu_bar.show() | 619 | menu_bar.show() |
| 580 | self.vbox = gtk.VBox(False, 0) | 620 | self.vbox = gtk.VBox(False, 0) |
| 581 | 621 | ||
| 582 | self.notebook = gtk.Notebook() | 622 | self.notebook = gtk.Notebook() |
| 583 | 623 | ||
| 584 | self.notebook.last_page = -1 | 624 | self.notebook.last_page = -1 |
| 585 | self.notebook.connect('switch-page', self.switch_page) | 625 | self.notebook.connect('switch-page', self.switch_page) |
| 586 | 626 | ||
| 587 | self.notebook.show() | 627 | self.notebook.show() |
| 588 | 628 | ||
| 589 | self.desc_label = gtk.Label('') | 629 | self.desc_label = gtk.Label('') |
| 590 | self.desc_label.set_justify(gtk.JUSTIFY_LEFT) | 630 | self.desc_label.set_alignment(0.0, 0.0) |
| 591 | self.desc_label.show() | 631 | self.desc_label.show() |
| 592 | 632 | ||
| 593 | self.vbox.pack_start(menu_bar, False, False, 0) | 633 | self.vbox.pack_start(menu_bar, False, False, 0) |
| 594 | self.vbox.pack_start(self.notebook, True, True, 0) | 634 | self.vbox.pack_start(self.notebook, True, True, 0) |
| 595 | self.vbox.pack_start(self.desc_label, False, False, 0) | 635 | self.vbox.pack_start(self.desc_label, False, False, 0) |
| 596 | self.vbox.show() | 636 | self.vbox.show() |
| 597 | 637 | ||
| 598 | self.add(self.vbox) | 638 | self.add(self.vbox) |
| 599 | 639 | ||
| 640 | self.info_win = InfoWindow() | ||
| 641 | |||
| 600 | self.set_size_request(MainWindow.WINDOW_WIDTH_REQ, MainWindow.WINDOW_HEIGHT_REQ) | 642 | self.set_size_request(MainWindow.WINDOW_WIDTH_REQ, MainWindow.WINDOW_HEIGHT_REQ) |
| 601 | 643 | ||
| 644 | self.set_title('Unit-Trace Visualizer') | ||
| 602 | self.show() | 645 | self.show() |
| 603 | 646 | ||
| 604 | def connect_widgets(self, gwindow): | 647 | def connect_widgets(self, gwindow): |
| 605 | gwindow.get_graph_area().connect('update-event-description', self.update_event_description) | 648 | gwindow.get_graph_area().connect('update-event-description', self.update_event_description) |
| 606 | gwindow.get_graph_area().connect('request-context-menu', self.request_context_menu) | 649 | gwindow.get_graph_area().connect('request-context-menu', self.request_context_menu) |
| 607 | gwindow.get_graph_area().connect('request-refresh-events', self.request_refresh_events) | 650 | gwindow.get_graph_area().connect('request-refresh-events', self.request_refresh_events) |
| 608 | gwindow.connect('request-zoom-in', self.zoom_in_item_activate) | 651 | gwindow.connect('request-zoom-in', self.zoom_in_item_activate) |
| 609 | gwindow.connect('request-zoom-out', self.zoom_out_item_activate) | 652 | gwindow.connect('request-zoom-out', self.zoom_out_item_activate) |
| 610 | 653 | ||
| 611 | def set_renderers(self, renderers): | 654 | def set_renderers(self, renderers): |
| 612 | for i in range(0, self.notebook.get_n_pages()): | 655 | for i in range(0, self.notebook.get_n_pages()): |
| 613 | self.notebook.remove_page(0) | 656 | self.notebook.remove_page(0) |
| @@ -618,7 +661,13 @@ class MainWindow(gtk.Window): | |||
| 618 | self.notebook.append_page(gwindow, gtk.Label(title)) | 661 | self.notebook.append_page(gwindow, gtk.Label(title)) |
| 619 | if self.notebook.get_n_pages() > 0: | 662 | if self.notebook.get_n_pages() > 0: |
| 620 | self.notebook.get_nth_page(0).grab_focus() | 663 | self.notebook.get_nth_page(0).grab_focus() |
| 621 | 664 | ||
| 665 | if self.notebook.get_n_pages() > 0: | ||
| 666 | self.move_item.set_sensitive(True) | ||
| 667 | else: | ||
| 668 | self.move_item.set_sensitive(False) | ||
| 669 | |||
| 670 | |||
| 622 | def switch_page(self, widget, page, page_num): | 671 | def switch_page(self, widget, page, page_num): |
| 623 | if self.notebook.get_nth_page(self.notebook.last_page) is not None: | 672 | if self.notebook.get_nth_page(self.notebook.last_page) is not None: |
| 624 | old_value = self.notebook.get_nth_page(self.notebook.last_page).get_hadjustment().get_value() | 673 | old_value = self.notebook.get_nth_page(self.notebook.last_page).get_hadjustment().get_value() |
| @@ -626,44 +675,84 @@ class MainWindow(gtk.Window): | |||
| 626 | new_ofs = self.notebook.get_nth_page(page_num).get_graph_area().get_graph().get_origin()[0] | 675 | new_ofs = self.notebook.get_nth_page(page_num).get_graph_area().get_graph().get_origin()[0] |
| 627 | new_value = old_value - old_ofs + new_ofs | 676 | new_value = old_value - old_ofs + new_ofs |
| 628 | self.notebook.get_nth_page(page_num).get_hadjustment().set_value(new_value) | 677 | self.notebook.get_nth_page(page_num).get_hadjustment().set_value(new_value) |
| 629 | 678 | ||
| 630 | self.notebook.last_page = page_num | 679 | self.notebook.last_page = page_num |
| 631 | 680 | ||
| 632 | def update_event_description(self, widget, event, msg): | 681 | def update_event_description(self, widget, event, msg): |
| 633 | self.desc_label.set_text(msg) | 682 | self.desc_label.set_text(msg) |
| 634 | 683 | ||
| 635 | def request_context_menu(self, widget, gdk_event, selected): | 684 | def request_context_menu(self, widget, gdk_event, selected): |
| 636 | button = 0 | 685 | button = 0 |
| 637 | if hasattr(gdk_event, 'button'): | 686 | if hasattr(gdk_event, 'button'): |
| 638 | button = gdk_event.button | 687 | button = gdk_event.button |
| 639 | time = gdk_event.time | 688 | time = gdk_event.time |
| 640 | 689 | ||
| 641 | menu = GraphContextMenu(selected) | 690 | menu = GraphContextMenu(selected, self.info_win) |
| 642 | menu.popup(None, None, None, button, time) | 691 | menu.popup(None, None, None, button, time) |
| 643 | 692 | ||
| 644 | def request_refresh_events(self, widget, sender, old, new, replace): | 693 | def request_refresh_events(self, widget, sender, old, new, replace): |
| 645 | for i in range(0, self.notebook.get_n_pages()): | 694 | for i in range(0, self.notebook.get_n_pages()): |
| 646 | if self.notebook.get_nth_page(i).get_graph_area() is not sender: | 695 | if self.notebook.get_nth_page(i).get_graph_area() is not sender: |
| 647 | self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace) | 696 | self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace) |
| 648 | break | 697 | break |
| 649 | for i in range(0, self.notebook.get_n_pages()): | 698 | for i in range(0, self.notebook.get_n_pages()): |
| 650 | if self.notebook.get_nth_page(i).get_graph_area() is sender: | 699 | if self.notebook.get_nth_page(i).get_graph_area() is sender: |
| 651 | self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace) | 700 | self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace) |
| 652 | 701 | ||
| 702 | def move_to_time_activate(self, widget): | ||
| 703 | dialog = TextInputDialog('Move to Time', 'What time to move to?', self) | ||
| 704 | |||
| 705 | err = True | ||
| 706 | while err: | ||
| 707 | ret = dialog.run() | ||
| 708 | |||
| 709 | if ret == gtk.RESPONSE_ACCEPT: | ||
| 710 | err, time = None, None | ||
| 711 | try: | ||
| 712 | time = float(dialog.get_input()) | ||
| 713 | start, end = self.notebook.get_nth_page(0).get_graph_area().get_schedule().get_time_bounds() | ||
| 714 | if time < start or time > end: | ||
| 715 | err = 'Time out of range!' | ||
| 716 | except ValueError: | ||
| 717 | err = 'Must input a number!' | ||
| 718 | |||
| 719 | if not err: | ||
| 720 | for i in xrange(0, self.notebook.get_n_pages()): | ||
| 721 | garea = self.notebook.get_nth_page(i).get_graph_area() | ||
| 722 | # Center as much as possible | ||
| 723 | pos = garea.get_graph().get_time_xpos(time) - garea.get_width() / 2.0 | ||
| 724 | pos = max(0, pos) | ||
| 725 | pos = min(garea.get_graph().get_width(), pos) | ||
| 726 | |||
| 727 | self.notebook.get_nth_page(i).set_hvalue(pos) | ||
| 728 | else: | ||
| 729 | err_dialog = gtk.MessageDialog(self, gtk.DIALOG_DESTROY_WITH_PARENT, | ||
| 730 | gtk.MESSAGE_ERROR, | ||
| 731 | gtk.BUTTONS_CLOSE, | ||
| 732 | err) | ||
| 733 | err_dialog.set_title('Input Error') | ||
| 734 | err_dialog.run() | ||
| 735 | err_dialog.destroy() | ||
| 736 | |||
| 737 | else: | ||
| 738 | break | ||
| 739 | |||
| 740 | dialog.destroy() | ||
| 741 | |||
| 653 | def zoom_in_item_activate(self, widget): | 742 | def zoom_in_item_activate(self, widget): |
| 654 | for i in range(0, self.notebook.get_n_pages()): | 743 | for i in range(0, self.notebook.get_n_pages()): |
| 655 | self.notebook.get_nth_page(i).get_graph_area().zoom_in() | 744 | self.notebook.get_nth_page(i).get_graph_area().zoom_in() |
| 656 | 745 | ||
| 657 | def zoom_out_item_activate(self, widget): | 746 | def zoom_out_item_activate(self, widget): |
| 658 | for i in range(0, self.notebook.get_n_pages()): | 747 | for i in range(0, self.notebook.get_n_pages()): |
| 659 | self.notebook.get_nth_page(i).get_graph_area().zoom_out() | 748 | self.notebook.get_nth_page(i).get_graph_area().zoom_out() |
| 660 | 749 | ||
| 661 | def quit_item_activate(self, widget): | 750 | def quit_item_activate(self, widget): |
| 662 | self.destroy() | 751 | self.destroy() |
| 663 | 752 | ||
| 664 | def delete_event(self, widget, event, data=None): | 753 | def delete_event(self, widget, event, data=None): |
| 665 | return False | 754 | return False |
| 666 | 755 | ||
| 667 | def die(self, widget, data=None): | 756 | def die(self, widget, data=None): |
| 668 | gtk.main_quit() | 757 | gtk.main_quit() |
| 669 | 758 | ||
diff --git a/unit_trace/viz/visualizer.py b/unit_trace/viz/visualizer.py index 5027e89..c0186f7 100755 --- a/unit_trace/viz/visualizer.py +++ b/unit_trace/viz/visualizer.py | |||
| @@ -1,9 +1,11 @@ | |||
| 1 | #!/usr/bin/python | 1 | #!/usr/bin/python |
| 2 | 2 | ||
| 3 | import viewer | ||
| 4 | import convert | 3 | import convert |
| 4 | import viewer | ||
| 5 | import renderer | 5 | import renderer |
| 6 | import schedule | ||
| 6 | import format | 7 | import format |
| 8 | import pygtk | ||
| 7 | import gtk | 9 | import gtk |
| 8 | 10 | ||
| 9 | TIME_PER_MAJ = 10000000 | 11 | TIME_PER_MAJ = 10000000 |
| @@ -11,13 +13,13 @@ TIME_PER_MAJ = 10000000 | |||
| 11 | def visualizer(stream): | 13 | def visualizer(stream): |
| 12 | sched = convert.convert_trace_to_schedule(stream) | 14 | sched = convert.convert_trace_to_schedule(stream) |
| 13 | sched.scan(TIME_PER_MAJ) | 15 | sched.scan(TIME_PER_MAJ) |
| 14 | 16 | ||
| 15 | task_renderer = renderer.Renderer(sched) | 17 | task_renderer = renderer.Renderer(sched) |
| 16 | task_renderer.prepare_task_graph(attrs=format.GraphFormat(time_per_maj=TIME_PER_MAJ)) | 18 | task_renderer.prepare_task_graph(attrs=format.GraphFormat(time_per_maj=TIME_PER_MAJ)) |
| 17 | cpu_renderer = renderer.Renderer(sched) | 19 | cpu_renderer = renderer.Renderer(sched) |
| 18 | cpu_renderer.prepare_cpu_graph(attrs=format.GraphFormat(time_per_maj=TIME_PER_MAJ)) | 20 | cpu_renderer.prepare_cpu_graph(attrs=format.GraphFormat(time_per_maj=TIME_PER_MAJ)) |
| 19 | 21 | ||
| 20 | window = viewer.MainWindow() | 22 | window = viewer.MainWindow() |
| 21 | window.set_renderers({'Tasks' : task_renderer, 'CPUs' : cpu_renderer}) | 23 | window.set_renderers({'Tasks' : task_renderer, 'CPUs' : cpu_renderer}) |
| 22 | 24 | ||
| 23 | gtk.main() | 25 | gtk.main() |
diff --git a/unit_trace/viz/windows.py b/unit_trace/viz/windows.py new file mode 100644 index 0000000..4e5af5c --- /dev/null +++ b/unit_trace/viz/windows.py | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | """Module for various miscellanious GUI windows.""" | ||
| 2 | |||
| 3 | import pygtk | ||
| 4 | import gtk | ||
| 5 | import gobject | ||
| 6 | |||
| 7 | class TextInputDialog(gtk.Dialog): | ||
| 8 | WINDOW_WIDTH_REQ = 250 | ||
| 9 | WINDOW_HEIGHT_REQ = 100 | ||
| 10 | |||
| 11 | def __init__(self, title, label, parent_window=None): | ||
| 12 | super(TextInputDialog, self).__init__(title, parent_window, | ||
| 13 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, | ||
| 14 | (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, | ||
| 15 | gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) | ||
| 16 | label_widget = gtk.Label(label) | ||
| 17 | label_widget.set_alignment(0.0, 0.0) | ||
| 18 | self.text_input = gtk.Entry() | ||
| 19 | label_widget.show() | ||
| 20 | self.text_input.show() | ||
| 21 | |||
| 22 | vbox = self.get_content_area() | ||
| 23 | |||
| 24 | vbox.pack_start(label_widget, False, False, 0) | ||
| 25 | vbox.pack_start(self.text_input, False, False, 0) | ||
| 26 | vbox.show() | ||
| 27 | |||
| 28 | self.set_resizable(False) | ||
| 29 | |||
| 30 | self.set_size_request(TextInputDialog.WINDOW_WIDTH_REQ, TextInputDialog.WINDOW_HEIGHT_REQ) | ||
| 31 | |||
| 32 | def get_input(self): | ||
| 33 | return self.text_input.get_text() | ||
| 34 | |||
| 35 | class InfoWindow(gtk.Window): | ||
| 36 | """Window designed to show information about an event.""" | ||
| 37 | |||
| 38 | WINDOW_WIDTH_REQ = 400 | ||
| 39 | WINDOW_HEIGHT_REQ = 300 | ||
| 40 | |||
| 41 | def __init__(self): | ||
| 42 | super(InfoWindow, self).__init__(gtk.WINDOW_TOPLEVEL) | ||
| 43 | |||
| 44 | self.frm = gtk.Frame() | ||
| 45 | |||
| 46 | self.connect('delete_event', gtk.Widget.hide_on_delete) | ||
| 47 | |||
| 48 | self.text_view = gtk.TextView() | ||
| 49 | self.text_view.set_editable(False) | ||
| 50 | self.text_view.show() | ||
| 51 | self.frm.add(self.text_view) | ||
| 52 | self.frm.show() | ||
| 53 | |||
| 54 | self.vbox = gtk.VBox(False, 0) | ||
| 55 | self.vbox.pack_start(self.frm, True, True, 0) | ||
| 56 | self.vbox.show() | ||
| 57 | |||
| 58 | self.add(self.vbox) | ||
| 59 | |||
| 60 | self.set_default_size(InfoWindow.WINDOW_WIDTH_REQ, InfoWindow.WINDOW_HEIGHT_REQ) | ||
| 61 | self.set_title('Event Details') | ||
| 62 | |||
| 63 | def set_event(self, event): | ||
| 64 | self.text_view.get_buffer().set_text(event.str_long()) | ||
| 65 | self.frm.set_label('Details for ' + event.get_name()) | ||
