[poky] [PATCH 12/13] bitbake: Add new UI hob, a prototype Gtk+ GUI for creating images

Joshua Lock josh at linux.intel.com
Fri Feb 4 00:54:35 PST 2011


From: Joshua Lock <josh at linux.intel.com>

Hob is a first stab at implementing an interactive GUI for BitBake.

Signed-off-by: Joshua Lock <josh at linux.intel.com>
---
 bitbake/lib/bb/ui/crumbs/hobeventhandler.py |  138 ++++++
 bitbake/lib/bb/ui/hob.py                    |  596 +++++++++++++++++++++++++++
 2 files changed, 734 insertions(+), 0 deletions(-)
 create mode 100644 bitbake/lib/bb/ui/crumbs/hobeventhandler.py
 create mode 100644 bitbake/lib/bb/ui/hob.py

diff --git a/bitbake/lib/bb/ui/crumbs/hobeventhandler.py b/bitbake/lib/bb/ui/crumbs/hobeventhandler.py
new file mode 100644
index 0000000..699354c
--- /dev/null
+++ b/bitbake/lib/bb/ui/crumbs/hobeventhandler.py
@@ -0,0 +1,138 @@
+#
+# BitBake Graphical GTK User Interface
+#
+# Copyright (C) 2011        Intel Corporation
+#
+# Authored by Joshua Lock <josh at linux.intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import gobject
+from bb.ui.crumbs.progress import ProgressBar
+
+progress_total = 0
+
+class HobHandler(gobject.GObject):
+
+    """
+    This object does BitBake event handling for the hob gui.
+    """
+    __gsignals__ = {
+         "machines-updated" : (gobject.SIGNAL_RUN_LAST,
+	                       gobject.TYPE_NONE,
+			       (gobject.TYPE_PYOBJECT,)),
+	 "distros-updated" : (gobject.SIGNAL_RUN_LAST,
+	 		      gobject.TYPE_NONE,
+			      (gobject.TYPE_PYOBJECT,)),
+         "generating-data" : (gobject.SIGNAL_RUN_LAST,
+                              gobject.TYPE_NONE,
+                              ()),
+         "data-generated" : (gobject.SIGNAL_RUN_LAST,
+                             gobject.TYPE_NONE,
+                             ())
+    }
+
+    def __init__(self, taskmodel, server):
+        gobject.GObject.__init__(self)
+
+        self.model = taskmodel
+        self.server = server
+        self.current_command = None
+        self.building = False
+
+        self.command_map = {
+            "findConfigFilesDistro" : ("findConfigFiles", "MACHINE", "findConfigFilesMachine"),
+            "findConfigFilesMachine" : ("generateTargetsTree", "classes/image.bbclass", None),
+            "generateTargetsTree"  : (None, None, None),
+            }
+
+    def run_next_command(self):
+        # FIXME: this is ugly and I *will* replace it
+        if self.current_command:
+            next_cmd = self.command_map[self.current_command]
+            command = next_cmd[0]
+            argument = next_cmd[1]
+            self.current_command = next_cmd[2]
+            if command == "generateTargetsTree":
+                self.emit("generating-data")
+            self.server.runCommand([command, argument])
+
+    def handle_event(self, event, running_build, pbar=None):
+        if not event:
+	    return
+
+        # If we're running a build, use the RunningBuild event handler
+        if self.building:
+            running_build.handle_event(event)
+        elif isinstance(event, bb.event.TargetsTreeGenerated):
+            self.emit("data-generated")
+            if event._model:
+                self.model.populate(event._model)
+                
+        elif isinstance(event, bb.event.ConfigFilesFound):
+            var = event._variable
+	    if var == "distro":
+		distros = event._values
+		distros.sort()
+		self.emit("distros-updated", distros)
+	    elif var == "machine":
+	        machines = event._values
+		machines.sort()
+		self.emit("machines-updated", machines)
+
+        elif isinstance(event, bb.command.CommandCompleted):
+            self.run_next_command()
+        elif isinstance(event, bb.event.CacheLoadStarted) and pbar:
+            pbar.set_title("Loading cache")
+            bb.ui.crumbs.hobeventhandler.progress_total = event.total
+            pbar.update(0, bb.ui.crumbs.hobeventhandler.progress_total)
+        elif isinstance(event, bb.event.CacheLoadProgress) and pbar:
+            pbar.update(event.current, bb.ui.crumbs.hobeventhandler.progress_total)
+        elif isinstance(event, bb.event.CacheLoadCompleted) and pbar:
+            pbar.update(bb.ui.crumbs.hobeventhandler.progress_total, bb.ui.crumbs.hobeventhandler.progress_total)
+        elif isinstance(event, bb.event.ParseStarted) and pbar:
+            pbar.set_title("Processing recipes")
+            bb.ui.crumbs.hobeventhandler.progress_total = event.total
+            pbar.update(0, bb.ui.crumbs.hobeventhandler.progress_total)
+        elif isinstance(event, bb.event.ParseProgress) and pbar:
+            pbar.update(event.current, bb.ui.crumbs.hobeventhandler.progress_total)
+        elif isinstance(event, bb.event.ParseCompleted) and pbar:
+            pbar.hide()
+            
+        return
+
+    def event_handle_idle_func (self, eventHandler, running_build, pbar):
+        # Consume as many messages as we can in the time available to us
+        event = eventHandler.getEvent()
+        while event:
+            self.handle_event(event, running_build, pbar)
+            event = eventHandler.getEvent()
+        return True
+
+    def set_machine(self, machine):
+        self.server.runCommand(["setVariable", "MACHINE", machine])
+        self.current_command = "findConfigFilesMachine"
+        self.emit("generating-data")
+        self.run_next_command()
+
+    def set_distro(self, distro):
+        self.server.runCommand(["setVariable", "DISTRO", distro])
+
+    def run_build(self, targets):
+        self.building = True
+        self.server.runCommand(["buildTargets", targets, "build"])
+
+    def cancel_build(self):
+        # Note: this may not be the right way to stop an in-progress build
+        self.server.runCommand(["stateStop"])
diff --git a/bitbake/lib/bb/ui/hob.py b/bitbake/lib/bb/ui/hob.py
new file mode 100644
index 0000000..5278881
--- /dev/null
+++ b/bitbake/lib/bb/ui/hob.py
@@ -0,0 +1,596 @@
+#
+# BitBake Graphical GTK User Interface
+#
+# Copyright (C) 2011        Intel Corporation
+#
+# Authored by Joshua Lock <josh at linux.intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import gobject
+import gtk
+from bb.ui.crumbs.progress import ProgressBar
+from bb.ui.crumbs.tasklistmodel import TaskListModel
+from bb.ui.crumbs.hobeventhandler import HobHandler
+from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild
+import xmlrpclib
+import logging
+import Queue
+
+class MainWindow (gtk.Window):
+            
+    def __init__(self, taskmodel, handler, curr_mach=None, curr_distro=None):
+        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
+        self.model = taskmodel
+	self.model.connect("tasklist-populated", self.update_model)
+	self.curr_mach = curr_mach
+	self.curr_distro = curr_distro
+        self.handler = handler
+        self.set_border_width(10)
+        self.connect("delete-event", gtk.main_quit)
+        self.set_title("BitBake Image Creator")
+        self.set_default_size(700, 600)
+
+        self.build = RunningBuild()
+        self.build.connect("build-succeeded", self.running_build_succeeded_cb)
+        self.build.connect("build-failed", self.running_build_failed_cb)
+
+	createview = self.create_build_gui()
+        buildview = self.view_build_gui()
+	self.nb = gtk.Notebook()
+	self.nb.append_page(createview)
+	self.nb.append_page(buildview)
+	self.nb.set_current_page(0)
+	self.nb.set_show_tabs(False)
+        self.add(self.nb)
+        self.generating = False
+
+    def scroll_tv_cb(self, model, path, it, view):
+        view.scroll_to_cell(path)
+
+    def running_build_failed_cb(self, running_build):
+        # FIXME: handle this
+        return
+
+    def running_build_succeeded_cb(self, running_build):
+        label = gtk.Label("Build completed, start another build?")
+        dialog = gtk.Dialog("Build complete",
+                            self,
+                            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+                            (gtk.STOCK_NO, gtk.RESPONSE_NO,
+                             gtk.STOCK_YES, gtk.RESPONSE_YES))
+        dialog.vbox.pack_start(label)
+        label.show()
+        response = dialog.run()
+        dialog.destroy()
+        if not response == gtk.RESPONSE_YES:
+            self.model.reset() # NOTE: really?
+            self.nb.set_current_page(0)
+        return
+
+    def machine_combo_changed_cb(self, combo, handler):
+        mach = combo.get_active_text()
+	if mach != self.curr_mach:
+	    self.curr_mach = mach
+            handler.set_machine(mach)
+
+    def update_machines(self, handler, machines):
+	active = 0
+	for machine in machines:
+	    self.machine_combo.append_text(machine)
+	    if machine == self.curr_mach:
+                self.machine_combo.set_active(active)
+	    active = active + 1
+	self.machine_combo.connect("changed", self.machine_combo_changed_cb, handler)
+
+    def update_distros(self, handler, distros):
+        # FIXME: when we add UI for changing distro this will be used
+        return
+
+    def data_generated(self, handler):
+        self.generating = False
+
+    def spin_idle_func(self, pbar):
+        if self.generating:
+            pbar.pulse()
+            return True
+        else:
+            pbar.hide()
+            return False
+
+    def busy(self, handler):
+        self.generating = True
+        pbar = ProgressBar(self)
+        pbar.connect("delete-event", gtk.main_quit) # NOTE: questionable...
+        pbar.pulse()
+        gobject.timeout_add (200,
+                             self.spin_idle_func,
+                             pbar)
+
+    def update_model(self, model):
+	pkgsaz_model = gtk.TreeModelSort(self.model.packages_model())
+        pkgsaz_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING)
+        self.pkgsaz_tree.set_model(pkgsaz_model)
+
+        # FIXME: need to implement a custom sort function, as otherwise the column
+        # is re-ordered when toggling the inclusion state (COL_INC)
+	pkgsgrp_model = gtk.TreeModelSort(self.model.packages_model())
+	pkgsgrp_model.set_sort_column_id(self.model.COL_GROUP, gtk.SORT_ASCENDING)
+	self.pkgsgrp_tree.set_model(pkgsgrp_model)
+
+        self.contents_tree.set_model(self.model.contents_model())
+	self.images_tree.set_model(self.model.images_model())
+	self.tasks_tree.set_model(self.model.tasks_model())
+
+    def reset_clicked_cb(self, button):
+        label = gtk.Label("Are you sure you want to reset the image contents?")
+        dialog = gtk.Dialog("Confirm reset", self,
+                            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+                            (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
+                             gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+        dialog.vbox.pack_start(label)
+        label.show()
+        response = dialog.run()
+        dialog.destroy()
+        if (response == gtk.RESPONSE_ACCEPT):
+            self.model.reset()
+        return
+
+    def bake_clicked_cb(self, button):
+        if not self.model.targets_contains_image():
+            label = gtk.Label("No image was selected. Just build the selected packages?")
+            dialog = gtk.Dialog("Warning, no image selected",
+                                self,
+                                gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+                                (gtk.STOCK_NO, gtk.RESPONSE_NO,
+                                 gtk.STOCK_YES, gtk.RESPONSE_YES))
+            dialog.vbox.pack_start(label)
+            label.show()
+            response = dialog.run()
+            dialog.destroy()
+            if not response == gtk.RESPONSE_YES:
+                return
+
+        # Note: We could "squash" the targets list to only include things not brought in by an image
+	task_list = self.model.get_targets()
+	if len(task_list):
+	    tasks = " ".join(task_list)
+            # TODO: show a confirmation dialog
+            print("Including these extra tasks in IMAGE_INSTALL: %s" % tasks)
+        else:
+            return
+
+        self.nb.set_current_page(1)
+        self.handler.run_build(task_list)
+
+        return
+
+    def advanced_expander_cb(self, expander, param):
+        return
+
+    def images(self):
+        self.images_tree = gtk.TreeView()
+	self.images_tree.set_headers_visible(True)
+        self.images_tree.set_headers_clickable(False)
+        self.images_tree.set_enable_search(True)
+        self.images_tree.set_search_column(0)
+        self.images_tree.get_selection().set_mode(gtk.SELECTION_NONE)
+
+        col = gtk.TreeViewColumn('Package')
+        col1 = gtk.TreeViewColumn('Description')
+        col2 = gtk.TreeViewColumn('License')
+        col3 = gtk.TreeViewColumn('Include')
+	col3.set_resizable(False)
+
+        self.images_tree.append_column(col)
+        self.images_tree.append_column(col1)
+        self.images_tree.append_column(col2)
+        self.images_tree.append_column(col3)
+
+        cell = gtk.CellRendererText()
+        cell1 = gtk.CellRendererText()
+        cell2 = gtk.CellRendererText()
+        cell3 = gtk.CellRendererToggle()
+        cell3.set_property('activatable', True)
+        cell3.connect("toggled", self.toggle_include_cb, self.images_tree)
+
+        col.pack_start(cell, True)
+        col1.pack_start(cell1, True)
+        col2.pack_start(cell2, True)
+        col3.pack_start(cell3, True)
+
+        col.set_attributes(cell, text=self.model.COL_NAME)
+        col1.set_attributes(cell1, text=self.model.COL_DESC)
+        col2.set_attributes(cell2, text=self.model.COL_LIC)
+        col3.set_attributes(cell3, active=self.model.COL_INC)
+
+        self.images_tree.show()
+
+        scroll = gtk.ScrolledWindow()
+        scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
+        scroll.set_shadow_type(gtk.SHADOW_IN)
+        scroll.add(self.images_tree)
+
+        return scroll
+
+    def toggle_package(self, path, model):
+        # Convert path to path in original model
+        opath = model.convert_path_to_child_path(path)
+        # current include status
+	inc = self.model[opath][self.model.COL_INC]
+	if inc:
+	    self.model.mark(opath)
+            self.model.sweep_up()
+	    #self.model.remove_package_full(cpath)
+	else:
+	    self.model.include_item(opath)
+        return
+
+    def remove_package_cb(self, cell, path):
+        model = self.model.contents_model()
+        label = gtk.Label("Are you sure you want to remove this item?")
+        dialog = gtk.Dialog("Confirm removal", self,
+                            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+                            (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
+                             gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+        dialog.vbox.pack_start(label)
+        label.show()
+        response = dialog.run()
+        dialog.destroy()
+        if (response == gtk.RESPONSE_ACCEPT):
+            self.toggle_package(path, model)
+
+    def toggle_include_cb(self, cell, path, tv):
+        model = tv.get_model()
+        self.toggle_package(path, model)
+
+    def toggle_pkg_include_cb(self, cell, path, tv):
+        # there's an extra layer of models in the packages case.
+        sort_model = tv.get_model()
+        cpath = sort_model.convert_path_to_child_path(path)
+        self.toggle_package(cpath, sort_model.get_model())
+	
+    def pkgsaz(self):
+        self.pkgsaz_tree = gtk.TreeView()
+        self.pkgsaz_tree.set_headers_visible(True)
+        self.pkgsaz_tree.set_headers_clickable(True)
+        self.pkgsaz_tree.set_enable_search(True)
+        self.pkgsaz_tree.set_search_column(0)
+        self.pkgsaz_tree.get_selection().set_mode(gtk.SELECTION_NONE)
+
+        col = gtk.TreeViewColumn('Package')
+        col1 = gtk.TreeViewColumn('Description')
+	col1.set_resizable(True)
+        col2 = gtk.TreeViewColumn('License')
+	col2.set_resizable(True)
+        col3 = gtk.TreeViewColumn('Group')
+        col4 = gtk.TreeViewColumn('Include')
+	col4.set_resizable(False)
+
+        self.pkgsaz_tree.append_column(col)
+        self.pkgsaz_tree.append_column(col1)
+        self.pkgsaz_tree.append_column(col2)
+        self.pkgsaz_tree.append_column(col3)
+        self.pkgsaz_tree.append_column(col4)
+
+        cell = gtk.CellRendererText()
+        cell1 = gtk.CellRendererText()
+	cell1.set_property('width-chars', 20)
+        cell2 = gtk.CellRendererText()
+	cell2.set_property('width-chars', 20)
+        cell3 = gtk.CellRendererText()
+        cell4 = gtk.CellRendererToggle()
+        cell4.set_property('activatable', True)
+        cell4.connect("toggled", self.toggle_pkg_include_cb, self.pkgsaz_tree)
+
+        col.pack_start(cell, True)
+        col1.pack_start(cell1, True)
+        col2.pack_start(cell2, True)
+        col3.pack_start(cell3, True)
+        col4.pack_start(cell4, True)
+
+        col.set_attributes(cell, text=self.model.COL_NAME)
+        col1.set_attributes(cell1, text=self.model.COL_DESC)
+        col2.set_attributes(cell2, text=self.model.COL_LIC)
+        col3.set_attributes(cell3, text=self.model.COL_GROUP)
+        col4.set_attributes(cell4, active=self.model.COL_INC)
+
+        self.pkgsaz_tree.show()
+
+        scroll = gtk.ScrolledWindow()
+        scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
+        scroll.set_shadow_type(gtk.SHADOW_IN)
+        scroll.add(self.pkgsaz_tree)
+
+        return scroll
+
+    def pkgsgrp(self):
+        self.pkgsgrp_tree = gtk.TreeView()
+        self.pkgsgrp_tree.set_headers_visible(True)
+        self.pkgsgrp_tree.set_headers_clickable(False)
+        self.pkgsgrp_tree.set_enable_search(True)
+        self.pkgsgrp_tree.set_search_column(0)
+        self.pkgsgrp_tree.get_selection().set_mode(gtk.SELECTION_NONE)
+
+        col = gtk.TreeViewColumn('Package')
+        col1 = gtk.TreeViewColumn('Description')
+	col1.set_resizable(True)
+        col2 = gtk.TreeViewColumn('License')
+	col2.set_resizable(True)
+        col3 = gtk.TreeViewColumn('Group')
+        col4 = gtk.TreeViewColumn('Include')
+	col4.set_resizable(False)
+
+        self.pkgsgrp_tree.append_column(col)
+        self.pkgsgrp_tree.append_column(col1)
+        self.pkgsgrp_tree.append_column(col2)
+        self.pkgsgrp_tree.append_column(col3)
+        self.pkgsgrp_tree.append_column(col4)
+
+        cell = gtk.CellRendererText()
+        cell1 = gtk.CellRendererText()
+	cell1.set_property('width-chars', 20)
+        cell2 = gtk.CellRendererText()
+	cell2.set_property('width-chars', 20)
+        cell3 = gtk.CellRendererText()
+        cell4 = gtk.CellRendererToggle()
+        cell4.set_property("activatable", True)
+        cell4.connect("toggled", self.toggle_pkg_include_cb, self.pkgsgrp_tree)
+
+        col.pack_start(cell, True)
+        col1.pack_start(cell1, True)
+        col2.pack_start(cell2, True)
+        col3.pack_start(cell3, True)
+        col4.pack_start(cell4, True)
+
+        col.set_attributes(cell, text=self.model.COL_NAME)
+        col1.set_attributes(cell1, text=self.model.COL_DESC)
+        col2.set_attributes(cell2, text=self.model.COL_LIC)
+        col3.set_attributes(cell3, text=self.model.COL_GROUP)
+        col4.set_attributes(cell4, active=self.model.COL_INC)
+
+        self.pkgsgrp_tree.show()
+
+        scroll = gtk.ScrolledWindow()
+        scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
+        scroll.set_shadow_type(gtk.SHADOW_IN)
+        scroll.add(self.pkgsgrp_tree)
+
+        return scroll
+
+    def tasks(self):
+        self.tasks_tree = gtk.TreeView()
+        self.tasks_tree.set_headers_visible(True)
+        self.tasks_tree.set_headers_clickable(False)
+        self.tasks_tree.set_enable_search(True)
+        self.tasks_tree.set_search_column(0)
+        self.tasks_tree.get_selection().set_mode(gtk.SELECTION_NONE)
+
+        col = gtk.TreeViewColumn('Package')
+        col1 = gtk.TreeViewColumn('Description')
+        col2 = gtk.TreeViewColumn('Include')
+	col2.set_resizable(False)
+
+        self.tasks_tree.append_column(col)
+        self.tasks_tree.append_column(col1)
+        self.tasks_tree.append_column(col2)
+
+        cell = gtk.CellRendererText()
+        cell1 = gtk.CellRendererText()
+        cell2 = gtk.CellRendererToggle()
+        cell2.set_property('activatable', True)
+        cell2.connect("toggled", self.toggle_include_cb, self.tasks_tree)
+
+        col.pack_start(cell, True)
+        col1.pack_start(cell1, True)
+        col2.pack_start(cell2, True)
+
+        col.set_attributes(cell, text=self.model.COL_NAME)
+        col1.set_attributes(cell1, text=self.model.COL_DESC)
+        col2.set_attributes(cell2, active=self.model.COL_INC)
+
+        self.tasks_tree.show()
+
+        scroll = gtk.ScrolledWindow()
+        scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
+        scroll.set_shadow_type(gtk.SHADOW_IN)
+        scroll.add(self.tasks_tree)
+
+        return scroll
+
+    def cancel_build(self, button):
+        label = gtk.Label("Do you really want to stop this build?")
+        dialog = gtk.Dialog("Cancel build",
+                            self,
+                            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+                            (gtk.STOCK_NO, gtk.RESPONSE_NO,
+                             gtk.STOCK_YES, gtk.RESPONSE_YES))
+        dialog.vbox.pack_start(label)
+        label.show()
+        response = dialog.run()
+        dialog.destroy()
+        if not response == gtk.RESPONSE_YES:
+            self.handler.cancel_build()
+        return
+
+    def view_build_gui(self):
+        vbox = gtk.VBox(False, 6)
+        vbox.show()
+        build_tv = RunningBuildTreeView()
+        build_tv.show()
+        build_tv.set_model(self.build.model)
+        self.build.model.connect("row-inserted", self.scroll_tv_cb, build_tv)
+        scrolled_view = gtk.ScrolledWindow ()
+        scrolled_view.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolled_view.add(build_tv)
+        scrolled_view.show()
+        vbox.pack_start(scrolled_view, expand=True, fill=True)
+        hbox = gtk.HBox(False, 6)
+        hbox.show()
+        vbox.pack_start(hbox, expand=False, fill=False)
+        cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
+        cancel.connect("clicked", self.cancel_build)
+        cancel.show()
+        hbox.pack_end(cancel, expand=False, fill=False)
+
+        return vbox
+    
+    def create_build_gui(self):
+        vbox = gtk.VBox(False, 6)
+        vbox.show()
+        hbox = gtk.HBox(False, 6)
+        hbox.show()
+        vbox.pack_start(hbox, expand=False, fill=False)
+
+        label = gtk.Label("Machine:")
+        label.show()
+        hbox.pack_start(label, expand=False, fill=False, padding=6)
+        self.machine_combo = gtk.combo_box_new_text()
+	self.machine_combo.set_active(0)
+        self.machine_combo.show()
+        self.machine_combo.set_tooltip_text("Selects the architecture of the target board for which you would like to build an image.")
+        hbox.pack_start(self.machine_combo, expand=False, fill=False, padding=6)
+
+        ins = gtk.Notebook()
+        vbox.pack_start(ins, expand=True, fill=True)
+        ins.set_show_tabs(True)
+        label = gtk.Label("Images")
+        label.show()
+        ins.append_page(self.images(), tab_label=label)
+        label = gtk.Label("Tasks")
+        label.show()
+        ins.append_page(self.tasks(), tab_label=label)
+        label = gtk.Label("Packages (by Group)")
+        label.show()
+        ins.append_page(self.pkgsgrp(), tab_label=label)
+        label = gtk.Label("Packages (by Name)")
+        label.show()
+        ins.append_page(self.pkgsaz(), tab_label=label)
+        ins.set_current_page(0)
+        ins.show_all()
+
+        hbox = gtk.HBox()
+        hbox.show()
+        vbox.pack_start(hbox, expand=False, fill=False)
+        label = gtk.Label("Image contents:")
+        label.show()
+        hbox.pack_start(label, expand=False, fill=False, padding=6)
+        con = self.contents()
+        con.show()
+        vbox.pack_start(con, expand=True, fill=True)
+
+        #advanced = gtk.Expander(label="Advanced")
+        #advanced.connect("notify::expanded", self.advanced_expander_cb)
+        #advanced.show()
+        #vbox.pack_start(advanced, expand=False, fill=False)
+
+        hbox = gtk.HBox()
+        hbox.show()
+        vbox.pack_start(hbox, expand=False, fill=False)
+        bake = gtk.Button("Bake")
+        bake.connect("clicked", self.bake_clicked_cb)
+        bake.show()
+        hbox.pack_end(bake, expand=False, fill=False, padding=6)
+        reset = gtk.Button("Reset")
+        reset.connect("clicked", self.reset_clicked_cb)
+        reset.show()
+        hbox.pack_end(reset, expand=False, fill=False, padding=6)
+
+        return vbox
+
+    def contents(self):
+        self.contents_tree = gtk.TreeView()
+        self.contents_tree.set_headers_visible(True)
+        self.contents_tree.get_selection().set_mode(gtk.SELECTION_NONE)
+
+        # allow searching in the package column
+        self.contents_tree.set_search_column(0)
+
+        col = gtk.TreeViewColumn('Package')
+	col.set_sort_column_id(0)
+        col1 = gtk.TreeViewColumn('Brought in by')
+	col1.set_resizable(True)
+        col2 = gtk.TreeViewColumn('Remove')
+	col2.set_expand(False)
+
+        self.contents_tree.append_column(col)
+        self.contents_tree.append_column(col1)
+        self.contents_tree.append_column(col2)
+
+        cell = gtk.CellRendererText()
+        cell1 = gtk.CellRendererText()
+	cell1.set_property('width-chars', 20)
+        cell2 = gtk.CellRendererToggle()
+        cell2.connect("toggled", self.remove_package_cb)
+
+        col.pack_start(cell, True)
+        col1.pack_start(cell1, True)
+        col2.pack_start(cell2, True)
+
+        col.set_attributes(cell, text=self.model.COL_NAME)
+        col1.set_attributes(cell1, text=self.model.COL_BINB)
+        col2.set_attributes(cell2, active=self.model.COL_INC)
+
+        self.contents_tree.show()
+
+        scroll = gtk.ScrolledWindow()
+        scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
+        scroll.set_shadow_type(gtk.SHADOW_IN)
+        scroll.add(self.contents_tree)
+
+        return scroll
+
+def main (server, eventHandler):
+    gobject.threads_init()
+    gtk.gdk.threads_init()
+
+    taskmodel = TaskListModel()
+    handler = HobHandler(taskmodel, server)
+    mach = server.runCommand(["getVariable", "MACHINE"])
+    distro = server.runCommand(["getVariable", "DISTRO"])
+
+    window = MainWindow(taskmodel, handler, mach, distro)
+    window.show_all ()
+    handler.connect("machines-updated", window.update_machines)
+    handler.connect("distros-updated", window.update_distros)
+    handler.connect("generating-data", window.busy)
+    handler.connect("data-generated", window.data_generated)
+    pbar = ProgressBar(window)
+    pbar.connect("delete-event", gtk.main_quit)
+
+    try:
+        # kick the while thing off
+        handler.current_command = "findConfigFilesDistro"
+        server.runCommand(["findConfigFiles", "DISTRO"])
+    except xmlrpclib.Fault:
+        print("XMLRPC Fault getting commandline:\n %s" % x)
+        return 1
+
+    # This timeout function regularly probes the event queue to find out if we
+    # have any messages waiting for us.
+    gobject.timeout_add (100,
+                         handler.event_handle_idle_func,
+                         eventHandler,
+                         window.build,
+                         pbar)
+
+    try:
+        gtk.main()
+    except EnvironmentError as ioerror:
+        # ignore interrupted io
+        if ioerror.args[0] == 4:
+            pass
+    finally:
+        server.runCommand(["stateStop"])
+
-- 
1.7.4




More information about the poky mailing list