From e91f1e28bc1742f010225c49410e49b1257c15c7 Mon Sep 17 00:00:00 2001 From: Liming An Date: Mon, 4 Jun 2012 18:52:13 +0800 Subject: Hob: Change 'run image' work flow and image detail screen as ui design Added the qemu_image_kernel selection view box to image detail screen GUI, and changed the 'run image' button clicked work flow. [YOCTO #2155] (Bitbake rev: d548eb8a03cfba5c64c018898972bc0a0bdb280c) Signed-off-by: Liming An Signed-off-by: Richard Purdie --- bitbake/lib/bb/ui/crumbs/builder.py | 118 +++++++--------- bitbake/lib/bb/ui/crumbs/hig.py | 2 +- bitbake/lib/bb/ui/crumbs/hobeventhandler.py | 3 +- bitbake/lib/bb/ui/crumbs/imagedetailspage.py | 201 +++++++++++++++++++++------ 4 files changed, 211 insertions(+), 113 deletions(-) diff --git a/bitbake/lib/bb/ui/crumbs/builder.py b/bitbake/lib/bb/ui/crumbs/builder.py index 7861a6fbaf..ad9c0f4587 100755 --- a/bitbake/lib/bb/ui/crumbs/builder.py +++ b/bitbake/lib/bb/ui/crumbs/builder.py @@ -303,6 +303,7 @@ class Parameters: self.tmpdir = params["tmpdir"] self.image_white_pattern = params["image_white_pattern"] self.image_black_pattern = params["image_black_pattern"] + self.kernel_image_type = params["kernel_image_type"] # for build log to show self.bb_version = params["bb_version"] self.target_arch = params["target_arch"] @@ -1160,25 +1161,32 @@ class Builder(gtk.Window): response = dialog.run() dialog.destroy() - def get_kernel_file_name(self): - name_list = [] - kernel_name = "" - image_path = self.parameters.image_addr - if image_path: - files = [f for f in os.listdir(image_path) if f[0] <> '.'] - for check_file in files: - if check_file.endswith(".bin"): - name_splits = check_file.split(".")[0] - if self.configuration.curr_mach in name_splits.split("-"): - kernel_name = check_file - if not os.path.islink(os.path.join(image_path, check_file)): - name_list.append(check_file) - - return kernel_name, len(name_list) - - def runqemu_image(self, image_name): - if not image_name: - lbl = "Please select an image to launch in QEMU." + def show_load_kernel_dialog(self): + dialog = gtk.FileChooserDialog("Load Kernel Files", self, + gtk.FILE_CHOOSER_ACTION_SAVE) + button = dialog.add_button("Cancel", gtk.RESPONSE_NO) + HobAltButton.style_button(button) + button = dialog.add_button("Open", gtk.RESPONSE_YES) + HobButton.style_button(button) + filter = gtk.FileFilter() + filter.set_name("Kernel Files") + filter.add_pattern("*.bin") + dialog.add_filter(filter) + + dialog.set_current_folder(self.parameters.image_addr) + + response = dialog.run() + kernel_path = "" + if response == gtk.RESPONSE_YES: + kernel_path = dialog.get_filename() + + dialog.destroy() + + return kernel_path + + def runqemu_image(self, image_name, kernel_name): + if not image_name or not kernel_name: + lbl = "Please select an %s to launch in QEMU." % ("kernel" if image_name else "image") dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO) button = dialog.add_button("Close", gtk.RESPONSE_OK) HobButton.style_button(button) @@ -1186,57 +1194,33 @@ class Builder(gtk.Window): dialog.destroy() return - kernel_name, kernels_number = self.get_kernel_file_name() - if not kernel_name or kernels_number > 1: - dialog = gtk.FileChooserDialog("Load Kernel Files", self, - gtk.FILE_CHOOSER_ACTION_SAVE) - button = dialog.add_button("Cancel", gtk.RESPONSE_NO) - HobAltButton.style_button(button) - button = dialog.add_button("Open", gtk.RESPONSE_YES) - HobButton.style_button(button) - filter = gtk.FileFilter() - filter.set_name("Kernel Files") - filter.add_pattern("*.bin") - dialog.add_filter(filter) - - dialog.set_current_folder(self.parameters.image_addr) + kernel_path = os.path.join(self.parameters.image_addr, kernel_name) + image_path = os.path.join(self.parameters.image_addr, image_name) - response = dialog.run() - if response == gtk.RESPONSE_YES: - kernel_path = dialog.get_filename() - image_path = os.path.join(self.parameters.image_addr, image_name) + source_env_path = os.path.join(self.parameters.core_base, "oe-init-build-env") + tmp_path = self.parameters.tmpdir + cmdline = bb.ui.crumbs.utils.which_terminal() + if os.path.exists(image_path) and os.path.exists(kernel_path) \ + and os.path.exists(source_env_path) and os.path.exists(tmp_path) \ + and cmdline: + cmdline += "\' bash -c \"export OE_TMPDIR=" + tmp_path + "; " + cmdline += "source " + source_env_path + " " + os.getcwd() + "; " + cmdline += "runqemu " + kernel_path + " " + image_path + "\"\'" + subprocess.Popen(shlex.split(cmdline)) + else: + lbl = "Path error\nOne of your paths is wrong," + lbl = lbl + " please make sure the following paths exist:\n" + lbl = lbl + "image path:" + image_path + "\n" + lbl = lbl + "kernel path:" + kernel_path + "\n" + lbl = lbl + "source environment path:" + source_env_path + "\n" + lbl = lbl + "tmp path: " + tmp_path + "." + lbl = lbl + "You may be missing either xterm or vte for terminal services." + dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR) + button = dialog.add_button("Close", gtk.RESPONSE_OK) + HobButton.style_button(button) + dialog.run() dialog.destroy() - elif kernel_name: - kernel_path = os.path.join(self.parameters.image_addr, kernel_name) - image_path = os.path.join(self.parameters.image_addr, image_name) - response = gtk.RESPONSE_YES - - if response == gtk.RESPONSE_YES: - source_env_path = os.path.join(self.parameters.core_base, "oe-init-build-env") - tmp_path = self.parameters.tmpdir - cmdline = bb.ui.crumbs.utils.which_terminal() - if os.path.exists(image_path) and os.path.exists(kernel_path) \ - and os.path.exists(source_env_path) and os.path.exists(tmp_path) \ - and cmdline: - cmdline += "\' bash -c \"export OE_TMPDIR=" + tmp_path + "; " - cmdline += "source " + source_env_path + " " + os.getcwd() + "; " - cmdline += "runqemu " + kernel_path + " " + image_path + "\"\'" - subprocess.Popen(shlex.split(cmdline)) - else: - lbl = "Path error\nOne of your paths is wrong," - lbl = lbl + " please make sure the following paths exist:\n" - lbl = lbl + "image path:" + image_path + "\n" - lbl = lbl + "kernel path:" + kernel_path + "\n" - lbl = lbl + "source environment path:" + source_env_path + "\n" - lbl = lbl + "tmp path: " + tmp_path + "." - lbl = lbl + "You may be missing either xterm or vte for terminal services." - dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_ERROR) - button = dialog.add_button("Close", gtk.RESPONSE_OK) - HobButton.style_button(button) - dialog.run() - dialog.destroy() - def show_packages(self, ask=True): _, selected_recipes = self.recipe_model.get_selected_recipes() if selected_recipes and ask: diff --git a/bitbake/lib/bb/ui/crumbs/hig.py b/bitbake/lib/bb/ui/crumbs/hig.py index 7c9e73fdf9..f64f1be46c 100644 --- a/bitbake/lib/bb/ui/crumbs/hig.py +++ b/bitbake/lib/bb/ui/crumbs/hig.py @@ -1210,7 +1210,7 @@ class ImageSelectionDialog (CrumbsDialog): if f.endswith('.' + real_image_type): imageset.add(f.rsplit('.' + real_image_type)[0].rsplit('.rootfs')[0]) self.image_list.append(f) - + for image in imageset: self.image_store.set(self.image_store.append(), 0, image, 1, False) diff --git a/bitbake/lib/bb/ui/crumbs/hobeventhandler.py b/bitbake/lib/bb/ui/crumbs/hobeventhandler.py index 1db9c44295..a00fcd8eda 100644 --- a/bitbake/lib/bb/ui/crumbs/hobeventhandler.py +++ b/bitbake/lib/bb/ui/crumbs/hobeventhandler.py @@ -347,7 +347,7 @@ class HobHandler(gobject.GObject): self.commands_async.append(self.SUB_PARSE_CONFIG) self.commands_async.append(self.SUB_GNERATE_TGTS) self.run_next_command(self.GENERATE_RECIPES) - + def generate_packages(self, tgts, default_task="build"): targets = [] targets.extend(tgts) @@ -492,6 +492,7 @@ class HobHandler(gobject.GObject): params["runnable_image_types"] = self._remove_redundant(self.runCommand(["getVariable", "RUNNABLE_IMAGE_TYPES"]) or "") params["runnable_machine_patterns"] = self._remove_redundant(self.runCommand(["getVariable", "RUNNABLE_MACHINE_PATTERNS"]) or "") params["deployable_image_types"] = self._remove_redundant(self.runCommand(["getVariable", "DEPLOYABLE_IMAGE_TYPES"]) or "") + params["kernel_image_type"] = self.runCommand(["getVariable", "KERNEL_IMAGETYPE"]) or "" params["tmpdir"] = self.runCommand(["getVariable", "TMPDIR"]) or "" params["distro_version"] = self.runCommand(["getVariable", "DISTRO_VERSION"]) or "" params["target_os"] = self.runCommand(["getVariable", "TARGET_OS"]) or "" diff --git a/bitbake/lib/bb/ui/crumbs/imagedetailspage.py b/bitbake/lib/bb/ui/crumbs/imagedetailspage.py index 1ab80f8277..ba0ad7ed47 100755 --- a/bitbake/lib/bb/ui/crumbs/imagedetailspage.py +++ b/bitbake/lib/bb/ui/crumbs/imagedetailspage.py @@ -26,7 +26,7 @@ from bb.ui.crumbs.hobcolor import HobColors from bb.ui.crumbs.hobwidget import hic, HobViewTable, HobAltButton, HobButton from bb.ui.crumbs.hobpages import HobPage import subprocess - +from bb.ui.crumbs.hig import CrumbsDialog # # ImageDetailsPage # @@ -101,9 +101,23 @@ class ImageDetailsPage (HobPage): return self.line_widgets[variable].set_markup(self.format_line(variable, value)) + def wrap_line(self, inputs): + # wrap the long text of inputs + wrap_width_chars = 75 + outputs = "" + tmps = inputs + less_chars = len(inputs) + while (less_chars - wrap_width_chars) > 0: + less_chars -= wrap_width_chars + outputs += tmps[:wrap_width_chars] + "\n " + tmps = inputs[less_chars:] + outputs += tmps + return outputs + def format_line(self, variable, value): + wraped_value = self.wrap_line(value) markup = "%s" % variable - markup += "%s" % value + markup += "%s" % wraped_value return markup def text2label(self, variable, value): @@ -117,7 +131,7 @@ class ImageDetailsPage (HobPage): def __init__(self, builder): super(ImageDetailsPage, self).__init__(builder, "Image details") - self.image_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN) + self.image_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN, gobject.TYPE_STRING) self.button_ids = {} self.details_bottom_buttons = gtk.HBox(False, 6) self.create_visual_elements() @@ -162,10 +176,10 @@ class ImageDetailsPage (HobPage): self.details_bottom_buttons.remove(child) def show_page(self, step): - build_succeeded = (step == self.builder.IMAGE_GENERATED) + self.build_succeeded = (step == self.builder.IMAGE_GENERATED) image_addr = self.builder.parameters.image_addr image_names = self.builder.parameters.image_names - if build_succeeded: + if self.build_succeeded: machine = self.builder.configuration.curr_mach base_image = self.builder.recipe_model.get_selected_image() layers = self.builder.configuration.layers @@ -183,7 +197,7 @@ class ImageDetailsPage (HobPage): self.pack_start(self.group_align, expand=True, fill=True) self.build_result = None - if build_succeeded: + if self.build_succeeded: # building is the previous step icon = gtk.Image() pixmap_path = hic.ICON_INDI_CONFIRM_FILE @@ -196,38 +210,40 @@ class ImageDetailsPage (HobPage): self.box_group_area.pack_start(self.build_result, expand=False, fill=False) # create the buttons at the bottom first because the buttons are used in apply_button_per_image() - if build_succeeded: + if self.build_succeeded: self.buttonlist = ["Build new image", "Save as template", "Run image", "Deploy image"] else: # get to this page from "My images" self.buttonlist = ["Build new image", "Run image", "Deploy image"] # Name self.image_store.clear() - default_toggled = "" + self.toggled_image = "" default_image_size = 0 - num_toggled = 0 + self.num_toggled = 0 i = 0 for image_name in image_names: image_size = HobPage._size_to_string(os.stat(os.path.join(image_addr, image_name)).st_size) - is_toggled = (self.test_type_runnable(image_name) and self.test_mach_runnable(image_name)) \ - or self.test_deployable(image_name) - if not default_toggled: + image_attr = ("runnable" if (self.test_type_runnable(image_name) and self.test_mach_runnable(image_name)) else \ + ("deploy" if self.test_deployable(image_name) else "")) + is_toggled = (image_attr != "") + + if not self.toggled_image: if i == (len(image_names) - 1): is_toggled = True - self.image_store.set(self.image_store.append(), 0, image_name, 1, image_size, 2, is_toggled) + self.image_store.set(self.image_store.append(), 0, image_name, 1, image_size, 2, is_toggled, 3, image_attr) if is_toggled: default_image_size = image_size - default_toggled = image_name + self.toggled_image = image_name else: - self.image_store.set(self.image_store.append(), 0, image_name, 1, image_size, 2, False) + self.image_store.set(self.image_store.append(), 0, image_name, 1, image_size, 2, False, 3, image_attr) i = i + 1 - num_toggled += is_toggled + self.num_toggled += is_toggled - self.create_bottom_buttons(self.buttonlist, default_toggled) + is_runnable = self.create_bottom_buttons(self.buttonlist, self.toggled_image) - if build_succeeded and (num_toggled < 2): + if self.build_succeeded: varlist = ["Name: ", "Directory: "] vallist = [] vallist.append(image_name.split('.')[0]) @@ -247,12 +263,27 @@ class ImageDetailsPage (HobPage): self.image_detail = self.DetailBox(widget=image_table, varlist=varlist, vallist=vallist, button=view_files_button) self.box_group_area.pack_start(self.image_detail, expand=True, fill=True) + # The default kernel box for the qemu images + self.sel_kernel = "" + if 'qemu' in image_name: + self.sel_kernel = self.get_kernel_file_name() + + varlist = ["Kernel: "] + vallist = [] + vallist.append(self.sel_kernel) + + change_kernel_button = HobAltButton("Change") + change_kernel_button.connect("clicked", self.change_kernel_cb) + change_kernel_button.set_tooltip_text("Change qemu kernel file") + self.kernel_detail = self.DetailBox(varlist=varlist, vallist=vallist, button=change_kernel_button) + self.box_group_area.pack_start(self.kernel_detail, expand=False, fill=False) + # Machine, Base image and Layers layer_num_limit = 15 varlist = ["Machine: ", "Base image: ", "Layers: "] vallist = [] self.setting_detail = None - if build_succeeded: + if self.build_succeeded: vallist.append(machine) vallist.append(base_image) i = 0 @@ -283,7 +314,7 @@ class ImageDetailsPage (HobPage): vallist = [] vallist.append(pkg_num) vallist.append(default_image_size) - if build_succeeded: + if self.build_succeeded: edit_packages_button = HobAltButton("Edit packages") edit_packages_button.set_tooltip_text("Edit the packages included in your image") edit_packages_button.connect("clicked", self.edit_packages_button_clicked_cb) @@ -296,6 +327,8 @@ class ImageDetailsPage (HobPage): self.box_group_area.pack_end(self.details_bottom_buttons, expand=False, fill=False) self.show_all() + if not is_runnable: + self.kernel_detail.hide() def view_files_clicked_cb(self, button, image_addr): subprocess.call("xdg-open /%s" % image_addr, shell=True) @@ -327,24 +360,63 @@ class ImageDetailsPage (HobPage): break return deployable - def table_selected_cb(self, selection): - model, paths = selection.get_selected_rows() - if (not model) or (not paths): - return + def get_kernel_file_name(self, kernel_addr=""): + kernel_name = "" - path = paths[0] - columnid = 2 - iter = model.get_iter_first() - while iter: - rowpath = model.get_path(iter) - model[rowpath][columnid] = False - iter = model.iter_next(iter) + if not kernel_addr: + kernel_addr = self.builder.parameters.image_addr - model[path][columnid] = True - self.refresh_package_detail_box(model[path][1]) + files = [f for f in os.listdir(kernel_addr) if f[0] <> '.'] + for check_file in files: + if check_file.endswith(".bin"): + name_splits = check_file.split(".")[0] + if self.builder.parameters.kernel_image_type in name_splits.split("-"): + kernel_name = check_file + break - image_name = model[path][0] + return kernel_name + + def show_builded_images_dialog(self, widget): + dialog = CrumbsDialog("Your builded images", self.builder, + gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT) + dialog.set_size_request(-1, 350) + + label = gtk.Label() + label.set_use_markup(True) + label.set_alignment(0.0, 0.5) + label.set_markup("Please select a image to run or deploy") + dialog.vbox.pack_start(label, expand=False, fill=False) + + image_table = HobViewTable(self.__columns__) + image_table.set_model(self.image_store) + image_table.connect("row-activated", self.row_activated_cb) + image_table.connect_group_selection(self.table_selected_cb) + dialog.vbox.pack_start(image_table, expand=True, fill=True) + + button = dialog.add_button(" OK ", gtk.RESPONSE_YES) + HobButton.style_button(button) + dialog.show_all() + + response = dialog.run() + dialog.destroy() + + if response != gtk.RESPONSE_YES: + return + + it = self.image_store.get_iter_first() + while it: + image_attr = self.image_store.get_value(it, 3) + is_select = self.image_store.get_value(it, 2) + if is_select: + image_name = self.image_store.get_value(it, 0) + if image_attr == 'runnable': + self.builder.runqemu_image(image_name, self.sel_kernel) + elif image_attr == 'deploy': + self.builder.deploy_image(image_name) + it = self.image_store.iter_next(it) + + def repack_box_group(self, image_name=None): # remove for button_id, button in self.button_ids.items(): button.disconnect(button_id) @@ -355,27 +427,59 @@ class ImageDetailsPage (HobPage): if self.build_result: self.box_group_area.pack_start(self.build_result, expand=False, fill=False) self.box_group_area.pack_start(self.image_detail, expand=True, fill=True) + if self.kernel_detail: + self.box_group_area.pack_start(self.kernel_detail, expand=False, fill=False) if self.setting_detail: self.box_group_area.pack_start(self.setting_detail, expand=False, fill=False) self.box_group_area.pack_start(self.package_detail, expand=False, fill=False) - self.create_bottom_buttons(self.buttonlist, image_name) + is_runnable = self.create_bottom_buttons(self.buttonlist, image_name) self.box_group_area.pack_end(self.details_bottom_buttons, expand=False, fill=False) self.show_all() + if not is_runnable: + self.kernel_detail.hide() + + def table_selected_cb(self, selection): + model, paths = selection.get_selected_rows() + if (not model) or (not paths): + return + + path = paths[0] + columnid = 2 + iter = model.get_iter_first() + while iter: + rowpath = model.get_path(iter) + model[rowpath][columnid] = False + iter = model.iter_next(iter) + + model[path][columnid] = True + self.refresh_package_detail_box(model[path][1]) + + self.toggled_image = model[path][0] + self.repack_box_group(self.toggled_image) + + def change_kernel_cb(self, widget): + kernel_path = self.builder.show_load_kernel_dialog() + if kernel_path and self.kernel_detail: + import os.path + self.sel_kernel = os.path.basename(kernel_path) + markup = self.kernel_detail.format_line("Kernel: ", self.sel_kernel) + label = ((self.kernel_detail.get_children()[0]).get_children()[0]).get_children()[0] + label.set_markup(markup) def row_activated_cb(self, table, model, path): if not model: return iter = model.get_iter(path) image_name = model[path][0] - if iter and model[path][2] == 'runnable': - kernel_name, kernel_number = self.builder.parameters.get_kernel_file_name() - self.builder.runqemu_image(image_name, kernel_name, kernel_number) + if iter and model[path][2] == True: + self.builder.runqemu_image(image_name, self.sel_kernel) def create_bottom_buttons(self, buttonlist, image_name): # Create the buttons at the bottom created = False packed = False self.button_ids = {} + is_runnable = False # create button "Deploy image" name = "Deploy image" @@ -384,7 +488,7 @@ class ImageDetailsPage (HobPage): deploy_button.set_size_request(205, 49) deploy_button.set_tooltip_text("Burn a live image to a USB drive or flash memory") deploy_button.set_flags(gtk.CAN_DEFAULT) - button_id = deploy_button.connect("clicked", self.deploy_button_clicked_cb, image_name) + button_id = deploy_button.connect("clicked", self.deploy_button_clicked_cb) self.button_ids[button_id] = deploy_button self.details_bottom_buttons.pack_end(deploy_button, expand=False, fill=False) created = True @@ -406,10 +510,11 @@ class ImageDetailsPage (HobPage): run_button.set_flags(gtk.CAN_DEFAULT) packed = True run_button.set_tooltip_text("Start up an image with qemu emulator") - button_id = run_button.connect("clicked", self.run_button_clicked_cb, image_name) + button_id = run_button.connect("clicked", self.run_button_clicked_cb) self.button_ids[button_id] = run_button self.details_bottom_buttons.pack_end(run_button, expand=False, fill=False) created = True + is_runnable = True name = "Save as template" if name in buttonlist: @@ -446,14 +551,22 @@ class ImageDetailsPage (HobPage): button_id = build_new_button.connect("clicked", self.build_new_button_clicked_cb) self.button_ids[button_id] = build_new_button + return is_runnable + def save_button_clicked_cb(self, button): self.builder.show_save_template_dialog() - def deploy_button_clicked_cb(self, button, image_name): - self.builder.deploy_image(image_name) + def deploy_button_clicked_cb(self, button): + if self.build_succeeded and self.num_toggled > 1: + self.show_builded_images_dialog() + return + self.builder.deploy_image(self.toggled_image) - def run_button_clicked_cb(self, button, image_name): - self.builder.runqemu_image(image_name) + def run_button_clicked_cb(self, button): + if self.build_succeeded and self.num_toggled > 1: + self.show_builded_images_dialog() + return + self.builder.runqemu_image(self.toggled_image, self.sel_kernel) def build_new_button_clicked_cb(self, button): self.builder.initiate_new_build_async() -- cgit v1.2.3-54-g00ecf