diff options
| -rw-r--r-- | bitbake/lib/bb/ui/crumbs/hobeventhandler.py | 138 | ||||
| -rw-r--r-- | bitbake/lib/bb/ui/hob.py | 596 |
2 files changed, 734 insertions, 0 deletions
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 @@ | |||
| 1 | # | ||
| 2 | # BitBake Graphical GTK User Interface | ||
| 3 | # | ||
| 4 | # Copyright (C) 2011 Intel Corporation | ||
| 5 | # | ||
| 6 | # Authored by Joshua Lock <josh@linux.intel.com> | ||
| 7 | # | ||
| 8 | # This program is free software; you can redistribute it and/or modify | ||
| 9 | # it under the terms of the GNU General Public License version 2 as | ||
| 10 | # published by the Free Software Foundation. | ||
| 11 | # | ||
| 12 | # This program is distributed in the hope that it will be useful, | ||
| 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | # GNU General Public License for more details. | ||
| 16 | # | ||
| 17 | # You should have received a copy of the GNU General Public License along | ||
| 18 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
| 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | |||
| 21 | import gobject | ||
| 22 | from bb.ui.crumbs.progress import ProgressBar | ||
| 23 | |||
| 24 | progress_total = 0 | ||
| 25 | |||
| 26 | class HobHandler(gobject.GObject): | ||
| 27 | |||
| 28 | """ | ||
| 29 | This object does BitBake event handling for the hob gui. | ||
| 30 | """ | ||
| 31 | __gsignals__ = { | ||
| 32 | "machines-updated" : (gobject.SIGNAL_RUN_LAST, | ||
| 33 | gobject.TYPE_NONE, | ||
| 34 | (gobject.TYPE_PYOBJECT,)), | ||
| 35 | "distros-updated" : (gobject.SIGNAL_RUN_LAST, | ||
| 36 | gobject.TYPE_NONE, | ||
| 37 | (gobject.TYPE_PYOBJECT,)), | ||
| 38 | "generating-data" : (gobject.SIGNAL_RUN_LAST, | ||
| 39 | gobject.TYPE_NONE, | ||
| 40 | ()), | ||
| 41 | "data-generated" : (gobject.SIGNAL_RUN_LAST, | ||
| 42 | gobject.TYPE_NONE, | ||
| 43 | ()) | ||
| 44 | } | ||
| 45 | |||
| 46 | def __init__(self, taskmodel, server): | ||
| 47 | gobject.GObject.__init__(self) | ||
| 48 | |||
| 49 | self.model = taskmodel | ||
| 50 | self.server = server | ||
| 51 | self.current_command = None | ||
| 52 | self.building = False | ||
| 53 | |||
| 54 | self.command_map = { | ||
| 55 | "findConfigFilesDistro" : ("findConfigFiles", "MACHINE", "findConfigFilesMachine"), | ||
| 56 | "findConfigFilesMachine" : ("generateTargetsTree", "classes/image.bbclass", None), | ||
| 57 | "generateTargetsTree" : (None, None, None), | ||
| 58 | } | ||
| 59 | |||
| 60 | def run_next_command(self): | ||
| 61 | # FIXME: this is ugly and I *will* replace it | ||
| 62 | if self.current_command: | ||
| 63 | next_cmd = self.command_map[self.current_command] | ||
| 64 | command = next_cmd[0] | ||
| 65 | argument = next_cmd[1] | ||
| 66 | self.current_command = next_cmd[2] | ||
| 67 | if command == "generateTargetsTree": | ||
| 68 | self.emit("generating-data") | ||
| 69 | self.server.runCommand([command, argument]) | ||
| 70 | |||
| 71 | def handle_event(self, event, running_build, pbar=None): | ||
| 72 | if not event: | ||
| 73 | return | ||
| 74 | |||
| 75 | # If we're running a build, use the RunningBuild event handler | ||
| 76 | if self.building: | ||
| 77 | running_build.handle_event(event) | ||
| 78 | elif isinstance(event, bb.event.TargetsTreeGenerated): | ||
| 79 | self.emit("data-generated") | ||
| 80 | if event._model: | ||
| 81 | self.model.populate(event._model) | ||
| 82 | |||
| 83 | elif isinstance(event, bb.event.ConfigFilesFound): | ||
| 84 | var = event._variable | ||
| 85 | if var == "distro": | ||
| 86 | distros = event._values | ||
| 87 | distros.sort() | ||
| 88 | self.emit("distros-updated", distros) | ||
| 89 | elif var == "machine": | ||
| 90 | machines = event._values | ||
| 91 | machines.sort() | ||
| 92 | self.emit("machines-updated", machines) | ||
| 93 | |||
| 94 | elif isinstance(event, bb.command.CommandCompleted): | ||
| 95 | self.run_next_command() | ||
| 96 | elif isinstance(event, bb.event.CacheLoadStarted) and pbar: | ||
| 97 | pbar.set_title("Loading cache") | ||
| 98 | bb.ui.crumbs.hobeventhandler.progress_total = event.total | ||
| 99 | pbar.update(0, bb.ui.crumbs.hobeventhandler.progress_total) | ||
| 100 | elif isinstance(event, bb.event.CacheLoadProgress) and pbar: | ||
| 101 | pbar.update(event.current, bb.ui.crumbs.hobeventhandler.progress_total) | ||
| 102 | elif isinstance(event, bb.event.CacheLoadCompleted) and pbar: | ||
| 103 | pbar.update(bb.ui.crumbs.hobeventhandler.progress_total, bb.ui.crumbs.hobeventhandler.progress_total) | ||
| 104 | elif isinstance(event, bb.event.ParseStarted) and pbar: | ||
| 105 | pbar.set_title("Processing recipes") | ||
| 106 | bb.ui.crumbs.hobeventhandler.progress_total = event.total | ||
| 107 | pbar.update(0, bb.ui.crumbs.hobeventhandler.progress_total) | ||
| 108 | elif isinstance(event, bb.event.ParseProgress) and pbar: | ||
| 109 | pbar.update(event.current, bb.ui.crumbs.hobeventhandler.progress_total) | ||
| 110 | elif isinstance(event, bb.event.ParseCompleted) and pbar: | ||
| 111 | pbar.hide() | ||
| 112 | |||
| 113 | return | ||
| 114 | |||
| 115 | def event_handle_idle_func (self, eventHandler, running_build, pbar): | ||
| 116 | # Consume as many messages as we can in the time available to us | ||
| 117 | event = eventHandler.getEvent() | ||
| 118 | while event: | ||
| 119 | self.handle_event(event, running_build, pbar) | ||
| 120 | event = eventHandler.getEvent() | ||
| 121 | return True | ||
| 122 | |||
| 123 | def set_machine(self, machine): | ||
| 124 | self.server.runCommand(["setVariable", "MACHINE", machine]) | ||
| 125 | self.current_command = "findConfigFilesMachine" | ||
| 126 | self.emit("generating-data") | ||
| 127 | self.run_next_command() | ||
| 128 | |||
| 129 | def set_distro(self, distro): | ||
| 130 | self.server.runCommand(["setVariable", "DISTRO", distro]) | ||
| 131 | |||
| 132 | def run_build(self, targets): | ||
| 133 | self.building = True | ||
| 134 | self.server.runCommand(["buildTargets", targets, "build"]) | ||
| 135 | |||
| 136 | def cancel_build(self): | ||
| 137 | # Note: this may not be the right way to stop an in-progress build | ||
| 138 | 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 @@ | |||
| 1 | # | ||
| 2 | # BitBake Graphical GTK User Interface | ||
| 3 | # | ||
| 4 | # Copyright (C) 2011 Intel Corporation | ||
| 5 | # | ||
| 6 | # Authored by Joshua Lock <josh@linux.intel.com> | ||
| 7 | # | ||
| 8 | # This program is free software; you can redistribute it and/or modify | ||
| 9 | # it under the terms of the GNU General Public License version 2 as | ||
| 10 | # published by the Free Software Foundation. | ||
| 11 | # | ||
| 12 | # This program is distributed in the hope that it will be useful, | ||
| 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | # GNU General Public License for more details. | ||
| 16 | # | ||
| 17 | # You should have received a copy of the GNU General Public License along | ||
| 18 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
| 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | |||
| 21 | import gobject | ||
| 22 | import gtk | ||
| 23 | from bb.ui.crumbs.progress import ProgressBar | ||
| 24 | from bb.ui.crumbs.tasklistmodel import TaskListModel | ||
| 25 | from bb.ui.crumbs.hobeventhandler import HobHandler | ||
| 26 | from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild | ||
| 27 | import xmlrpclib | ||
| 28 | import logging | ||
| 29 | import Queue | ||
| 30 | |||
| 31 | class MainWindow (gtk.Window): | ||
| 32 | |||
| 33 | def __init__(self, taskmodel, handler, curr_mach=None, curr_distro=None): | ||
| 34 | gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) | ||
| 35 | self.model = taskmodel | ||
| 36 | self.model.connect("tasklist-populated", self.update_model) | ||
| 37 | self.curr_mach = curr_mach | ||
| 38 | self.curr_distro = curr_distro | ||
| 39 | self.handler = handler | ||
| 40 | self.set_border_width(10) | ||
| 41 | self.connect("delete-event", gtk.main_quit) | ||
| 42 | self.set_title("BitBake Image Creator") | ||
| 43 | self.set_default_size(700, 600) | ||
| 44 | |||
| 45 | self.build = RunningBuild() | ||
| 46 | self.build.connect("build-succeeded", self.running_build_succeeded_cb) | ||
| 47 | self.build.connect("build-failed", self.running_build_failed_cb) | ||
| 48 | |||
| 49 | createview = self.create_build_gui() | ||
| 50 | buildview = self.view_build_gui() | ||
| 51 | self.nb = gtk.Notebook() | ||
| 52 | self.nb.append_page(createview) | ||
| 53 | self.nb.append_page(buildview) | ||
| 54 | self.nb.set_current_page(0) | ||
| 55 | self.nb.set_show_tabs(False) | ||
| 56 | self.add(self.nb) | ||
| 57 | self.generating = False | ||
| 58 | |||
| 59 | def scroll_tv_cb(self, model, path, it, view): | ||
| 60 | view.scroll_to_cell(path) | ||
| 61 | |||
| 62 | def running_build_failed_cb(self, running_build): | ||
| 63 | # FIXME: handle this | ||
| 64 | return | ||
| 65 | |||
| 66 | def running_build_succeeded_cb(self, running_build): | ||
| 67 | label = gtk.Label("Build completed, start another build?") | ||
| 68 | dialog = gtk.Dialog("Build complete", | ||
| 69 | self, | ||
| 70 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, | ||
| 71 | (gtk.STOCK_NO, gtk.RESPONSE_NO, | ||
| 72 | gtk.STOCK_YES, gtk.RESPONSE_YES)) | ||
| 73 | dialog.vbox.pack_start(label) | ||
| 74 | label.show() | ||
| 75 | response = dialog.run() | ||
| 76 | dialog.destroy() | ||
| 77 | if not response == gtk.RESPONSE_YES: | ||
| 78 | self.model.reset() # NOTE: really? | ||
| 79 | self.nb.set_current_page(0) | ||
| 80 | return | ||
| 81 | |||
| 82 | def machine_combo_changed_cb(self, combo, handler): | ||
| 83 | mach = combo.get_active_text() | ||
| 84 | if mach != self.curr_mach: | ||
| 85 | self.curr_mach = mach | ||
| 86 | handler.set_machine(mach) | ||
| 87 | |||
| 88 | def update_machines(self, handler, machines): | ||
| 89 | active = 0 | ||
| 90 | for machine in machines: | ||
| 91 | self.machine_combo.append_text(machine) | ||
| 92 | if machine == self.curr_mach: | ||
| 93 | self.machine_combo.set_active(active) | ||
| 94 | active = active + 1 | ||
| 95 | self.machine_combo.connect("changed", self.machine_combo_changed_cb, handler) | ||
| 96 | |||
| 97 | def update_distros(self, handler, distros): | ||
| 98 | # FIXME: when we add UI for changing distro this will be used | ||
| 99 | return | ||
| 100 | |||
| 101 | def data_generated(self, handler): | ||
| 102 | self.generating = False | ||
| 103 | |||
| 104 | def spin_idle_func(self, pbar): | ||
| 105 | if self.generating: | ||
| 106 | pbar.pulse() | ||
| 107 | return True | ||
| 108 | else: | ||
| 109 | pbar.hide() | ||
| 110 | return False | ||
| 111 | |||
| 112 | def busy(self, handler): | ||
| 113 | self.generating = True | ||
| 114 | pbar = ProgressBar(self) | ||
| 115 | pbar.connect("delete-event", gtk.main_quit) # NOTE: questionable... | ||
| 116 | pbar.pulse() | ||
| 117 | gobject.timeout_add (200, | ||
| 118 | self.spin_idle_func, | ||
| 119 | pbar) | ||
| 120 | |||
| 121 | def update_model(self, model): | ||
| 122 | pkgsaz_model = gtk.TreeModelSort(self.model.packages_model()) | ||
| 123 | pkgsaz_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING) | ||
| 124 | self.pkgsaz_tree.set_model(pkgsaz_model) | ||
| 125 | |||
| 126 | # FIXME: need to implement a custom sort function, as otherwise the column | ||
| 127 | # is re-ordered when toggling the inclusion state (COL_INC) | ||
| 128 | pkgsgrp_model = gtk.TreeModelSort(self.model.packages_model()) | ||
| 129 | pkgsgrp_model.set_sort_column_id(self.model.COL_GROUP, gtk.SORT_ASCENDING) | ||
| 130 | self.pkgsgrp_tree.set_model(pkgsgrp_model) | ||
| 131 | |||
| 132 | self.contents_tree.set_model(self.model.contents_model()) | ||
| 133 | self.images_tree.set_model(self.model.images_model()) | ||
| 134 | self.tasks_tree.set_model(self.model.tasks_model()) | ||
| 135 | |||
| 136 | def reset_clicked_cb(self, button): | ||
| 137 | label = gtk.Label("Are you sure you want to reset the image contents?") | ||
| 138 | dialog = gtk.Dialog("Confirm reset", self, | ||
| 139 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, | ||
| 140 | (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, | ||
| 141 | gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) | ||
| 142 | dialog.vbox.pack_start(label) | ||
| 143 | label.show() | ||
| 144 | response = dialog.run() | ||
| 145 | dialog.destroy() | ||
| 146 | if (response == gtk.RESPONSE_ACCEPT): | ||
| 147 | self.model.reset() | ||
| 148 | return | ||
| 149 | |||
| 150 | def bake_clicked_cb(self, button): | ||
| 151 | if not self.model.targets_contains_image(): | ||
| 152 | label = gtk.Label("No image was selected. Just build the selected packages?") | ||
| 153 | dialog = gtk.Dialog("Warning, no image selected", | ||
| 154 | self, | ||
| 155 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, | ||
| 156 | (gtk.STOCK_NO, gtk.RESPONSE_NO, | ||
| 157 | gtk.STOCK_YES, gtk.RESPONSE_YES)) | ||
| 158 | dialog.vbox.pack_start(label) | ||
| 159 | label.show() | ||
| 160 | response = dialog.run() | ||
| 161 | dialog.destroy() | ||
| 162 | if not response == gtk.RESPONSE_YES: | ||
| 163 | return | ||
| 164 | |||
| 165 | # Note: We could "squash" the targets list to only include things not brought in by an image | ||
| 166 | task_list = self.model.get_targets() | ||
| 167 | if len(task_list): | ||
| 168 | tasks = " ".join(task_list) | ||
| 169 | # TODO: show a confirmation dialog | ||
| 170 | print("Including these extra tasks in IMAGE_INSTALL: %s" % tasks) | ||
| 171 | else: | ||
| 172 | return | ||
| 173 | |||
| 174 | self.nb.set_current_page(1) | ||
| 175 | self.handler.run_build(task_list) | ||
| 176 | |||
| 177 | return | ||
| 178 | |||
| 179 | def advanced_expander_cb(self, expander, param): | ||
| 180 | return | ||
| 181 | |||
| 182 | def images(self): | ||
| 183 | self.images_tree = gtk.TreeView() | ||
| 184 | self.images_tree.set_headers_visible(True) | ||
| 185 | self.images_tree.set_headers_clickable(False) | ||
| 186 | self.images_tree.set_enable_search(True) | ||
| 187 | self.images_tree.set_search_column(0) | ||
| 188 | self.images_tree.get_selection().set_mode(gtk.SELECTION_NONE) | ||
| 189 | |||
| 190 | col = gtk.TreeViewColumn('Package') | ||
| 191 | col1 = gtk.TreeViewColumn('Description') | ||
| 192 | col2 = gtk.TreeViewColumn('License') | ||
| 193 | col3 = gtk.TreeViewColumn('Include') | ||
| 194 | col3.set_resizable(False) | ||
| 195 | |||
| 196 | self.images_tree.append_column(col) | ||
| 197 | self.images_tree.append_column(col1) | ||
| 198 | self.images_tree.append_column(col2) | ||
| 199 | self.images_tree.append_column(col3) | ||
| 200 | |||
| 201 | cell = gtk.CellRendererText() | ||
| 202 | cell1 = gtk.CellRendererText() | ||
| 203 | cell2 = gtk.CellRendererText() | ||
| 204 | cell3 = gtk.CellRendererToggle() | ||
| 205 | cell3.set_property('activatable', True) | ||
| 206 | cell3.connect("toggled", self.toggle_include_cb, self.images_tree) | ||
| 207 | |||
| 208 | col.pack_start(cell, True) | ||
| 209 | col1.pack_start(cell1, True) | ||
| 210 | col2.pack_start(cell2, True) | ||
| 211 | col3.pack_start(cell3, True) | ||
| 212 | |||
| 213 | col.set_attributes(cell, text=self.model.COL_NAME) | ||
| 214 | col1.set_attributes(cell1, text=self.model.COL_DESC) | ||
| 215 | col2.set_attributes(cell2, text=self.model.COL_LIC) | ||
| 216 | col3.set_attributes(cell3, active=self.model.COL_INC) | ||
| 217 | |||
| 218 | self.images_tree.show() | ||
| 219 | |||
| 220 | scroll = gtk.ScrolledWindow() | ||
| 221 | scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) | ||
| 222 | scroll.set_shadow_type(gtk.SHADOW_IN) | ||
| 223 | scroll.add(self.images_tree) | ||
| 224 | |||
| 225 | return scroll | ||
| 226 | |||
| 227 | def toggle_package(self, path, model): | ||
| 228 | # Convert path to path in original model | ||
| 229 | opath = model.convert_path_to_child_path(path) | ||
| 230 | # current include status | ||
| 231 | inc = self.model[opath][self.model.COL_INC] | ||
| 232 | if inc: | ||
| 233 | self.model.mark(opath) | ||
| 234 | self.model.sweep_up() | ||
| 235 | #self.model.remove_package_full(cpath) | ||
| 236 | else: | ||
| 237 | self.model.include_item(opath) | ||
| 238 | return | ||
| 239 | |||
| 240 | def remove_package_cb(self, cell, path): | ||
| 241 | model = self.model.contents_model() | ||
| 242 | label = gtk.Label("Are you sure you want to remove this item?") | ||
| 243 | dialog = gtk.Dialog("Confirm removal", self, | ||
| 244 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, | ||
| 245 | (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, | ||
| 246 | gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) | ||
| 247 | dialog.vbox.pack_start(label) | ||
| 248 | label.show() | ||
| 249 | response = dialog.run() | ||
| 250 | dialog.destroy() | ||
| 251 | if (response == gtk.RESPONSE_ACCEPT): | ||
| 252 | self.toggle_package(path, model) | ||
| 253 | |||
| 254 | def toggle_include_cb(self, cell, path, tv): | ||
| 255 | model = tv.get_model() | ||
| 256 | self.toggle_package(path, model) | ||
| 257 | |||
| 258 | def toggle_pkg_include_cb(self, cell, path, tv): | ||
| 259 | # there's an extra layer of models in the packages case. | ||
| 260 | sort_model = tv.get_model() | ||
| 261 | cpath = sort_model.convert_path_to_child_path(path) | ||
| 262 | self.toggle_package(cpath, sort_model.get_model()) | ||
| 263 | |||
| 264 | def pkgsaz(self): | ||
| 265 | self.pkgsaz_tree = gtk.TreeView() | ||
| 266 | self.pkgsaz_tree.set_headers_visible(True) | ||
| 267 | self.pkgsaz_tree.set_headers_clickable(True) | ||
| 268 | self.pkgsaz_tree.set_enable_search(True) | ||
| 269 | self.pkgsaz_tree.set_search_column(0) | ||
| 270 | self.pkgsaz_tree.get_selection().set_mode(gtk.SELECTION_NONE) | ||
| 271 | |||
| 272 | col = gtk.TreeViewColumn('Package') | ||
| 273 | col1 = gtk.TreeViewColumn('Description') | ||
| 274 | col1.set_resizable(True) | ||
| 275 | col2 = gtk.TreeViewColumn('License') | ||
| 276 | col2.set_resizable(True) | ||
| 277 | col3 = gtk.TreeViewColumn('Group') | ||
| 278 | col4 = gtk.TreeViewColumn('Include') | ||
| 279 | col4.set_resizable(False) | ||
| 280 | |||
| 281 | self.pkgsaz_tree.append_column(col) | ||
| 282 | self.pkgsaz_tree.append_column(col1) | ||
| 283 | self.pkgsaz_tree.append_column(col2) | ||
| 284 | self.pkgsaz_tree.append_column(col3) | ||
| 285 | self.pkgsaz_tree.append_column(col4) | ||
| 286 | |||
| 287 | cell = gtk.CellRendererText() | ||
| 288 | cell1 = gtk.CellRendererText() | ||
| 289 | cell1.set_property('width-chars', 20) | ||
| 290 | cell2 = gtk.CellRendererText() | ||
| 291 | cell2.set_property('width-chars', 20) | ||
| 292 | cell3 = gtk.CellRendererText() | ||
| 293 | cell4 = gtk.CellRendererToggle() | ||
| 294 | cell4.set_property('activatable', True) | ||
| 295 | cell4.connect("toggled", self.toggle_pkg_include_cb, self.pkgsaz_tree) | ||
| 296 | |||
| 297 | col.pack_start(cell, True) | ||
| 298 | col1.pack_start(cell1, True) | ||
| 299 | col2.pack_start(cell2, True) | ||
| 300 | col3.pack_start(cell3, True) | ||
| 301 | col4.pack_start(cell4, True) | ||
| 302 | |||
| 303 | col.set_attributes(cell, text=self.model.COL_NAME) | ||
| 304 | col1.set_attributes(cell1, text=self.model.COL_DESC) | ||
| 305 | col2.set_attributes(cell2, text=self.model.COL_LIC) | ||
| 306 | col3.set_attributes(cell3, text=self.model.COL_GROUP) | ||
| 307 | col4.set_attributes(cell4, active=self.model.COL_INC) | ||
| 308 | |||
| 309 | self.pkgsaz_tree.show() | ||
| 310 | |||
| 311 | scroll = gtk.ScrolledWindow() | ||
| 312 | scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) | ||
| 313 | scroll.set_shadow_type(gtk.SHADOW_IN) | ||
| 314 | scroll.add(self.pkgsaz_tree) | ||
| 315 | |||
| 316 | return scroll | ||
| 317 | |||
| 318 | def pkgsgrp(self): | ||
| 319 | self.pkgsgrp_tree = gtk.TreeView() | ||
| 320 | self.pkgsgrp_tree.set_headers_visible(True) | ||
| 321 | self.pkgsgrp_tree.set_headers_clickable(False) | ||
| 322 | self.pkgsgrp_tree.set_enable_search(True) | ||
| 323 | self.pkgsgrp_tree.set_search_column(0) | ||
| 324 | self.pkgsgrp_tree.get_selection().set_mode(gtk.SELECTION_NONE) | ||
| 325 | |||
| 326 | col = gtk.TreeViewColumn('Package') | ||
| 327 | col1 = gtk.TreeViewColumn('Description') | ||
| 328 | col1.set_resizable(True) | ||
| 329 | col2 = gtk.TreeViewColumn('License') | ||
| 330 | col2.set_resizable(True) | ||
| 331 | col3 = gtk.TreeViewColumn('Group') | ||
| 332 | col4 = gtk.TreeViewColumn('Include') | ||
| 333 | col4.set_resizable(False) | ||
| 334 | |||
| 335 | self.pkgsgrp_tree.append_column(col) | ||
| 336 | self.pkgsgrp_tree.append_column(col1) | ||
| 337 | self.pkgsgrp_tree.append_column(col2) | ||
| 338 | self.pkgsgrp_tree.append_column(col3) | ||
| 339 | self.pkgsgrp_tree.append_column(col4) | ||
| 340 | |||
| 341 | cell = gtk.CellRendererText() | ||
| 342 | cell1 = gtk.CellRendererText() | ||
| 343 | cell1.set_property('width-chars', 20) | ||
| 344 | cell2 = gtk.CellRendererText() | ||
| 345 | cell2.set_property('width-chars', 20) | ||
| 346 | cell3 = gtk.CellRendererText() | ||
| 347 | cell4 = gtk.CellRendererToggle() | ||
| 348 | cell4.set_property("activatable", True) | ||
| 349 | cell4.connect("toggled", self.toggle_pkg_include_cb, self.pkgsgrp_tree) | ||
| 350 | |||
| 351 | col.pack_start(cell, True) | ||
| 352 | col1.pack_start(cell1, True) | ||
| 353 | col2.pack_start(cell2, True) | ||
| 354 | col3.pack_start(cell3, True) | ||
| 355 | col4.pack_start(cell4, True) | ||
| 356 | |||
| 357 | col.set_attributes(cell, text=self.model.COL_NAME) | ||
| 358 | col1.set_attributes(cell1, text=self.model.COL_DESC) | ||
| 359 | col2.set_attributes(cell2, text=self.model.COL_LIC) | ||
| 360 | col3.set_attributes(cell3, text=self.model.COL_GROUP) | ||
| 361 | col4.set_attributes(cell4, active=self.model.COL_INC) | ||
| 362 | |||
| 363 | self.pkgsgrp_tree.show() | ||
| 364 | |||
| 365 | scroll = gtk.ScrolledWindow() | ||
| 366 | scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) | ||
| 367 | scroll.set_shadow_type(gtk.SHADOW_IN) | ||
| 368 | scroll.add(self.pkgsgrp_tree) | ||
| 369 | |||
| 370 | return scroll | ||
| 371 | |||
| 372 | def tasks(self): | ||
| 373 | self.tasks_tree = gtk.TreeView() | ||
| 374 | self.tasks_tree.set_headers_visible(True) | ||
| 375 | self.tasks_tree.set_headers_clickable(False) | ||
| 376 | self.tasks_tree.set_enable_search(True) | ||
| 377 | self.tasks_tree.set_search_column(0) | ||
| 378 | self.tasks_tree.get_selection().set_mode(gtk.SELECTION_NONE) | ||
| 379 | |||
| 380 | col = gtk.TreeViewColumn('Package') | ||
| 381 | col1 = gtk.TreeViewColumn('Description') | ||
| 382 | col2 = gtk.TreeViewColumn('Include') | ||
| 383 | col2.set_resizable(False) | ||
| 384 | |||
| 385 | self.tasks_tree.append_column(col) | ||
| 386 | self.tasks_tree.append_column(col1) | ||
| 387 | self.tasks_tree.append_column(col2) | ||
| 388 | |||
| 389 | cell = gtk.CellRendererText() | ||
| 390 | cell1 = gtk.CellRendererText() | ||
| 391 | cell2 = gtk.CellRendererToggle() | ||
| 392 | cell2.set_property('activatable', True) | ||
| 393 | cell2.connect("toggled", self.toggle_include_cb, self.tasks_tree) | ||
| 394 | |||
| 395 | col.pack_start(cell, True) | ||
| 396 | col1.pack_start(cell1, True) | ||
| 397 | col2.pack_start(cell2, True) | ||
| 398 | |||
| 399 | col.set_attributes(cell, text=self.model.COL_NAME) | ||
| 400 | col1.set_attributes(cell1, text=self.model.COL_DESC) | ||
| 401 | col2.set_attributes(cell2, active=self.model.COL_INC) | ||
| 402 | |||
| 403 | self.tasks_tree.show() | ||
| 404 | |||
| 405 | scroll = gtk.ScrolledWindow() | ||
| 406 | scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) | ||
| 407 | scroll.set_shadow_type(gtk.SHADOW_IN) | ||
| 408 | scroll.add(self.tasks_tree) | ||
| 409 | |||
| 410 | return scroll | ||
| 411 | |||
| 412 | def cancel_build(self, button): | ||
| 413 | label = gtk.Label("Do you really want to stop this build?") | ||
| 414 | dialog = gtk.Dialog("Cancel build", | ||
| 415 | self, | ||
| 416 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, | ||
| 417 | (gtk.STOCK_NO, gtk.RESPONSE_NO, | ||
| 418 | gtk.STOCK_YES, gtk.RESPONSE_YES)) | ||
| 419 | dialog.vbox.pack_start(label) | ||
| 420 | label.show() | ||
| 421 | response = dialog.run() | ||
| 422 | dialog.destroy() | ||
| 423 | if not response == gtk.RESPONSE_YES: | ||
| 424 | self.handler.cancel_build() | ||
| 425 | return | ||
| 426 | |||
| 427 | def view_build_gui(self): | ||
| 428 | vbox = gtk.VBox(False, 6) | ||
| 429 | vbox.show() | ||
| 430 | build_tv = RunningBuildTreeView() | ||
| 431 | build_tv.show() | ||
| 432 | build_tv.set_model(self.build.model) | ||
| 433 | self.build.model.connect("row-inserted", self.scroll_tv_cb, build_tv) | ||
| 434 | scrolled_view = gtk.ScrolledWindow () | ||
| 435 | scrolled_view.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) | ||
| 436 | scrolled_view.add(build_tv) | ||
| 437 | scrolled_view.show() | ||
| 438 | vbox.pack_start(scrolled_view, expand=True, fill=True) | ||
| 439 | hbox = gtk.HBox(False, 6) | ||
| 440 | hbox.show() | ||
| 441 | vbox.pack_start(hbox, expand=False, fill=False) | ||
| 442 | cancel = gtk.Button(stock=gtk.STOCK_CANCEL) | ||
| 443 | cancel.connect("clicked", self.cancel_build) | ||
| 444 | cancel.show() | ||
| 445 | hbox.pack_end(cancel, expand=False, fill=False) | ||
| 446 | |||
| 447 | return vbox | ||
| 448 | |||
| 449 | def create_build_gui(self): | ||
| 450 | vbox = gtk.VBox(False, 6) | ||
| 451 | vbox.show() | ||
| 452 | hbox = gtk.HBox(False, 6) | ||
| 453 | hbox.show() | ||
| 454 | vbox.pack_start(hbox, expand=False, fill=False) | ||
| 455 | |||
| 456 | label = gtk.Label("Machine:") | ||
| 457 | label.show() | ||
| 458 | hbox.pack_start(label, expand=False, fill=False, padding=6) | ||
| 459 | self.machine_combo = gtk.combo_box_new_text() | ||
| 460 | self.machine_combo.set_active(0) | ||
| 461 | self.machine_combo.show() | ||
| 462 | self.machine_combo.set_tooltip_text("Selects the architecture of the target board for which you would like to build an image.") | ||
| 463 | hbox.pack_start(self.machine_combo, expand=False, fill=False, padding=6) | ||
| 464 | |||
| 465 | ins = gtk.Notebook() | ||
| 466 | vbox.pack_start(ins, expand=True, fill=True) | ||
| 467 | ins.set_show_tabs(True) | ||
| 468 | label = gtk.Label("Images") | ||
| 469 | label.show() | ||
| 470 | ins.append_page(self.images(), tab_label=label) | ||
| 471 | label = gtk.Label("Tasks") | ||
| 472 | label.show() | ||
| 473 | ins.append_page(self.tasks(), tab_label=label) | ||
| 474 | label = gtk.Label("Packages (by Group)") | ||
| 475 | label.show() | ||
| 476 | ins.append_page(self.pkgsgrp(), tab_label=label) | ||
| 477 | label = gtk.Label("Packages (by Name)") | ||
| 478 | label.show() | ||
| 479 | ins.append_page(self.pkgsaz(), tab_label=label) | ||
| 480 | ins.set_current_page(0) | ||
| 481 | ins.show_all() | ||
| 482 | |||
| 483 | hbox = gtk.HBox() | ||
| 484 | hbox.show() | ||
| 485 | vbox.pack_start(hbox, expand=False, fill=False) | ||
| 486 | label = gtk.Label("Image contents:") | ||
| 487 | label.show() | ||
| 488 | hbox.pack_start(label, expand=False, fill=False, padding=6) | ||
| 489 | con = self.contents() | ||
| 490 | con.show() | ||
| 491 | vbox.pack_start(con, expand=True, fill=True) | ||
| 492 | |||
| 493 | #advanced = gtk.Expander(label="Advanced") | ||
| 494 | #advanced.connect("notify::expanded", self.advanced_expander_cb) | ||
| 495 | #advanced.show() | ||
| 496 | #vbox.pack_start(advanced, expand=False, fill=False) | ||
| 497 | |||
| 498 | hbox = gtk.HBox() | ||
| 499 | hbox.show() | ||
| 500 | vbox.pack_start(hbox, expand=False, fill=False) | ||
| 501 | bake = gtk.Button("Bake") | ||
| 502 | bake.connect("clicked", self.bake_clicked_cb) | ||
| 503 | bake.show() | ||
| 504 | hbox.pack_end(bake, expand=False, fill=False, padding=6) | ||
| 505 | reset = gtk.Button("Reset") | ||
| 506 | reset.connect("clicked", self.reset_clicked_cb) | ||
| 507 | reset.show() | ||
| 508 | hbox.pack_end(reset, expand=False, fill=False, padding=6) | ||
| 509 | |||
| 510 | return vbox | ||
| 511 | |||
| 512 | def contents(self): | ||
| 513 | self.contents_tree = gtk.TreeView() | ||
| 514 | self.contents_tree.set_headers_visible(True) | ||
| 515 | self.contents_tree.get_selection().set_mode(gtk.SELECTION_NONE) | ||
| 516 | |||
| 517 | # allow searching in the package column | ||
| 518 | self.contents_tree.set_search_column(0) | ||
| 519 | |||
| 520 | col = gtk.TreeViewColumn('Package') | ||
| 521 | col.set_sort_column_id(0) | ||
| 522 | col1 = gtk.TreeViewColumn('Brought in by') | ||
| 523 | col1.set_resizable(True) | ||
| 524 | col2 = gtk.TreeViewColumn('Remove') | ||
| 525 | col2.set_expand(False) | ||
| 526 | |||
| 527 | self.contents_tree.append_column(col) | ||
| 528 | self.contents_tree.append_column(col1) | ||
| 529 | self.contents_tree.append_column(col2) | ||
| 530 | |||
| 531 | cell = gtk.CellRendererText() | ||
| 532 | cell1 = gtk.CellRendererText() | ||
| 533 | cell1.set_property('width-chars', 20) | ||
| 534 | cell2 = gtk.CellRendererToggle() | ||
| 535 | cell2.connect("toggled", self.remove_package_cb) | ||
| 536 | |||
| 537 | col.pack_start(cell, True) | ||
| 538 | col1.pack_start(cell1, True) | ||
| 539 | col2.pack_start(cell2, True) | ||
| 540 | |||
| 541 | col.set_attributes(cell, text=self.model.COL_NAME) | ||
| 542 | col1.set_attributes(cell1, text=self.model.COL_BINB) | ||
| 543 | col2.set_attributes(cell2, active=self.model.COL_INC) | ||
| 544 | |||
| 545 | self.contents_tree.show() | ||
| 546 | |||
| 547 | scroll = gtk.ScrolledWindow() | ||
| 548 | scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) | ||
| 549 | scroll.set_shadow_type(gtk.SHADOW_IN) | ||
| 550 | scroll.add(self.contents_tree) | ||
| 551 | |||
| 552 | return scroll | ||
| 553 | |||
| 554 | def main (server, eventHandler): | ||
| 555 | gobject.threads_init() | ||
| 556 | gtk.gdk.threads_init() | ||
| 557 | |||
| 558 | taskmodel = TaskListModel() | ||
| 559 | handler = HobHandler(taskmodel, server) | ||
| 560 | mach = server.runCommand(["getVariable", "MACHINE"]) | ||
| 561 | distro = server.runCommand(["getVariable", "DISTRO"]) | ||
| 562 | |||
| 563 | window = MainWindow(taskmodel, handler, mach, distro) | ||
| 564 | window.show_all () | ||
| 565 | handler.connect("machines-updated", window.update_machines) | ||
| 566 | handler.connect("distros-updated", window.update_distros) | ||
| 567 | handler.connect("generating-data", window.busy) | ||
| 568 | handler.connect("data-generated", window.data_generated) | ||
| 569 | pbar = ProgressBar(window) | ||
| 570 | pbar.connect("delete-event", gtk.main_quit) | ||
| 571 | |||
| 572 | try: | ||
| 573 | # kick the while thing off | ||
| 574 | handler.current_command = "findConfigFilesDistro" | ||
| 575 | server.runCommand(["findConfigFiles", "DISTRO"]) | ||
| 576 | except xmlrpclib.Fault: | ||
| 577 | print("XMLRPC Fault getting commandline:\n %s" % x) | ||
| 578 | return 1 | ||
| 579 | |||
| 580 | # This timeout function regularly probes the event queue to find out if we | ||
| 581 | # have any messages waiting for us. | ||
| 582 | gobject.timeout_add (100, | ||
| 583 | handler.event_handle_idle_func, | ||
| 584 | eventHandler, | ||
| 585 | window.build, | ||
| 586 | pbar) | ||
| 587 | |||
| 588 | try: | ||
| 589 | gtk.main() | ||
| 590 | except EnvironmentError as ioerror: | ||
| 591 | # ignore interrupted io | ||
| 592 | if ioerror.args[0] == 4: | ||
| 593 | pass | ||
| 594 | finally: | ||
| 595 | server.runCommand(["stateStop"]) | ||
| 596 | |||
