summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGary Bressler <garybressler@nc.rr.com>2010-04-08 17:11:08 -0400
committerGary Bressler <garybressler@nc.rr.com>2010-04-08 17:11:08 -0400
commitceff6457bfeb5642616f4711f14e0bb652d12164 (patch)
treed05b4ebd1c3ee6e28884c669d65fd31700941086
parent01abc8352aa2fd192678b4066b26ea749a203801 (diff)
Updated the documentation to describe the visualizer, made unit-trace itself not require gtk/cairo, and a few other minor things.
-rw-r--r--doc/index.txt79
-rwxr-xr-xunit-trace2
-rw-r--r--unit_trace/viz/__init__.py23
-rw-r--r--unit_trace/viz/canvas.py340
-rw-r--r--unit_trace/viz/convert.py24
-rw-r--r--unit_trace/viz/format.py30
-rw-r--r--unit_trace/viz/graph.py444
-rw-r--r--unit_trace/viz/renderer.py16
-rw-r--r--unit_trace/viz/schedule.py366
-rw-r--r--unit_trace/viz/viewer.py409
-rwxr-xr-xunit_trace/viz/visualizer.py10
-rw-r--r--unit_trace/viz/windows.py65
12 files changed, 1057 insertions, 751 deletions
diff --git a/doc/index.txt b/doc/index.txt
index 9360e40..135579b 100644
--- a/doc/index.txt
+++ b/doc/index.txt
@@ -115,9 +115,82 @@ Some submodules have further documentation, appearing later in this document.
115 115
116## Specific Submodule Documentation ## 116## Specific Submodule Documentation ##
117 117
118Here, documentation is provided for submodules for which relevant information is not considered to be self-evident. 118### The Visualizer Module ###
119
120The visualizer can give you an on-the-fly visual representation of the input
121stream, with the ability to scroll through a graph of the schedule and inspect
122various elements of it. To run the visualizer, add the `-v`
123option when invoking unit-trace. Note that you don't have to run the visualizer
124by itself -- for instance, you can both run the visualizer and get input to
125stdout by combining the `-v` and `-o`
126options. The information that goes into the visualizer is dependent on the
127input parameters you specify. For example, if you use `-e`
128and `-l` to specify a time range, the visualizer will
129generate a graph restricted to that time range.
130
131When the visualizer starts up, you'll see the beginning of the graph which the
132visualizer automatically generated. We'll first discuss the axes.
133The x-axis gives time (in whatever units
134the trace file was using). The meanings of the markings by y-axis depend on whether you
135are in Task Mode or CPU Mode. (You can change between Task Mode and
136CPU Mode by clicking the tabs at the top.) In Task Mode, the schedule is organized
137by task, so each item listed to the left of the y-axis gives
138the name of a task that was running (at present, the name of a task is its PID).
139Likewise, in CPU Mode the schedule is organized by CPU number, and each item
140gives the identifier of a CPU that at one point was used by at least one task.
141
142The horizontal cross-section demarcated by each task name or CPU
143identifier gives the chronological sequence of events in the input stream
144for the relevant task or CPU. The event symbols are as follows:
119 145
120(So far, it hasn't been necessary to add anything here, but once questions arise, it will become clear which submodules need to be documented more fully.) 146<table border=1>
147<tr><td>Symbol Description</td><td>Event Type</td><td>Meaning</td></tr>
148<tr><td>Large colored bar</td><td>Scheduled</td><td>A job was scheduled during the period spanned by the bar.</td>
149<tr><td>Black triangle</td><td>Suspend (Block)</td><td>A task blocked at this time.</td></tr>
150<tr><td>White triangle</td><td>Resume (Unblock)</td><td>A task resumed execution at this time.</td></tr>
151<tr><td>"T" shape</td><td>Complete</td><td>A task signaled its completion of a job at this time.</td></tr>
152<tr><td>Large up arrow</td><td>Release</td><td>A job release occurred. (Appears only in Task Mode.)</td></tr>
153<tr><td>Small up arrow</td><td>Release</td><td>A job release occurred. (Appears only in CPU Mode. These appear
154attached to the x-axis, as is customary, rather than in a CPU's area.)</td></tr>
155<tr><td>Large down arrow</td><td>Deadline</td><td>A job's deadline occurs at this time. (Appears only in Task Mode.)</td></tr>
156<tr><td>Small down arrow</td><td>Deadline</td><td>A job's deadline occurs at this time. (Appears only in CPU Mode. These appear
157attached to the x-axis, as is customary, rather than in a CPU's area.)</td></tr>
158<tr><td>Small colored bar</td><td>Priority Inversion</td><td>(Appears only in conjuction with the gedf_test module.) A priority inversion occurred for some task: that is, the task in question <i>should</i> have been scheduled at the depicted time, but wasn't. In Task Mode these are organized by task (and appear gray since color would be redundant), and in CPU mode they appear at the bottom, colored by task.</td></tr>
159</table>
160
161If you're unsure as
162to what a certain symbol means, you can also mouse over it in the visualizer
163and read the description at the bottom of the screen.
164
165Also, a note about the `Scheduled` (and `Priority Inversion`) events: each of these events actually
166corresponds to two events in the input stream. Namely, a `Scheduled` event is really a `Switch To`
167event paired with a `Switch Away` event, and a `Priority Inversion` event is really an
168`Inversion Start` event paired with an `Inversion End` event. These events of course correspond to
169being scheduled and being descheduled, respectively. <i>However</i>, if the visualizer module
170finds a start event but not an end event (or vice-versa), it assumes that the corresponding
171event occurred, but at a time not in the input stream.
172In other words, it assumes that such events are genuine. To represent this phenomenon,
173the visualizer shows the bar going "off the graph".
174
175Interacting with the visualizer is easy. The scrollbars work in the obvious way. You can also
176use the arrow keys to move, or use Ctrl+arrow keys to move faster. Mousing over an event gives
177a description of the event at the bottom. You can also click an event to
178select it. Hold down Ctrl to select multiple events. You can also drag a box around multiple
179events to select them. You can even combine this with the Ctrl key to select multiple
180boxes of events in succession. Your selection is independent of the mode you are in --
181thus if you wanted to see e.g. what CPUs a task was running on from time A to time B,
182you could just select all the events under the task in question in Task Mode and then
183switch over to CPU mode. Right-click and you will get a context menu containing each event
184you selected. Selecting an item in the menu gives you detailed information about the event
185in its own window.
186
187If you want to see what's happening at a certain time, but don't want to bother scrolling there
188manually, you can select `View->Move to Time` and type in the time you want to move to.
189
190You can also zoom by either going to `View->Zoom In/Out`, or by holding down Ctrl and scrolling
191the mouse wheel.
192
193To exit the Unit-Trace visualizer, go to `File->Quit` or click the close button.
121 194
122## Gotchas ## 195## Gotchas ##
123 196
@@ -150,7 +223,7 @@ The following "rules" are currently in place:
150### Architecture ### 223### Architecture ###
151If you are interested in contributing to Unit-Trace, you probably ought to know a bit about its overall architecture. 224If you are interested in contributing to Unit-Trace, you probably ought to know a bit about its overall architecture.
152 225
153Generally speaking, each Unit-Trace submodules is a Python generator. It accepts a Python iterator object as input and returns a Python iterator 226Generally speaking, each Unit-Trace submodule is a Python generator. It accepts a Python iterator object as input and returns a Python iterator
154object as output. (You may want to look up the relevant Python terminology.) 227object as output. (You may want to look up the relevant Python terminology.)
155 228
156The exceptions are input submodules, which do not take any input other than a list of trace files, and the output submodules, which do not return 229The exceptions are input submodules, which do not take any input other than a list of trace files, and the output submodules, which do not return
diff --git a/unit-trace b/unit-trace
index a146874..43f752b 100755
--- a/unit-trace
+++ b/unit-trace
@@ -17,12 +17,12 @@ from unit_trace import sanitizer
17from unit_trace import gedf_test 17from unit_trace import gedf_test
18from unit_trace import gedf_inversion_stat_printer 18from unit_trace import gedf_inversion_stat_printer
19from unit_trace import stdout_printer 19from unit_trace import stdout_printer
20from unit_trace import viz
21from unit_trace import progress 20from unit_trace import progress
22from unit_trace import skipper 21from unit_trace import skipper
23from unit_trace import maxer 22from unit_trace import maxer
24from unit_trace import earliest 23from unit_trace import earliest
25from unit_trace import latest 24from unit_trace import latest
25from unit_trace import viz
26 26
27# Get trace files from command line arguments 27# Get trace files from command line arguments
28from optparse import OptionParser 28from optparse import OptionParser
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 @@
1import visualizer 1try:
2import viewer 2 import pygtk
3import renderer 3 import gtk
4import format 4 import gobject
5import gobject 5 import cairo
6import gtk 6 import visualizer
7import convert 7 import viewer
8 import renderer
9 import format
10 import convert
11except 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
9gobject.signal_new('set-scroll-adjustments', viewer.GraphArea, gobject.SIGNAL_RUN_FIRST, 18gobject.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
82class SVGSurface(Surface): 82class 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
92class ImageSurface(Surface): 92class 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
105class Pattern(object): 105class 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
144class Canvas(object): 144class 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
786class SelectableRegion(object): 786class 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
28def convert_trace_to_schedule(stream): 28def 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
84def _pid_to_task_name(pid): 84def _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
88def _find_num_cpus(stream): 88def _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
20class GraphFormat(object): 20class 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
290class TaskGraph(Graph): 290class 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
448class CpuGraph(Graph): 448class 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
6on a graph.""" 6on a graph."""
7 7
8class Renderer(object): 8class 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
129class Schedule(object): 129class 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
232def deepcopy_selected(selected): 232def 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
238class Task(object): 238class 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
268class Job(object): 268class 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
329class Event(DummyEvent): 329class 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
376class ErrorEvent(Event): 388class ErrorEvent(Event):
377 pass 389 pass
378 390
379class SuspendEvent(Event): 391class 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
404class ResumeEvent(Event): 416class 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
429class CompleteEvent(Event): 441class 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
497class SwitchAwayEvent(Event): 526class 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
554class ReleaseEvent(Event): 594class 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
576class DeadlineEvent(Event): 616class 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
598class InversionStartEvent(ErrorEvent): 638class 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
642class InversionEndEvent(ErrorEvent): 699class 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
692class InversionDummy(DummyEvent): 760class 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
707class IsRunningDummy(DummyEvent): 775class 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
5from schedule import * 5from schedule import *
6
7from renderer import * 6from renderer import *
7from windows import *
8 8
9import pygtk 9import pygtk
10import gtk 10import gtk
@@ -13,145 +13,157 @@ import copy
13 13
14class GraphContextMenu(gtk.Menu): 14class 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
35class GraphArea(gtk.DrawingArea): 42class 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
431class GraphWindow(gtk.ScrolledWindow): 449class 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
517class MainWindow(gtk.Window): 547class 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
3import viewer
4import convert 3import convert
4import viewer
5import renderer 5import renderer
6import schedule
6import format 7import format
8import pygtk
7import gtk 9import gtk
8 10
9TIME_PER_MAJ = 10000000 11TIME_PER_MAJ = 10000000
@@ -11,13 +13,13 @@ TIME_PER_MAJ = 10000000
11def visualizer(stream): 13def 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
3import pygtk
4import gtk
5import gobject
6
7class 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
35class 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())