diff options
author | Joshua Lock <josh@linux.intel.com> | 2011-01-12 12:24:04 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2011-02-24 15:54:52 +0000 |
commit | 9b43eaff99615d8bd58b2f3a54c280e5470d3a9c (patch) | |
tree | 1aa1ee1e41d269887e1dc80f0e751237ccc85074 | |
parent | a13304e1c6500db95823961a5a34849db46037e3 (diff) | |
download | poky-9b43eaff99615d8bd58b2f3a54c280e5470d3a9c.tar.gz |
bitbake: introduce crumbs.TaskListModel a gtk.ListStore subclass
Provide a gtk.ListStore subclass which includes a function,
populate(), which takes as input the data emitted by
bb.event.TargetsTreeGenerated and fills the ListStore model
appropriately.
Furthermore convenience functions are provided by which the caller can
get gtk.TreeModel subclasses which provide filtered views of the data.
Signed-off-by: Joshua Lock <josh@linux.intel.com>
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/tasklistmodel.py | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/bitbake/lib/bb/ui/crumbs/tasklistmodel.py b/bitbake/lib/bb/ui/crumbs/tasklistmodel.py new file mode 100644 index 0000000000..a83a176ddc --- /dev/null +++ b/bitbake/lib/bb/ui/crumbs/tasklistmodel.py | |||
@@ -0,0 +1,346 @@ | |||
1 | # | ||
2 | # BitBake Graphical GTK User Interface | ||
3 | # | ||
4 | # Copyright (C) 2011 Intel Corporation | ||
5 | # | ||
6 | # Authored by Joshua Lock <josh@linux.intel.com> | ||
7 | # | ||
8 | # This program is free software; you can redistribute it and/or modify | ||
9 | # it under the terms of the GNU General Public License version 2 as | ||
10 | # published by the Free Software Foundation. | ||
11 | # | ||
12 | # This program is distributed in the hope that it will be useful, | ||
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | # GNU General Public License for more details. | ||
16 | # | ||
17 | # You should have received a copy of the GNU General Public License along | ||
18 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
20 | |||
21 | import gtk | ||
22 | import gobject | ||
23 | |||
24 | class TaskListModel(gtk.ListStore): | ||
25 | """ | ||
26 | This class defines an gtk.ListStore subclass which will convert the output | ||
27 | of the bb.event.TargetsTreeGenerated event into a gtk.ListStore whilst also | ||
28 | providing convenience functions to access gtk.TreeModel subclasses which | ||
29 | provide filtered views of the data. | ||
30 | """ | ||
31 | (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC) = range(8) | ||
32 | |||
33 | __gsignals__ = { | ||
34 | "tasklist-populated" : (gobject.SIGNAL_RUN_LAST, | ||
35 | gobject.TYPE_NONE, | ||
36 | ()) | ||
37 | } | ||
38 | |||
39 | """ | ||
40 | """ | ||
41 | def __init__(self): | ||
42 | self.contents = None | ||
43 | self.tasks = None | ||
44 | self.packages = None | ||
45 | self.images = None | ||
46 | |||
47 | gtk.ListStore.__init__ (self, | ||
48 | gobject.TYPE_STRING, | ||
49 | gobject.TYPE_STRING, | ||
50 | gobject.TYPE_STRING, | ||
51 | gobject.TYPE_STRING, | ||
52 | gobject.TYPE_STRING, | ||
53 | gobject.TYPE_STRING, | ||
54 | gobject.TYPE_STRING, | ||
55 | gobject.TYPE_BOOLEAN) | ||
56 | |||
57 | """ | ||
58 | Create, if required, and return a filtered gtk.TreeModel | ||
59 | containing only the items which are to be included in the | ||
60 | image | ||
61 | """ | ||
62 | def contents_model(self): | ||
63 | if not self.contents: | ||
64 | self.contents = self.filter_new() | ||
65 | self.contents.set_visible_column(self.COL_INC) | ||
66 | return self.contents | ||
67 | |||
68 | """ | ||
69 | Helper function to determine whether an item is a task | ||
70 | """ | ||
71 | def task_model_filter(self, model, it): | ||
72 | if model.get_value(it, self.COL_TYPE) == 'task': | ||
73 | return True | ||
74 | else: | ||
75 | return False | ||
76 | |||
77 | """ | ||
78 | Create, if required, and return a filtered gtk.TreeModel | ||
79 | containing only the items which are tasks | ||
80 | """ | ||
81 | def tasks_model(self): | ||
82 | if not self.tasks: | ||
83 | self.tasks = self.filter_new() | ||
84 | self.tasks.set_visible_func(self.task_model_filter) | ||
85 | return self.tasks | ||
86 | |||
87 | """ | ||
88 | Helper function to determine whether an item is an image | ||
89 | """ | ||
90 | def image_model_filter(self, model, it): | ||
91 | if model.get_value(it, self.COL_TYPE) == 'image': | ||
92 | return True | ||
93 | else: | ||
94 | return False | ||
95 | |||
96 | """ | ||
97 | Create, if required, and return a filtered gtk.TreeModel | ||
98 | containing only the items which are images | ||
99 | """ | ||
100 | def images_model(self): | ||
101 | if not self.images: | ||
102 | self.images = self.filter_new() | ||
103 | self.images.set_visible_func(self.image_model_filter) | ||
104 | return self.images | ||
105 | |||
106 | """ | ||
107 | Helper function to determine whether an item is a package | ||
108 | """ | ||
109 | def package_model_filter(self, model, it): | ||
110 | if model.get_value(it, self.COL_TYPE) == 'package': | ||
111 | return True | ||
112 | else: | ||
113 | return False | ||
114 | |||
115 | """ | ||
116 | Create, if required, and return a filtered gtk.TreeModel | ||
117 | containing only the items which are packages | ||
118 | """ | ||
119 | def packages_model(self): | ||
120 | if not self.packages: | ||
121 | self.packages = self.filter_new() | ||
122 | self.packages.set_visible_func(self.package_model_filter) | ||
123 | return self.packages | ||
124 | |||
125 | """ | ||
126 | The populate() function takes as input the data from a | ||
127 | bb.event.TargetsTreeGenerated event and populates the TaskList. | ||
128 | Once the population is done it emits gsignal tasklist-populated | ||
129 | to notify any listeners that the model is ready | ||
130 | """ | ||
131 | def populate(self, event_model): | ||
132 | for item in event_model["pn"]: | ||
133 | atype = 'package' | ||
134 | name = item | ||
135 | summary = event_model["pn"][item]["summary"] | ||
136 | license = event_model["pn"][item]["license"] | ||
137 | group = event_model["pn"][item]["section"] | ||
138 | |||
139 | depends = event_model["depends"].get(item, "") | ||
140 | rdepends = event_model["rdepends-pn"].get(item, "") | ||
141 | depends = depends + rdepends | ||
142 | self.squish(depends) | ||
143 | deps = " ".join(depends) | ||
144 | |||
145 | if name.count('task-') > 0: | ||
146 | atype = 'task' | ||
147 | elif name.count('-image-') > 0: | ||
148 | atype = 'image' | ||
149 | |||
150 | self.set(self.append(), self.COL_NAME, name, self.COL_DESC, summary, | ||
151 | self.COL_LIC, license, self.COL_GROUP, group, | ||
152 | self.COL_DEPS, deps, self.COL_BINB, "", | ||
153 | self.COL_TYPE, atype, self.COL_INC, False) | ||
154 | |||
155 | self.emit("tasklist-populated") | ||
156 | |||
157 | """ | ||
158 | squish lst so that it doesn't contain any duplicates | ||
159 | """ | ||
160 | def squish(self, lst): | ||
161 | seen = {} | ||
162 | for l in lst: | ||
163 | seen[l] = 1 | ||
164 | return seen.keys() | ||
165 | |||
166 | """ | ||
167 | Mark the item at path as not included | ||
168 | NOTE: | ||
169 | path should be a gtk.TreeModelPath into self (not a filtered model) | ||
170 | """ | ||
171 | def remove_item_path(self, path): | ||
172 | self[path][self.COL_BINB] = "" | ||
173 | self[path][self.COL_INC] = False | ||
174 | |||
175 | """ | ||
176 | """ | ||
177 | def mark(self, path): | ||
178 | name = self[path][self.COL_NAME] | ||
179 | it = self.get_iter_first() | ||
180 | removals = [] | ||
181 | #print("Removing %s" % name) | ||
182 | |||
183 | self.remove_item_path(path) | ||
184 | |||
185 | # Remove all dependent packages, update binb | ||
186 | while it: | ||
187 | path = self.get_path(it) | ||
188 | # FIXME: need to ensure partial name matching doesn't happen, regexp? | ||
189 | if self[path][self.COL_INC] and self[path][self.COL_DEPS].count(name): | ||
190 | #print("%s depended on %s, marking for removal" % (self[path][self.COL_NAME], name)) | ||
191 | # found a dependency, remove it | ||
192 | self.mark(path) | ||
193 | if self[path][self.COL_INC] and self[path][self.COL_BINB].count(name): | ||
194 | binb = self.find_alt_dependency(self[path][self.COL_NAME]) | ||
195 | #print("%s was brought in by %s, binb set to %s" % (self[path][self.COL_NAME], name, binb)) | ||
196 | self[path][self.COL_BINB] = binb | ||
197 | it = self.iter_next(it) | ||
198 | |||
199 | """ | ||
200 | """ | ||
201 | def sweep_up(self): | ||
202 | removals = [] | ||
203 | it = self.get_iter_first() | ||
204 | |||
205 | while it: | ||
206 | path = self.get_path(it) | ||
207 | binb = self[path][self.COL_BINB] | ||
208 | if binb == "" or binb is None: | ||
209 | #print("Sweeping up %s" % self[path][self.COL_NAME]) | ||
210 | if not path in removals: | ||
211 | removals.extend(path) | ||
212 | it = self.iter_next(it) | ||
213 | |||
214 | while removals: | ||
215 | path = removals.pop() | ||
216 | self.mark(path) | ||
217 | |||
218 | """ | ||
219 | Remove an item from the contents | ||
220 | """ | ||
221 | def remove_item(self, path): | ||
222 | self.mark(path) | ||
223 | self.sweep_up() | ||
224 | |||
225 | """ | ||
226 | Find the name of an item in the image contents which depends on the item | ||
227 | at contents_path returns either an item name (str) or None | ||
228 | NOTE: | ||
229 | contents_path must be a path in the self.contents gtk.TreeModel | ||
230 | """ | ||
231 | def find_alt_dependency(self, name): | ||
232 | it = self.get_iter_first() | ||
233 | while it: | ||
234 | # iterate all items in the model | ||
235 | path = self.get_path(it) | ||
236 | deps = self[path][self.COL_DEPS] | ||
237 | itname = self[path][self.COL_NAME] | ||
238 | inc = self[path][self.COL_INC] | ||
239 | if itname != name and inc and deps.count(name) > 0: | ||
240 | # if this item depends on the item, return this items name | ||
241 | #print("%s depends on %s" % (itname, name)) | ||
242 | return itname | ||
243 | it = self.iter_next(it) | ||
244 | return "" | ||
245 | |||
246 | """ | ||
247 | Convert a path in self to a path in the filtered contents model | ||
248 | """ | ||
249 | def contents_path_for_path(self, path): | ||
250 | return self.contents.convert_child_path_to_path(path) | ||
251 | |||
252 | """ | ||
253 | Check the self.contents gtk.TreeModel for an item | ||
254 | where COL_NAME matches item_name | ||
255 | Returns True if a match is found, False otherwise | ||
256 | """ | ||
257 | def contents_includes_name(self, item_name): | ||
258 | it = self.contents.get_iter_first() | ||
259 | while it: | ||
260 | path = self.contents.get_path(it) | ||
261 | if self.contents[path][self.COL_NAME] == item_name: | ||
262 | return True | ||
263 | it = self.contents.iter_next(it) | ||
264 | return False | ||
265 | |||
266 | """ | ||
267 | Add this item, and any of its dependencies, to the image contents | ||
268 | """ | ||
269 | def include_item(self, item_path, binb=""): | ||
270 | name = self[item_path][self.COL_NAME] | ||
271 | deps = self[item_path][self.COL_DEPS] | ||
272 | cur_inc = self[item_path][self.COL_INC] | ||
273 | #print("Adding %s for %s dependency" % (name, binb)) | ||
274 | if not cur_inc: | ||
275 | self[item_path][self.COL_INC] = True | ||
276 | self[item_path][self.COL_BINB] = binb | ||
277 | if deps: | ||
278 | #print("Dependencies of %s are %s" % (name, deps)) | ||
279 | # add all of the deps and set their binb to this item | ||
280 | for dep in deps.split(" "): | ||
281 | # FIXME: this skipping virtuals can't be right? Unless we choose only to show target | ||
282 | # packages? In which case we should handle this server side... | ||
283 | # If the contents model doesn't already contain dep, add it | ||
284 | if not dep.startswith("virtual") and not self.contents_includes_name(dep): | ||
285 | path = self.find_path_for_item(dep) | ||
286 | if path: | ||
287 | self.include_item(path, name) | ||
288 | else: | ||
289 | pass | ||
290 | |||
291 | """ | ||
292 | Find the model path for the item_name | ||
293 | Returns the path in the model or None | ||
294 | """ | ||
295 | def find_path_for_item(self, item_name): | ||
296 | it = self.get_iter_first() | ||
297 | path = None | ||
298 | while it: | ||
299 | path = self.get_path(it) | ||
300 | if (self[path][self.COL_NAME] == item_name): | ||
301 | return path | ||
302 | else: | ||
303 | it = self.iter_next(it) | ||
304 | return None | ||
305 | |||
306 | """ | ||
307 | Empty self.contents by setting the include of each entry to None | ||
308 | """ | ||
309 | def reset(self): | ||
310 | it = self.contents.get_iter_first() | ||
311 | while it: | ||
312 | path = self.contents.get_path(it) | ||
313 | opath = self.contents.convert_path_to_child_path(path) | ||
314 | self[opath][self.COL_INC] = False | ||
315 | self[opath][self.COL_BINB] = "" | ||
316 | # As we've just removed the first item... | ||
317 | it = self.contents.get_iter_first() | ||
318 | |||
319 | """ | ||
320 | Returns True if one of the selected tasks is an image, False otherwise | ||
321 | """ | ||
322 | def targets_contains_image(self): | ||
323 | it = self.images.get_iter_first() | ||
324 | while it: | ||
325 | path = self.images.get_path(it) | ||
326 | inc = self.images[path][self.COL_INC] | ||
327 | if inc: | ||
328 | return True | ||
329 | it = self.images.iter_next(it) | ||
330 | return False | ||
331 | |||
332 | """ | ||
333 | Return a list of all selected items which are not -native or -cross | ||
334 | """ | ||
335 | def get_targets(self): | ||
336 | tasks = [] | ||
337 | |||
338 | it = self.contents.get_iter_first() | ||
339 | while it: | ||
340 | path = self.contents.get_path(it) | ||
341 | name = self.contents[path][self.COL_NAME] | ||
342 | stype = self.contents[path][self.COL_TYPE] | ||
343 | if not name.count('-native') and not name.count('-cross'): | ||
344 | tasks.append(name) | ||
345 | it = self.contents.iter_next(it) | ||
346 | return tasks | ||