summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/ui/hob.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/ui/hob.py')
-rw-r--r--bitbake/lib/bb/ui/hob.py596
1 files changed, 596 insertions, 0 deletions
diff --git a/bitbake/lib/bb/ui/hob.py b/bitbake/lib/bb/ui/hob.py
new file mode 100644
index 0000000000..52788813c5
--- /dev/null
+++ b/bitbake/lib/bb/ui/hob.py
@@ -0,0 +1,596 @@
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
21import gobject
22import gtk
23from bb.ui.crumbs.progress import ProgressBar
24from bb.ui.crumbs.tasklistmodel import TaskListModel
25from bb.ui.crumbs.hobeventhandler import HobHandler
26from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild
27import xmlrpclib
28import logging
29import Queue
30
31class MainWindow (gtk.Window):
32
33 def __init__(self, taskmodel, handler, curr_mach=None, curr_distro=None):
34 gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
35 self.model = taskmodel
36 self.model.connect("tasklist-populated", self.update_model)
37 self.curr_mach = curr_mach
38 self.curr_distro = curr_distro
39 self.handler = handler
40 self.set_border_width(10)
41 self.connect("delete-event", gtk.main_quit)
42 self.set_title("BitBake Image Creator")
43 self.set_default_size(700, 600)
44
45 self.build = RunningBuild()
46 self.build.connect("build-succeeded", self.running_build_succeeded_cb)
47 self.build.connect("build-failed", self.running_build_failed_cb)
48
49 createview = self.create_build_gui()
50 buildview = self.view_build_gui()
51 self.nb = gtk.Notebook()
52 self.nb.append_page(createview)
53 self.nb.append_page(buildview)
54 self.nb.set_current_page(0)
55 self.nb.set_show_tabs(False)
56 self.add(self.nb)
57 self.generating = False
58
59 def scroll_tv_cb(self, model, path, it, view):
60 view.scroll_to_cell(path)
61
62 def running_build_failed_cb(self, running_build):
63 # FIXME: handle this
64 return
65
66 def running_build_succeeded_cb(self, running_build):
67 label = gtk.Label("Build completed, start another build?")
68 dialog = gtk.Dialog("Build complete",
69 self,
70 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
71 (gtk.STOCK_NO, gtk.RESPONSE_NO,
72 gtk.STOCK_YES, gtk.RESPONSE_YES))
73 dialog.vbox.pack_start(label)
74 label.show()
75 response = dialog.run()
76 dialog.destroy()
77 if not response == gtk.RESPONSE_YES:
78 self.model.reset() # NOTE: really?
79 self.nb.set_current_page(0)
80 return
81
82 def machine_combo_changed_cb(self, combo, handler):
83 mach = combo.get_active_text()
84 if mach != self.curr_mach:
85 self.curr_mach = mach
86 handler.set_machine(mach)
87
88 def update_machines(self, handler, machines):
89 active = 0
90 for machine in machines:
91 self.machine_combo.append_text(machine)
92 if machine == self.curr_mach:
93 self.machine_combo.set_active(active)
94 active = active + 1
95 self.machine_combo.connect("changed", self.machine_combo_changed_cb, handler)
96
97 def update_distros(self, handler, distros):
98 # FIXME: when we add UI for changing distro this will be used
99 return
100
101 def data_generated(self, handler):
102 self.generating = False
103
104 def spin_idle_func(self, pbar):
105 if self.generating:
106 pbar.pulse()
107 return True
108 else:
109 pbar.hide()
110 return False
111
112 def busy(self, handler):
113 self.generating = True
114 pbar = ProgressBar(self)
115 pbar.connect("delete-event", gtk.main_quit) # NOTE: questionable...
116 pbar.pulse()
117 gobject.timeout_add (200,
118 self.spin_idle_func,
119 pbar)
120
121 def update_model(self, model):
122 pkgsaz_model = gtk.TreeModelSort(self.model.packages_model())
123 pkgsaz_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING)
124 self.pkgsaz_tree.set_model(pkgsaz_model)
125
126 # FIXME: need to implement a custom sort function, as otherwise the column
127 # is re-ordered when toggling the inclusion state (COL_INC)
128 pkgsgrp_model = gtk.TreeModelSort(self.model.packages_model())
129 pkgsgrp_model.set_sort_column_id(self.model.COL_GROUP, gtk.SORT_ASCENDING)
130 self.pkgsgrp_tree.set_model(pkgsgrp_model)
131
132 self.contents_tree.set_model(self.model.contents_model())
133 self.images_tree.set_model(self.model.images_model())
134 self.tasks_tree.set_model(self.model.tasks_model())
135
136 def reset_clicked_cb(self, button):
137 label = gtk.Label("Are you sure you want to reset the image contents?")
138 dialog = gtk.Dialog("Confirm reset", self,
139 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
140 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
141 gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
142 dialog.vbox.pack_start(label)
143 label.show()
144 response = dialog.run()
145 dialog.destroy()
146 if (response == gtk.RESPONSE_ACCEPT):
147 self.model.reset()
148 return
149
150 def bake_clicked_cb(self, button):
151 if not self.model.targets_contains_image():
152 label = gtk.Label("No image was selected. Just build the selected packages?")
153 dialog = gtk.Dialog("Warning, no image selected",
154 self,
155 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
156 (gtk.STOCK_NO, gtk.RESPONSE_NO,
157 gtk.STOCK_YES, gtk.RESPONSE_YES))
158 dialog.vbox.pack_start(label)
159 label.show()
160 response = dialog.run()
161 dialog.destroy()
162 if not response == gtk.RESPONSE_YES:
163 return
164
165 # Note: We could "squash" the targets list to only include things not brought in by an image
166 task_list = self.model.get_targets()
167 if len(task_list):
168 tasks = " ".join(task_list)
169 # TODO: show a confirmation dialog
170 print("Including these extra tasks in IMAGE_INSTALL: %s" % tasks)
171 else:
172 return
173
174 self.nb.set_current_page(1)
175 self.handler.run_build(task_list)
176
177 return
178
179 def advanced_expander_cb(self, expander, param):
180 return
181
182 def images(self):
183 self.images_tree = gtk.TreeView()
184 self.images_tree.set_headers_visible(True)
185 self.images_tree.set_headers_clickable(False)
186 self.images_tree.set_enable_search(True)
187 self.images_tree.set_search_column(0)
188 self.images_tree.get_selection().set_mode(gtk.SELECTION_NONE)
189
190 col = gtk.TreeViewColumn('Package')
191 col1 = gtk.TreeViewColumn('Description')
192 col2 = gtk.TreeViewColumn('License')
193 col3 = gtk.TreeViewColumn('Include')
194 col3.set_resizable(False)
195
196 self.images_tree.append_column(col)
197 self.images_tree.append_column(col1)
198 self.images_tree.append_column(col2)
199 self.images_tree.append_column(col3)
200
201 cell = gtk.CellRendererText()
202 cell1 = gtk.CellRendererText()
203 cell2 = gtk.CellRendererText()
204 cell3 = gtk.CellRendererToggle()
205 cell3.set_property('activatable', True)
206 cell3.connect("toggled", self.toggle_include_cb, self.images_tree)
207
208 col.pack_start(cell, True)
209 col1.pack_start(cell1, True)
210 col2.pack_start(cell2, True)
211 col3.pack_start(cell3, True)
212
213 col.set_attributes(cell, text=self.model.COL_NAME)
214 col1.set_attributes(cell1, text=self.model.COL_DESC)
215 col2.set_attributes(cell2, text=self.model.COL_LIC)
216 col3.set_attributes(cell3, active=self.model.COL_INC)
217
218 self.images_tree.show()
219
220 scroll = gtk.ScrolledWindow()
221 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
222 scroll.set_shadow_type(gtk.SHADOW_IN)
223 scroll.add(self.images_tree)
224
225 return scroll
226
227 def toggle_package(self, path, model):
228 # Convert path to path in original model
229 opath = model.convert_path_to_child_path(path)
230 # current include status
231 inc = self.model[opath][self.model.COL_INC]
232 if inc:
233 self.model.mark(opath)
234 self.model.sweep_up()
235 #self.model.remove_package_full(cpath)
236 else:
237 self.model.include_item(opath)
238 return
239
240 def remove_package_cb(self, cell, path):
241 model = self.model.contents_model()
242 label = gtk.Label("Are you sure you want to remove this item?")
243 dialog = gtk.Dialog("Confirm removal", self,
244 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
245 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
246 gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
247 dialog.vbox.pack_start(label)
248 label.show()
249 response = dialog.run()
250 dialog.destroy()
251 if (response == gtk.RESPONSE_ACCEPT):
252 self.toggle_package(path, model)
253
254 def toggle_include_cb(self, cell, path, tv):
255 model = tv.get_model()
256 self.toggle_package(path, model)
257
258 def toggle_pkg_include_cb(self, cell, path, tv):
259 # there's an extra layer of models in the packages case.
260 sort_model = tv.get_model()
261 cpath = sort_model.convert_path_to_child_path(path)
262 self.toggle_package(cpath, sort_model.get_model())
263
264 def pkgsaz(self):
265 self.pkgsaz_tree = gtk.TreeView()
266 self.pkgsaz_tree.set_headers_visible(True)
267 self.pkgsaz_tree.set_headers_clickable(True)
268 self.pkgsaz_tree.set_enable_search(True)
269 self.pkgsaz_tree.set_search_column(0)
270 self.pkgsaz_tree.get_selection().set_mode(gtk.SELECTION_NONE)
271
272 col = gtk.TreeViewColumn('Package')
273 col1 = gtk.TreeViewColumn('Description')
274 col1.set_resizable(True)
275 col2 = gtk.TreeViewColumn('License')
276 col2.set_resizable(True)
277 col3 = gtk.TreeViewColumn('Group')
278 col4 = gtk.TreeViewColumn('Include')
279 col4.set_resizable(False)
280
281 self.pkgsaz_tree.append_column(col)
282 self.pkgsaz_tree.append_column(col1)
283 self.pkgsaz_tree.append_column(col2)
284 self.pkgsaz_tree.append_column(col3)
285 self.pkgsaz_tree.append_column(col4)
286
287 cell = gtk.CellRendererText()
288 cell1 = gtk.CellRendererText()
289 cell1.set_property('width-chars', 20)
290 cell2 = gtk.CellRendererText()
291 cell2.set_property('width-chars', 20)
292 cell3 = gtk.CellRendererText()
293 cell4 = gtk.CellRendererToggle()
294 cell4.set_property('activatable', True)
295 cell4.connect("toggled", self.toggle_pkg_include_cb, self.pkgsaz_tree)
296
297 col.pack_start(cell, True)
298 col1.pack_start(cell1, True)
299 col2.pack_start(cell2, True)
300 col3.pack_start(cell3, True)
301 col4.pack_start(cell4, True)
302
303 col.set_attributes(cell, text=self.model.COL_NAME)
304 col1.set_attributes(cell1, text=self.model.COL_DESC)
305 col2.set_attributes(cell2, text=self.model.COL_LIC)
306 col3.set_attributes(cell3, text=self.model.COL_GROUP)
307 col4.set_attributes(cell4, active=self.model.COL_INC)
308
309 self.pkgsaz_tree.show()
310
311 scroll = gtk.ScrolledWindow()
312 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
313 scroll.set_shadow_type(gtk.SHADOW_IN)
314 scroll.add(self.pkgsaz_tree)
315
316 return scroll
317
318 def pkgsgrp(self):
319 self.pkgsgrp_tree = gtk.TreeView()
320 self.pkgsgrp_tree.set_headers_visible(True)
321 self.pkgsgrp_tree.set_headers_clickable(False)
322 self.pkgsgrp_tree.set_enable_search(True)
323 self.pkgsgrp_tree.set_search_column(0)
324 self.pkgsgrp_tree.get_selection().set_mode(gtk.SELECTION_NONE)
325
326 col = gtk.TreeViewColumn('Package')
327 col1 = gtk.TreeViewColumn('Description')
328 col1.set_resizable(True)
329 col2 = gtk.TreeViewColumn('License')
330 col2.set_resizable(True)
331 col3 = gtk.TreeViewColumn('Group')
332 col4 = gtk.TreeViewColumn('Include')
333 col4.set_resizable(False)
334
335 self.pkgsgrp_tree.append_column(col)
336 self.pkgsgrp_tree.append_column(col1)
337 self.pkgsgrp_tree.append_column(col2)
338 self.pkgsgrp_tree.append_column(col3)
339 self.pkgsgrp_tree.append_column(col4)
340
341 cell = gtk.CellRendererText()
342 cell1 = gtk.CellRendererText()
343 cell1.set_property('width-chars', 20)
344 cell2 = gtk.CellRendererText()
345 cell2.set_property('width-chars', 20)
346 cell3 = gtk.CellRendererText()
347 cell4 = gtk.CellRendererToggle()
348 cell4.set_property("activatable", True)
349 cell4.connect("toggled", self.toggle_pkg_include_cb, self.pkgsgrp_tree)
350
351 col.pack_start(cell, True)
352 col1.pack_start(cell1, True)
353 col2.pack_start(cell2, True)
354 col3.pack_start(cell3, True)
355 col4.pack_start(cell4, True)
356
357 col.set_attributes(cell, text=self.model.COL_NAME)
358 col1.set_attributes(cell1, text=self.model.COL_DESC)
359 col2.set_attributes(cell2, text=self.model.COL_LIC)
360 col3.set_attributes(cell3, text=self.model.COL_GROUP)
361 col4.set_attributes(cell4, active=self.model.COL_INC)
362
363 self.pkgsgrp_tree.show()
364
365 scroll = gtk.ScrolledWindow()
366 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
367 scroll.set_shadow_type(gtk.SHADOW_IN)
368 scroll.add(self.pkgsgrp_tree)
369
370 return scroll
371
372 def tasks(self):
373 self.tasks_tree = gtk.TreeView()
374 self.tasks_tree.set_headers_visible(True)
375 self.tasks_tree.set_headers_clickable(False)
376 self.tasks_tree.set_enable_search(True)
377 self.tasks_tree.set_search_column(0)
378 self.tasks_tree.get_selection().set_mode(gtk.SELECTION_NONE)
379
380 col = gtk.TreeViewColumn('Package')
381 col1 = gtk.TreeViewColumn('Description')
382 col2 = gtk.TreeViewColumn('Include')
383 col2.set_resizable(False)
384
385 self.tasks_tree.append_column(col)
386 self.tasks_tree.append_column(col1)
387 self.tasks_tree.append_column(col2)
388
389 cell = gtk.CellRendererText()
390 cell1 = gtk.CellRendererText()
391 cell2 = gtk.CellRendererToggle()
392 cell2.set_property('activatable', True)
393 cell2.connect("toggled", self.toggle_include_cb, self.tasks_tree)
394
395 col.pack_start(cell, True)
396 col1.pack_start(cell1, True)
397 col2.pack_start(cell2, True)
398
399 col.set_attributes(cell, text=self.model.COL_NAME)
400 col1.set_attributes(cell1, text=self.model.COL_DESC)
401 col2.set_attributes(cell2, active=self.model.COL_INC)
402
403 self.tasks_tree.show()
404
405 scroll = gtk.ScrolledWindow()
406 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
407 scroll.set_shadow_type(gtk.SHADOW_IN)
408 scroll.add(self.tasks_tree)
409
410 return scroll
411
412 def cancel_build(self, button):
413 label = gtk.Label("Do you really want to stop this build?")
414 dialog = gtk.Dialog("Cancel build",
415 self,
416 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
417 (gtk.STOCK_NO, gtk.RESPONSE_NO,
418 gtk.STOCK_YES, gtk.RESPONSE_YES))
419 dialog.vbox.pack_start(label)
420 label.show()
421 response = dialog.run()
422 dialog.destroy()
423 if not response == gtk.RESPONSE_YES:
424 self.handler.cancel_build()
425 return
426
427 def view_build_gui(self):
428 vbox = gtk.VBox(False, 6)
429 vbox.show()
430 build_tv = RunningBuildTreeView()
431 build_tv.show()
432 build_tv.set_model(self.build.model)
433 self.build.model.connect("row-inserted", self.scroll_tv_cb, build_tv)
434 scrolled_view = gtk.ScrolledWindow ()
435 scrolled_view.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
436 scrolled_view.add(build_tv)
437 scrolled_view.show()
438 vbox.pack_start(scrolled_view, expand=True, fill=True)
439 hbox = gtk.HBox(False, 6)
440 hbox.show()
441 vbox.pack_start(hbox, expand=False, fill=False)
442 cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
443 cancel.connect("clicked", self.cancel_build)
444 cancel.show()
445 hbox.pack_end(cancel, expand=False, fill=False)
446
447 return vbox
448
449 def create_build_gui(self):
450 vbox = gtk.VBox(False, 6)
451 vbox.show()
452 hbox = gtk.HBox(False, 6)
453 hbox.show()
454 vbox.pack_start(hbox, expand=False, fill=False)
455
456 label = gtk.Label("Machine:")
457 label.show()
458 hbox.pack_start(label, expand=False, fill=False, padding=6)
459 self.machine_combo = gtk.combo_box_new_text()
460 self.machine_combo.set_active(0)
461 self.machine_combo.show()
462 self.machine_combo.set_tooltip_text("Selects the architecture of the target board for which you would like to build an image.")
463 hbox.pack_start(self.machine_combo, expand=False, fill=False, padding=6)
464
465 ins = gtk.Notebook()
466 vbox.pack_start(ins, expand=True, fill=True)
467 ins.set_show_tabs(True)
468 label = gtk.Label("Images")
469 label.show()
470 ins.append_page(self.images(), tab_label=label)
471 label = gtk.Label("Tasks")
472 label.show()
473 ins.append_page(self.tasks(), tab_label=label)
474 label = gtk.Label("Packages (by Group)")
475 label.show()
476 ins.append_page(self.pkgsgrp(), tab_label=label)
477 label = gtk.Label("Packages (by Name)")
478 label.show()
479 ins.append_page(self.pkgsaz(), tab_label=label)
480 ins.set_current_page(0)
481 ins.show_all()
482
483 hbox = gtk.HBox()
484 hbox.show()
485 vbox.pack_start(hbox, expand=False, fill=False)
486 label = gtk.Label("Image contents:")
487 label.show()
488 hbox.pack_start(label, expand=False, fill=False, padding=6)
489 con = self.contents()
490 con.show()
491 vbox.pack_start(con, expand=True, fill=True)
492
493 #advanced = gtk.Expander(label="Advanced")
494 #advanced.connect("notify::expanded", self.advanced_expander_cb)
495 #advanced.show()
496 #vbox.pack_start(advanced, expand=False, fill=False)
497
498 hbox = gtk.HBox()
499 hbox.show()
500 vbox.pack_start(hbox, expand=False, fill=False)
501 bake = gtk.Button("Bake")
502 bake.connect("clicked", self.bake_clicked_cb)
503 bake.show()
504 hbox.pack_end(bake, expand=False, fill=False, padding=6)
505 reset = gtk.Button("Reset")
506 reset.connect("clicked", self.reset_clicked_cb)
507 reset.show()
508 hbox.pack_end(reset, expand=False, fill=False, padding=6)
509
510 return vbox
511
512 def contents(self):
513 self.contents_tree = gtk.TreeView()
514 self.contents_tree.set_headers_visible(True)
515 self.contents_tree.get_selection().set_mode(gtk.SELECTION_NONE)
516
517 # allow searching in the package column
518 self.contents_tree.set_search_column(0)
519
520 col = gtk.TreeViewColumn('Package')
521 col.set_sort_column_id(0)
522 col1 = gtk.TreeViewColumn('Brought in by')
523 col1.set_resizable(True)
524 col2 = gtk.TreeViewColumn('Remove')
525 col2.set_expand(False)
526
527 self.contents_tree.append_column(col)
528 self.contents_tree.append_column(col1)
529 self.contents_tree.append_column(col2)
530
531 cell = gtk.CellRendererText()
532 cell1 = gtk.CellRendererText()
533 cell1.set_property('width-chars', 20)
534 cell2 = gtk.CellRendererToggle()
535 cell2.connect("toggled", self.remove_package_cb)
536
537 col.pack_start(cell, True)
538 col1.pack_start(cell1, True)
539 col2.pack_start(cell2, True)
540
541 col.set_attributes(cell, text=self.model.COL_NAME)
542 col1.set_attributes(cell1, text=self.model.COL_BINB)
543 col2.set_attributes(cell2, active=self.model.COL_INC)
544
545 self.contents_tree.show()
546
547 scroll = gtk.ScrolledWindow()
548 scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
549 scroll.set_shadow_type(gtk.SHADOW_IN)
550 scroll.add(self.contents_tree)
551
552 return scroll
553
554def main (server, eventHandler):
555 gobject.threads_init()
556 gtk.gdk.threads_init()
557
558 taskmodel = TaskListModel()
559 handler = HobHandler(taskmodel, server)
560 mach = server.runCommand(["getVariable", "MACHINE"])
561 distro = server.runCommand(["getVariable", "DISTRO"])
562
563 window = MainWindow(taskmodel, handler, mach, distro)
564 window.show_all ()
565 handler.connect("machines-updated", window.update_machines)
566 handler.connect("distros-updated", window.update_distros)
567 handler.connect("generating-data", window.busy)
568 handler.connect("data-generated", window.data_generated)
569 pbar = ProgressBar(window)
570 pbar.connect("delete-event", gtk.main_quit)
571
572 try:
573 # kick the while thing off
574 handler.current_command = "findConfigFilesDistro"
575 server.runCommand(["findConfigFiles", "DISTRO"])
576 except xmlrpclib.Fault:
577 print("XMLRPC Fault getting commandline:\n %s" % x)
578 return 1
579
580 # This timeout function regularly probes the event queue to find out if we
581 # have any messages waiting for us.
582 gobject.timeout_add (100,
583 handler.event_handle_idle_func,
584 eventHandler,
585 window.build,
586 pbar)
587
588 try:
589 gtk.main()
590 except EnvironmentError as ioerror:
591 # ignore interrupted io
592 if ioerror.args[0] == 4:
593 pass
594 finally:
595 server.runCommand(["stateStop"])
596