diff options
author | Joshua Lock <josh@linux.intel.com> | 2011-02-02 13:02:43 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2011-02-24 15:54:53 +0000 |
commit | 6dbceb0be9a1b8d7d5124b4fbd74f18609bc6146 (patch) | |
tree | a252faaaf53b1b3858bd03cadec8ccb0c3cae7b3 /bitbake/lib/bb/ui | |
parent | c2814caa5d80d3a1097c83b82b7a3f8eeb8da548 (diff) | |
download | poky-6dbceb0be9a1b8d7d5124b4fbd74f18609bc6146.tar.gz |
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 <josh@linux.intel.com>
Diffstat (limited to 'bitbake/lib/bb/ui')
-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 | |||