From bb599a08b49f31272ed6192230b4469f6dc2f048 Mon Sep 17 00:00:00 2001 From: Joshua Lock Date: Wed, 2 Feb 2011 13:02:43 +0000 Subject: bitbake: Add new UI hob, a prototype Gtk+ GUI for creating images Hob is a first stab at implementing an interactive GUI for BitBake. Signed-off-by: Joshua Lock --- bitbake/lib/bb/ui/crumbs/hobeventhandler.py | 138 +++++++ bitbake/lib/bb/ui/hob.py | 596 ++++++++++++++++++++++++++++ 2 files changed, 734 insertions(+) create mode 100644 bitbake/lib/bb/ui/crumbs/hobeventhandler.py create mode 100644 bitbake/lib/bb/ui/hob.py (limited to 'bitbake') diff --git a/bitbake/lib/bb/ui/crumbs/hobeventhandler.py b/bitbake/lib/bb/ui/crumbs/hobeventhandler.py new file mode 100644 index 0000000000..699354c610 --- /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 +# +# 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 0000000000..52788813c5 --- /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 +# +# 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"]) + -- cgit 1.2.3-korg