summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/ui/crumbs/imageconfigurationpage.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/ui/crumbs/imageconfigurationpage.py')
-rw-r--r--bitbake/lib/bb/ui/crumbs/imageconfigurationpage.py561
1 files changed, 561 insertions, 0 deletions
diff --git a/bitbake/lib/bb/ui/crumbs/imageconfigurationpage.py b/bitbake/lib/bb/ui/crumbs/imageconfigurationpage.py
new file mode 100644
index 0000000000..2766bea8c7
--- /dev/null
+++ b/bitbake/lib/bb/ui/crumbs/imageconfigurationpage.py
@@ -0,0 +1,561 @@
1#!/usr/bin/env python
2#
3# BitBake Graphical GTK User Interface
4#
5# Copyright (C) 2012 Intel Corporation
6#
7# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
8# Authored by Shane Wang <shane.wang@intel.com>
9#
10# This program is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License version 2 as
12# published by the Free Software Foundation.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License along
20# with this program; if not, write to the Free Software Foundation, Inc.,
21# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
23import gtk
24import glib
25import re
26from bb.ui.crumbs.progressbar import HobProgressBar
27from bb.ui.crumbs.hobcolor import HobColors
28from bb.ui.crumbs.hobwidget import hic, HobImageButton, HobInfoButton, HobAltButton, HobButton
29from bb.ui.crumbs.hoblistmodel import RecipeListModel
30from bb.ui.crumbs.hobpages import HobPage
31from bb.ui.crumbs.hig.retrieveimagedialog import RetrieveImageDialog
32
33#
34# ImageConfigurationPage
35#
36class ImageConfigurationPage (HobPage):
37
38 __dummy_machine__ = "--select a machine--"
39 __dummy_image__ = "--select an image recipe--"
40 __custom_image__ = "Select from my image recipes"
41
42 def __init__(self, builder):
43 super(ImageConfigurationPage, self).__init__(builder, "Image configuration")
44
45 self.image_combo_id = None
46 # we use machine_combo_changed_by_manual to identify the machine is changed by code
47 # or by manual. If by manual, all user's recipe selection and package selection are
48 # cleared.
49 self.machine_combo_changed_by_manual = True
50 self.stopping = False
51 self.warning_shift = 0
52 self.custom_image_selected = None
53 self.create_visual_elements()
54
55 def create_visual_elements(self):
56 # create visual elements
57 self.toolbar = gtk.Toolbar()
58 self.toolbar.set_orientation(gtk.ORIENTATION_HORIZONTAL)
59 self.toolbar.set_style(gtk.TOOLBAR_BOTH)
60
61 my_images_button = self.append_toolbar_button(self.toolbar,
62 "Images",
63 hic.ICON_IMAGES_DISPLAY_FILE,
64 hic.ICON_IMAGES_HOVER_FILE,
65 "Open previously built images",
66 self.my_images_button_clicked_cb)
67 settings_button = self.append_toolbar_button(self.toolbar,
68 "Settings",
69 hic.ICON_SETTINGS_DISPLAY_FILE,
70 hic.ICON_SETTINGS_HOVER_FILE,
71 "View additional build settings",
72 self.settings_button_clicked_cb)
73
74 self.config_top_button = self.add_onto_top_bar(self.toolbar)
75
76 self.gtable = gtk.Table(40, 40, True)
77 self.create_config_machine()
78 self.create_config_baseimg()
79 self.config_build_button = self.create_config_build_button()
80
81 def _remove_all_widget(self):
82 children = self.gtable.get_children() or []
83 for child in children:
84 self.gtable.remove(child)
85 children = self.box_group_area.get_children() or []
86 for child in children:
87 self.box_group_area.remove(child)
88 children = self.get_children() or []
89 for child in children:
90 self.remove(child)
91
92 def _pack_components(self, pack_config_build_button = False):
93 self._remove_all_widget()
94 self.pack_start(self.config_top_button, expand=False, fill=False)
95 self.pack_start(self.group_align, expand=True, fill=True)
96
97 self.box_group_area.pack_start(self.gtable, expand=True, fill=True)
98 if pack_config_build_button:
99 self.box_group_area.pack_end(self.config_build_button, expand=False, fill=False)
100 else:
101 box = gtk.HBox(False, 6)
102 box.show()
103 subbox = gtk.HBox(False, 0)
104 subbox.set_size_request(205, 49)
105 subbox.show()
106 box.add(subbox)
107 self.box_group_area.pack_end(box, False, False)
108
109 def show_machine(self):
110 self.progress_bar.reset()
111 self._pack_components(pack_config_build_button = False)
112 self.set_config_machine_layout(show_progress_bar = False)
113 self.show_all()
114
115 def update_progress_bar(self, title, fraction, status=None):
116 if self.stopping == False:
117 self.progress_bar.update(fraction)
118 self.progress_bar.set_text(title)
119 self.progress_bar.set_rcstyle(status)
120
121 def show_info_populating(self):
122 self._pack_components(pack_config_build_button = False)
123 self.set_config_machine_layout(show_progress_bar = True)
124 self.show_all()
125
126 def show_info_populated(self):
127 self.progress_bar.reset()
128 self._pack_components(pack_config_build_button = False)
129 self.set_config_machine_layout(show_progress_bar = False)
130 self.set_config_baseimg_layout()
131 self.show_all()
132
133 def show_baseimg_selected(self):
134 self.progress_bar.reset()
135 self._pack_components(pack_config_build_button = True)
136 self.set_config_machine_layout(show_progress_bar = False)
137 self.set_config_baseimg_layout()
138 self.show_all()
139 if self.builder.recipe_model.get_selected_image() == self.builder.recipe_model.__custom_image__:
140 self.just_bake_button.hide()
141
142 def add_warnings_bar(self):
143 #create the warnings bar shown when recipes parsing generates warnings
144 color = HobColors.KHAKI
145 warnings_bar = gtk.EventBox()
146 warnings_bar.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
147 warnings_bar.set_flags(gtk.CAN_DEFAULT)
148 warnings_bar.grab_default()
149
150 build_stop_tab = gtk.Table(10, 20, True)
151 warnings_bar.add(build_stop_tab)
152
153 icon = gtk.Image()
154 icon_pix_buffer = gtk.gdk.pixbuf_new_from_file(hic.ICON_INDI_ALERT_FILE)
155 icon.set_from_pixbuf(icon_pix_buffer)
156 build_stop_tab.attach(icon, 0, 2, 0, 10)
157
158 label = gtk.Label()
159 label.set_alignment(0.0, 0.5)
160 warnings_nb = len(self.builder.parsing_warnings)
161 if warnings_nb == 1:
162 label.set_markup("<span size='x-large'><b>1 recipe parsing warning</b></span>")
163 else:
164 label.set_markup("<span size='x-large'><b>%s recipe parsing warnings</b></span>" % warnings_nb)
165 build_stop_tab.attach(label, 2, 12, 0, 10)
166
167 view_warnings_button = HobButton("View warnings")
168 view_warnings_button.connect('clicked', self.view_warnings_button_clicked_cb)
169 build_stop_tab.attach(view_warnings_button, 15, 19, 1, 9)
170
171 return warnings_bar
172
173 def disable_warnings_bar(self):
174 if self.builder.parsing_warnings:
175 if hasattr(self, 'warnings_bar'):
176 self.warnings_bar.hide_all()
177 self.builder.parsing_warnings = []
178
179 def create_config_machine(self):
180 self.machine_title = gtk.Label()
181 self.machine_title.set_alignment(0.0, 0.5)
182 mark = "<span %s>Select a machine</span>" % self.span_tag('x-large', 'bold')
183 self.machine_title.set_markup(mark)
184
185 self.machine_title_desc = gtk.Label()
186 self.machine_title_desc.set_alignment(0.0, 0.5)
187 mark = ("<span %s>Your selection is the profile of the target machine for which you"
188 " are building the image.\n</span>") % (self.span_tag('medium'))
189 self.machine_title_desc.set_markup(mark)
190
191 self.machine_combo = gtk.combo_box_new_text()
192 self.machine_combo.connect("changed", self.machine_combo_changed_cb)
193
194 icon_file = hic.ICON_LAYERS_DISPLAY_FILE
195 hover_file = hic.ICON_LAYERS_HOVER_FILE
196 self.layer_button = HobImageButton("Layers", "Add support for machines, software, etc.",
197 icon_file, hover_file)
198 self.layer_button.connect("clicked", self.layer_button_clicked_cb)
199
200 markup = "Layers are a powerful mechanism to extend the Yocto Project "
201 markup += "with your own functionality.\n"
202 markup += "For more on layers, check the <a href=\""
203 markup += "http://www.yoctoproject.org/docs/current/dev-manual/"
204 markup += "dev-manual.html#understanding-and-using-layers\">reference manual</a>."
205 self.layer_info_icon = HobInfoButton("<b>Layers</b>" + "*" + markup, self.get_parent())
206 self.progress_bar = HobProgressBar()
207 self.stop_button = HobAltButton("Stop")
208 self.stop_button.connect("clicked", self.stop_button_clicked_cb)
209 self.machine_separator = gtk.HSeparator()
210
211 def set_config_machine_layout(self, show_progress_bar = False):
212 self.gtable.attach(self.machine_title, 0, 40, 0, 4)
213 self.gtable.attach(self.machine_title_desc, 0, 40, 4, 6)
214 self.gtable.attach(self.machine_combo, 0, 12, 7, 10)
215 self.gtable.attach(self.layer_button, 14, 36, 7, 12)
216 self.gtable.attach(self.layer_info_icon, 36, 40, 7, 11)
217 if show_progress_bar:
218 #self.gtable.attach(self.progress_box, 0, 40, 15, 18)
219 self.gtable.attach(self.progress_bar, 0, 37, 15, 18)
220 self.gtable.attach(self.stop_button, 37, 40, 15, 18, 0, 0)
221 if self.builder.parsing_warnings:
222 self.warnings_bar = self.add_warnings_bar()
223 self.gtable.attach(self.warnings_bar, 0, 40, 14, 18)
224 self.warning_shift = 4
225 else:
226 self.warning_shift = 0
227 self.gtable.attach(self.machine_separator, 0, 40, 13, 14)
228
229 def create_config_baseimg(self):
230 self.image_title = gtk.Label()
231 self.image_title.set_alignment(0, 1.0)
232 mark = "<span %s>Select an image recipe</span>" % self.span_tag('x-large', 'bold')
233 self.image_title.set_markup(mark)
234
235 self.image_title_desc = gtk.Label()
236 self.image_title_desc.set_alignment(0, 0.5)
237
238 mark = ("<span %s>Image recipes are a starting point for the type of image you want. "
239 "You can build them as \n"
240 "they are or edit them to suit your needs.\n</span>") % self.span_tag('medium')
241 self.image_title_desc.set_markup(mark)
242
243 self.image_combo = gtk.combo_box_new_text()
244 self.image_combo.set_row_separator_func(self.combo_separator_func, None)
245 self.image_combo_id = self.image_combo.connect("changed", self.image_combo_changed_cb)
246
247 self.image_desc = gtk.Label()
248 self.image_desc.set_alignment(0.0, 0.5)
249 self.image_desc.set_size_request(256, -1)
250 self.image_desc.set_justify(gtk.JUSTIFY_LEFT)
251 self.image_desc.set_line_wrap(True)
252
253 # button to view recipes
254 icon_file = hic.ICON_RCIPE_DISPLAY_FILE
255 hover_file = hic.ICON_RCIPE_HOVER_FILE
256 self.view_adv_configuration_button = HobImageButton("Advanced configuration",
257 "Select image types, package formats, etc",
258 icon_file, hover_file)
259 self.view_adv_configuration_button.connect("clicked", self.view_adv_configuration_button_clicked_cb)
260
261 self.image_separator = gtk.HSeparator()
262
263 def combo_separator_func(self, model, iter, user_data):
264 name = model.get_value(iter, 0)
265 if name == "--Separator--":
266 return True
267
268 def set_config_baseimg_layout(self):
269 self.gtable.attach(self.image_title, 0, 40, 15+self.warning_shift, 17+self.warning_shift)
270 self.gtable.attach(self.image_title_desc, 0, 40, 18+self.warning_shift, 22+self.warning_shift)
271 self.gtable.attach(self.image_combo, 0, 12, 23+self.warning_shift, 26+self.warning_shift)
272 self.gtable.attach(self.image_desc, 0, 12, 27+self.warning_shift, 33+self.warning_shift)
273 self.gtable.attach(self.view_adv_configuration_button, 14, 36, 23+self.warning_shift, 28+self.warning_shift)
274 self.gtable.attach(self.image_separator, 0, 40, 35+self.warning_shift, 36+self.warning_shift)
275
276 def create_config_build_button(self):
277 # Create the "Build packages" and "Build image" buttons at the bottom
278 button_box = gtk.HBox(False, 6)
279
280 # create button "Build image"
281 self.just_bake_button = HobButton("Build image")
282 self.just_bake_button.set_tooltip_text("Build the image recipe as it is")
283 self.just_bake_button.connect("clicked", self.just_bake_button_clicked_cb)
284 button_box.pack_end(self.just_bake_button, expand=False, fill=False)
285
286 # create button "Edit image recipe"
287 self.edit_image_button = HobAltButton("Edit image recipe")
288 self.edit_image_button.set_tooltip_text("Customize the recipes and packages to be included in your image")
289 self.edit_image_button.connect("clicked", self.edit_image_button_clicked_cb)
290 button_box.pack_end(self.edit_image_button, expand=False, fill=False)
291
292 return button_box
293
294 def stop_button_clicked_cb(self, button):
295 self.stopping = True
296 self.progress_bar.set_text("Stopping recipe parsing")
297 self.progress_bar.set_rcstyle("stop")
298 self.builder.cancel_parse_sync()
299
300 def view_warnings_button_clicked_cb(self, button):
301 self.builder.show_warning_dialog()
302
303 def machine_combo_changed_idle_cb(self):
304 self.builder.window.set_cursor(None)
305
306 def machine_combo_changed_cb(self, machine_combo):
307 self.stopping = False
308 self.builder.parsing_warnings = []
309 combo_item = machine_combo.get_active_text()
310 if not combo_item or combo_item == self.__dummy_machine__:
311 return
312
313 self.builder.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
314 self.builder.wait(0.1) #wait for combo and cursor to update
315
316 # remove __dummy_machine__ item from the store list after first user selection
317 # because it is no longer valid
318 combo_store = machine_combo.get_model()
319 if len(combo_store) and (combo_store[0][0] == self.__dummy_machine__):
320 machine_combo.remove_text(0)
321
322 self.builder.configuration.curr_mach = combo_item
323 if self.machine_combo_changed_by_manual:
324 self.builder.configuration.clear_selection()
325 # reset machine_combo_changed_by_manual
326 self.machine_combo_changed_by_manual = True
327
328 self.builder.configuration.selected_image = None
329
330 # Do reparse recipes
331 self.builder.populate_recipe_package_info_async()
332
333 glib.idle_add(self.machine_combo_changed_idle_cb)
334
335 def update_machine_combo(self):
336 self.disable_warnings_bar()
337 all_machines = [self.__dummy_machine__] + self.builder.parameters.all_machines
338
339 model = self.machine_combo.get_model()
340 model.clear()
341 for machine in all_machines:
342 self.machine_combo.append_text(machine)
343 self.machine_combo.set_active(0)
344
345 def switch_machine_combo(self):
346 self.disable_warnings_bar()
347 self.machine_combo_changed_by_manual = False
348 model = self.machine_combo.get_model()
349 active = 0
350 while active < len(model):
351 if model[active][0] == self.builder.configuration.curr_mach:
352 self.machine_combo.set_active(active)
353 return
354 active += 1
355
356 if model[0][0] != self.__dummy_machine__:
357 self.machine_combo.insert_text(0, self.__dummy_machine__)
358
359 self.machine_combo.set_active(0)
360
361 def update_image_desc(self):
362 desc = ""
363 selected_image = self.image_combo.get_active_text()
364 if selected_image and selected_image in self.builder.recipe_model.pn_path.keys():
365 image_path = self.builder.recipe_model.pn_path[selected_image]
366 image_iter = self.builder.recipe_model.get_iter(image_path)
367 desc = self.builder.recipe_model.get_value(image_iter, self.builder.recipe_model.COL_DESC)
368
369 mark = ("<span %s>%s</span>\n") % (self.span_tag('small'), desc)
370 self.image_desc.set_markup(mark)
371
372 def image_combo_changed_idle_cb(self, selected_image, selected_recipes, selected_packages):
373 self.builder.update_recipe_model(selected_image, selected_recipes)
374 self.builder.update_package_model(selected_packages)
375 self.builder.window_sensitive(True)
376
377 def image_combo_changed_cb(self, combo):
378 self.builder.window_sensitive(False)
379 selected_image = self.image_combo.get_active_text()
380 if selected_image == self.__custom_image__:
381 topdir = self.builder.get_topdir()
382 images_dir = topdir + "/recipes/images/custom/"
383 self.builder.ensure_dir(images_dir)
384
385 dialog = RetrieveImageDialog(images_dir, "Select from my image recipes",
386 self.builder, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
387 response = dialog.run()
388 if response == gtk.RESPONSE_OK:
389 image_name = dialog.get_filename()
390 head, tail = os.path.split(image_name)
391 selected_image = os.path.splitext(tail)[0]
392 self.custom_image_selected = selected_image
393 self.update_image_combo(self.builder.recipe_model, selected_image)
394 else:
395 selected_image = self.__dummy_image__
396 self.update_image_combo(self.builder.recipe_model, None)
397 dialog.destroy()
398 else:
399 if self.custom_image_selected:
400 self.custom_image_selected = None
401 self.update_image_combo(self.builder.recipe_model, selected_image)
402
403 if not selected_image or (selected_image == self.__dummy_image__):
404 self.builder.window_sensitive(True)
405 self.just_bake_button.hide()
406 self.edit_image_button.hide()
407 return
408
409 # remove __dummy_image__ item from the store list after first user selection
410 # because it is no longer valid
411 combo_store = combo.get_model()
412 if len(combo_store) and (combo_store[0][0] == self.__dummy_image__):
413 combo.remove_text(0)
414
415 self.builder.customized = False
416
417 selected_recipes = []
418
419 image_path = self.builder.recipe_model.pn_path[selected_image]
420 image_iter = self.builder.recipe_model.get_iter(image_path)
421 selected_packages = self.builder.recipe_model.get_value(image_iter, self.builder.recipe_model.COL_INSTALL).split()
422 self.update_image_desc()
423
424 self.builder.recipe_model.reset()
425 self.builder.package_model.reset()
426
427 self.show_baseimg_selected()
428
429 if selected_image == self.builder.recipe_model.__custom_image__:
430 self.just_bake_button.hide()
431
432 glib.idle_add(self.image_combo_changed_idle_cb, selected_image, selected_recipes, selected_packages)
433
434 def _image_combo_connect_signal(self):
435 if not self.image_combo_id:
436 self.image_combo_id = self.image_combo.connect("changed", self.image_combo_changed_cb)
437
438 def _image_combo_disconnect_signal(self):
439 if self.image_combo_id:
440 self.image_combo.disconnect(self.image_combo_id)
441 self.image_combo_id = None
442
443 def update_image_combo(self, recipe_model, selected_image):
444 # Update the image combo according to the images in the recipe_model
445 # populate image combo
446 filter = {RecipeListModel.COL_TYPE : ['image']}
447 image_model = recipe_model.tree_model(filter)
448 image_model.set_sort_column_id(recipe_model.COL_NAME, gtk.SORT_ASCENDING)
449 active = 0
450 cnt = 0
451
452 white_pattern = []
453 if self.builder.parameters.image_white_pattern:
454 for i in self.builder.parameters.image_white_pattern.split():
455 white_pattern.append(re.compile(i))
456
457 black_pattern = []
458 if self.builder.parameters.image_black_pattern:
459 for i in self.builder.parameters.image_black_pattern.split():
460 black_pattern.append(re.compile(i))
461 black_pattern.append(re.compile("hob-image"))
462 black_pattern.append(re.compile("edited(-[0-9]*)*.bb$"))
463
464 it = image_model.get_iter_first()
465 self._image_combo_disconnect_signal()
466 model = self.image_combo.get_model()
467 model.clear()
468 # Set a indicator text to combo store when first open
469 if not selected_image:
470 self.image_combo.append_text(self.__dummy_image__)
471 cnt = cnt + 1
472
473 self.image_combo.append_text(self.__custom_image__)
474 self.image_combo.append_text("--Separator--")
475 cnt = cnt + 2
476
477 topdir = self.builder.get_topdir()
478 # append and set active
479 while it:
480 path = image_model.get_path(it)
481 it = image_model.iter_next(it)
482 image_name = image_model[path][recipe_model.COL_NAME]
483 if image_name == self.builder.recipe_model.__custom_image__:
484 continue
485
486 if black_pattern:
487 allow = True
488 for pattern in black_pattern:
489 if pattern.search(image_name):
490 allow = False
491 break
492 elif white_pattern:
493 allow = False
494 for pattern in white_pattern:
495 if pattern.search(image_name):
496 allow = True
497 break
498 else:
499 allow = True
500
501 file_name = image_model[path][recipe_model.COL_FILE]
502 if file_name and topdir in file_name:
503 allow = False
504
505 if allow:
506 self.image_combo.append_text(image_name)
507 if image_name == selected_image:
508 active = cnt
509 cnt = cnt + 1
510 self.image_combo.append_text(self.builder.recipe_model.__custom_image__)
511
512 if selected_image == self.builder.recipe_model.__custom_image__:
513 active = cnt
514
515 if self.custom_image_selected:
516 self.image_combo.append_text("--Separator--")
517 self.image_combo.append_text(self.custom_image_selected)
518 cnt = cnt + 2
519 if self.custom_image_selected == selected_image:
520 active = cnt
521
522 self.image_combo.set_active(active)
523
524 if active != 0:
525 self.show_baseimg_selected()
526
527 self._image_combo_connect_signal()
528
529 def layer_button_clicked_cb(self, button):
530 # Create a layer selection dialog
531 self.builder.show_layer_selection_dialog()
532
533 def view_adv_configuration_button_clicked_cb(self, button):
534 # Create an advanced settings dialog
535 response, settings_changed = self.builder.show_adv_settings_dialog()
536 if not response:
537 return
538 if settings_changed:
539 self.builder.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
540 self.builder.wait(0.1) #wait for adv_settings_dialog to terminate
541 self.builder.reparse_post_adv_settings()
542 self.builder.window.set_cursor(None)
543
544 def just_bake_button_clicked_cb(self, button):
545 self.builder.parsing_warnings = []
546 self.builder.just_bake()
547
548 def edit_image_button_clicked_cb(self, button):
549 self.builder.set_base_image()
550 self.builder.show_recipes()
551
552 def my_images_button_clicked_cb(self, button):
553 self.builder.show_load_my_images_dialog()
554
555 def settings_button_clicked_cb(self, button):
556 # Create an advanced settings dialog
557 response, settings_changed = self.builder.show_simple_settings_dialog()
558 if not response:
559 return
560 if settings_changed:
561 self.builder.reparse_post_adv_settings()