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