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()) | ||