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.py559
1 files changed, 559 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..79709d0d97
--- /dev/null
+++ b/bitbake/lib/bb/ui/crumbs/imageconfigurationpage.py
@@ -0,0 +1,559 @@
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.builder.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
308 self.builder.wait(0.1) #wait for combo and cursor to update
309 self.stopping = False
310 self.builder.parsing_warnings = []
311 combo_item = machine_combo.get_active_text()
312 if not combo_item or combo_item == self.__dummy_machine__:
313 return
314
315 # remove __dummy_machine__ item from the store list after first user selection
316 # because it is no longer valid
317 combo_store = machine_combo.get_model()
318 if len(combo_store) and (combo_store[0][0] == self.__dummy_machine__):
319 machine_combo.remove_text(0)
320
321 self.builder.configuration.curr_mach = combo_item
322 if self.machine_combo_changed_by_manual:
323 self.builder.configuration.clear_selection()
324 # reset machine_combo_changed_by_manual
325 self.machine_combo_changed_by_manual = True
326
327 self.builder.configuration.selected_image = None
328
329 # Do reparse recipes
330 self.builder.populate_recipe_package_info_async()
331
332 glib.idle_add(self.machine_combo_changed_idle_cb)
333
334 def update_machine_combo(self):
335 self.disable_warnings_bar()
336 all_machines = [self.__dummy_machine__] + self.builder.parameters.all_machines
337
338 model = self.machine_combo.get_model()
339 model.clear()
340 for machine in all_machines:
341 self.machine_combo.append_text(machine)
342 self.machine_combo.set_active(0)
343
344 def switch_machine_combo(self):
345 self.disable_warnings_bar()
346 self.machine_combo_changed_by_manual = False
347 model = self.machine_combo.get_model()
348 active = 0
349 while active < len(model):
350 if model[active][0] == self.builder.configuration.curr_mach:
351 self.machine_combo.set_active(active)
352 return
353 active += 1
354
355 if model[0][0] != self.__dummy_machine__:
356 self.machine_combo.insert_text(0, self.__dummy_machine__)
357
358 self.machine_combo.set_active(0)
359
360 def update_image_desc(self):
361 desc = ""
362 selected_image = self.image_combo.get_active_text()
363 if selected_image and selected_image in self.builder.recipe_model.pn_path.keys():
364 image_path = self.builder.recipe_model.pn_path[selected_image]
365 image_iter = self.builder.recipe_model.get_iter(image_path)
366 desc = self.builder.recipe_model.get_value(image_iter, self.builder.recipe_model.COL_DESC)
367
368 mark = ("<span %s>%s</span>\n") % (self.span_tag('small'), desc)
369 self.image_desc.set_markup(mark)
370
371 def image_combo_changed_idle_cb(self, selected_image, selected_recipes, selected_packages):
372 self.builder.update_recipe_model(selected_image, selected_recipes)
373 self.builder.update_package_model(selected_packages)
374 self.builder.window_sensitive(True)
375
376 def image_combo_changed_cb(self, combo):
377 self.builder.window_sensitive(False)
378 selected_image = self.image_combo.get_active_text()
379 if selected_image == self.__custom_image__:
380 topdir = self.builder.get_topdir()
381 images_dir = topdir + "/recipes/images/"
382 self.builder.ensure_dir(images_dir)
383
384 dialog = RetrieveImageDialog(images_dir, "Select from my image recipes",
385 self.builder, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
386 response = dialog.run()
387 if response == gtk.RESPONSE_OK:
388 image_name = dialog.get_filename()
389 head, tail = os.path.split(image_name)
390 selected_image = os.path.splitext(tail)[0]
391 self.custom_image_selected = selected_image
392 self.update_image_combo(self.builder.recipe_model, selected_image)
393 else:
394 selected_image = self.__dummy_image__
395 self.update_image_combo(self.builder.recipe_model, None)
396 dialog.destroy()
397 else:
398 if self.custom_image_selected:
399 self.custom_image_selected = None
400 self.update_image_combo(self.builder.recipe_model, selected_image)
401
402 if not selected_image or (selected_image == self.__dummy_image__):
403 self.builder.window_sensitive(True)
404 self.just_bake_button.hide()
405 self.edit_image_button.hide()
406 return
407
408 # remove __dummy_image__ item from the store list after first user selection
409 # because it is no longer valid
410 combo_store = combo.get_model()
411 if len(combo_store) and (combo_store[0][0] == self.__dummy_image__):
412 combo.remove_text(0)
413
414 self.builder.customized = False
415
416 selected_recipes = []
417
418 image_path = self.builder.recipe_model.pn_path[selected_image]
419 image_iter = self.builder.recipe_model.get_iter(image_path)
420 selected_packages = self.builder.recipe_model.get_value(image_iter, self.builder.recipe_model.COL_INSTALL).split()
421 self.update_image_desc()
422
423 self.builder.recipe_model.reset()
424 self.builder.package_model.reset()
425
426 self.show_baseimg_selected()
427
428 if selected_image == self.builder.recipe_model.__custom_image__:
429 self.just_bake_button.hide()
430
431 glib.idle_add(self.image_combo_changed_idle_cb, selected_image, selected_recipes, selected_packages)
432
433 def _image_combo_connect_signal(self):
434 if not self.image_combo_id:
435 self.image_combo_id = self.image_combo.connect("changed", self.image_combo_changed_cb)
436
437 def _image_combo_disconnect_signal(self):
438 if self.image_combo_id:
439 self.image_combo.disconnect(self.image_combo_id)
440 self.image_combo_id = None
441
442 def update_image_combo(self, recipe_model, selected_image):
443 # Update the image combo according to the images in the recipe_model
444 # populate image combo
445 filter = {RecipeListModel.COL_TYPE : ['image']}
446 image_model = recipe_model.tree_model(filter)
447 image_model.set_sort_column_id(recipe_model.COL_NAME, gtk.SORT_ASCENDING)
448 active = 0
449 cnt = 0
450
451 white_pattern = []
452 if self.builder.parameters.image_white_pattern:
453 for i in self.builder.parameters.image_white_pattern.split():
454 white_pattern.append(re.compile(i))
455
456 black_pattern = []
457 if self.builder.parameters.image_black_pattern:
458 for i in self.builder.parameters.image_black_pattern.split():
459 black_pattern.append(re.compile(i))
460 black_pattern.append(re.compile("hob-image"))
461
462 it = image_model.get_iter_first()
463 self._image_combo_disconnect_signal()
464 model = self.image_combo.get_model()
465 model.clear()
466 # Set a indicator text to combo store when first open
467 if not selected_image:
468 self.image_combo.append_text(self.__dummy_image__)
469 cnt = cnt + 1
470
471 self.image_combo.append_text(self.__custom_image__)
472 self.image_combo.append_text("--Separator--")
473 cnt = cnt + 2
474
475 topdir = self.builder.get_topdir()
476 # append and set active
477 while it:
478 path = image_model.get_path(it)
479 it = image_model.iter_next(it)
480 image_name = image_model[path][recipe_model.COL_NAME]
481 if image_name == self.builder.recipe_model.__custom_image__:
482 continue
483
484 if black_pattern:
485 allow = True
486 for pattern in black_pattern:
487 if pattern.search(image_name):
488 allow = False
489 break
490 elif white_pattern:
491 allow = False
492 for pattern in white_pattern:
493 if pattern.search(image_name):
494 allow = True
495 break
496 else:
497 allow = True
498
499 file_name = image_model[path][recipe_model.COL_FILE]
500 if file_name and topdir in file_name:
501 allow = False
502
503 if allow:
504 self.image_combo.append_text(image_name)
505 if image_name == selected_image:
506 active = cnt
507 cnt = cnt + 1
508 self.image_combo.append_text(self.builder.recipe_model.__custom_image__)
509
510 if selected_image == self.builder.recipe_model.__custom_image__:
511 active = cnt
512
513 if self.custom_image_selected:
514 self.image_combo.append_text("--Separator--")
515 self.image_combo.append_text(self.custom_image_selected)
516 cnt = cnt + 2
517 if self.custom_image_selected == selected_image:
518 active = cnt
519
520 self.image_combo.set_active(active)
521
522 if active != 0:
523 self.show_baseimg_selected()
524
525 self._image_combo_connect_signal()
526
527 def layer_button_clicked_cb(self, button):
528 # Create a layer selection dialog
529 self.builder.show_layer_selection_dialog()
530
531 def view_adv_configuration_button_clicked_cb(self, button):
532 # Create an advanced settings dialog
533 response, settings_changed = self.builder.show_adv_settings_dialog()
534 if not response:
535 return
536 if settings_changed:
537 self.builder.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
538 self.builder.wait(0.1) #wait for adv_settings_dialog to terminate
539 self.builder.reparse_post_adv_settings()
540 self.builder.window.set_cursor(None)
541
542 def just_bake_button_clicked_cb(self, button):
543 self.builder.parsing_warnings = []
544 self.builder.just_bake()
545
546 def edit_image_button_clicked_cb(self, button):
547 self.builder.configuration.initial_selected_image = self.builder.configuration.selected_image
548 self.builder.show_recipes()
549
550 def my_images_button_clicked_cb(self, button):
551 self.builder.show_load_my_images_dialog()
552
553 def settings_button_clicked_cb(self, button):
554 # Create an advanced settings dialog
555 response, settings_changed = self.builder.show_simple_settings_dialog()
556 if not response:
557 return
558 if settings_changed:
559 self.builder.reparse_post_adv_settings()